KingbaseES函数三态
理解函数的三态1
VOLATILE:
volatile函数没有限制,可以修改数据(如执行delete,insert,update), 使用同样的参数调用可能返回不同的值.
STABLE:
不允许修改数据, PG8.0以及以上版本不允许在volatile函数中使用非SELECT|PERFORM语句.
使用同样的参数调用返回同样的结果,在事务中有这个特性的也归属stable.
IMMUTABLE:
不允许修改数据, 使用同样的参数调用返回同样的结果.
备注:Pg 8.0以及以上版本不允许在stable或immutable函数中执行非select|perform语句.
示例:
test=# create table tbl(id int primary key, info text, crt_time timestamp);
CREATE TABLE
test=# create or replace function f_tbl(i_id int) returns void as $$
test$# declare
test$# begin
test$# update tbl set crt_time=now() where id=i_id;
test$# end;
test$# $$ language plpgsql stable;
CREATE FUNCTION
test=# \set VERBOSITY verbose
test=# select f_tbl(1);
ERROR: 0A000: UPDATE is not allowed in a non-volatile function
背景: SQL statement "update tbl set crt_time=now() where id=i_id"
PL/pgSQL function f_tbl(integer) line 4 at SQL statement
位置: _SPI_execute_plan_internal, spi.c:2951
漏洞:在stable或immutable函数中调用volatile函数是可以的.
示例:
test=# alter function f_tbl(int) volatile;
ALTER FUNCTION
test=# create or replace function f_tbl1(i_id int) returns void as $$
test$# declare
test$# begin
test$# perform f_tbl(i_id); -- 在stable或immutable函数中调用volatile函数是可以的.
test$# end;
test$# $$ language plpgsql stable;
CREATE FUNCTION
test=#
test=# insert into tbl values(1,'test',now());
INSERT 0 1
test=# select * from tbl;
id | info | crt_time
----+------+----------------------------
1 | test | 2022-11-10 11:46:05.854350
(1 行记录) test=# select f_tbl1(1);
f_tbl1
-------- (1 行记录) test=# select * from tbl;
id | info | crt_time
----+------+----------------------------
1 | test | 2022-11-10 11:46:33.153699
(1 行记录)
同样的参数多次调用.
volatile 函数相同的参数,多次调用返回结果可能不一样.
kbstattuple扩展插件可以参考KingbaseES在线手册
https://help.kingbase.com.cn/v8/development/sql-plsql/ref-extended-plug-in/kbstattuple.html?highlight=kbstattuple test=# create extension kbstattuple ;
CREATE EXTENSION
test=# \dx+ kbstattuple ;
对象用于扩展 "kbstattuple"
对象描述
---------------------------------------
function pg_relpages(regclass)
function pg_relpages(text)
function pgstatginindex(regclass)
function pgstathashindex(regclass)
function pgstatindex(regclass)
function pgstatindex(text)
function pgstattuple_approx(regclass)
function pgstattuple(regclass)
function pgstattuple(text)
(9 行记录) test=# create table t2(id int);
CREATE TABLE
test=# select pg_relpages('t2');
pg_relpages
-------------
0
(1 行记录) test=# insert into t2 values (1);
INSERT 0 1
test=# select pg_relpages('t2'); -- 返回值变化
pg_relpages
-------------
1
(1 行记录) test=# select proname,provolatile from pg_proc where proname='pg_relpages';
proname | provolatile
-------------+-------------
pg_relpages | v
pg_relpages | v
(2 行记录)
stable, immutable函数同样的参数多次调用返回结果不变.
在事务中多次调用返回结果一致的也可归属于stable.
test=# select now();
now
-------------------------------
2022-11-10 13:42:35.689740+08
(1 行记录) test=# select now();
now
-------------------------------
2022-11-10 13:42:36.756038+08
(1 行记录) test=# begin;
BEGIN
test=# select now();
now
-------------------------------
2022-11-10 13:42:39.037893+08
(1 行记录) test=# select now();
now
-------------------------------
2022-11-10 13:42:39.037893+08
(1 行记录) test=# select provolatile,proname,proargtypes from pg_proc where proname='now';
provolatile | proname | proargtypes
-------------+---------+-------------
s | now |
(1 行记录) test=# select now();
now
-------------------------------
2022-11-10 13:42:39.037893+08
(1 行记录) test=# end;
COMMIT
test=# select now();
now
-------------------------------
2022-11-10 13:43:31.772061+08
(1 行记录)
immutable函数同stable, 同样的参数多次调用结果一致.
test=# select proname,provolatile from pg_proc where proname='abs';
proname | provolatile
---------+-------------
abs | i
abs | i
abs | i
abs | i
abs | i
abs | i
abs | i
(7 行记录) test=# select abs(-10);
abs
-----
10
(1 行记录) test=# select abs(-10);
abs
-----
10
(1 行记录) test=# begin;
BEGIN
test=# select abs(-10);
abs
-----
10
(1 行记录) test=# select abs(-10);
abs
-----
10
(1 行记录) test=# end;
COMMIT
test=# select abs(-10);
abs
-----
10
(1 行记录)
VOLATILE/STABLE/IMMUTABLE区别:
VOLATILE
volatile函数不能被优化器作为优化条件.
1)例如单SQL处理多行时不能减少volatile函数的调用次数,
2)不能使用volatile函数创建函数索引,
3)在过滤条件中使用volatile函数时, 不能走索引扫描.
在同一个查询中, 同样参数的情况下可能被多次执行(QUERY有多行返回/扫描的情况下).
STABLE
1)优化器可根据实际场景优化stable函数的调用次数, 同样的参数多次调用可能减少成单次调用.
2)stable函数可以用于优化器选择合适的索引扫描, 因为索引扫描仅评估被比较的值一次, 后多次比较.
3)stable和volatile函数都不能用于创建函数索引, 只有immutable函数可以用于创建函数索引.
IMMUTABLE
优化器在处理immutable函数时, 先评估函数结果, 将结果替换为常量.
理解函数的三态2
test=# create table t3(id int);
CREATE TABLE
test=# insert into t3 values(1),(1),(1),(2),(2),(2);
INSERT 0 6
test=#
test=# create or replace function f_t3(id int) returns int as $$
test$# declare
test$# begin
test$# raise notice 'Called.';
test$# return id;
test$# end;
test$# $$ language plpgsql stable; -- stable 状态
CREATE FUNCTION
test=# select * from t3 where f_t3(id) =1;
-- f_t3(id) --这里id是变量,不能被优化器优化,因此函数调用了6次.
test=# select * from t3 where f_t3(id) =1;
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
id
----
1
1
1
(3 行记录) select * from t3 where f_t3(1) =1;
-- 这里使用常量调用f_t3()所以可以被优化器优化.
test=# select * from t3 where f_t3(1) =1;
NOTICE: 00000: Called. -- 函数只被调用一次
位置: exec_stmt_raise, pl_exec.c:3908
id
----
1
1
1
2
2
2
(6 行记录) 把函数改成volatile后, 函数不能被优化. test=# alter function f_t3(int) volatile;
ALTER FUNCTION
test=# select proname,provolatile from pg_proc where proname='f_t3';
proname | provolatile
---------+-------------
f_t3 | v
(1 行记录) test=# select * from t3 where f_t3(1) =1;
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
NOTICE: 00000: Called.
位置: exec_stmt_raise, pl_exec.c:3908
id
----
1
1
1
2
2
2
(6 行记录)
根据函数的实际情况设置稳定态, 可以达到优化效果.
例如f_t3函数调用一次耗时1秒, 并且是stable的状态,以上例子可以减少5秒的查询时间.使用volatile态则需要6秒.
优化器在处理immutable函数时,先评估函数结果,将结果替换为常量.
test=# explain select * from t2 where id>abs(-1);
QUERY PLAN
-----------------------------------------------------
Seq Scan on t2 (cost=0.00..41.75 rows=847 width=4)
Filter: (id > 1)
(2 行记录)
因为abs(int)是immutable函数,这里abs(-1)替换成常量1.
如果把函数改成stable, 那么将不会替换成常量.
test=# alter function abs(int) stable;
ALTER FUNCTION
test=# select proname,provolatile from pg_proc where proname='abs';
proname | provolatile
---------+-------------
abs | i
abs | i
abs | i
abs | i
abs | i
abs | s
abs | i
(7 行记录)
test=# explain select * from t2 where id>abs(-1);
QUERY PLAN
-----------------------------------------------------
Seq Scan on t2 (cost=0.00..48.10 rows=847 width=4)
Filter: (id > abs('-1'::integer))
(2 行记录)
由于abs(int)被改成stable了, 将不会替换成常量
在prepared statement中使用需要注意区别. 后面会有例子.
只有immutable函数可以创建函数索引.
test=# create table t4(id int,info timestamp(0));
CREATE TABLE
test=# \set VERBOSITY verbose
test=# create index idx_t4_1 on t4(to_char(info,'yyyymmdd'));
ERROR: 42P17: functions in index expression must be marked IMMUTABLE
位置: ComputeIndexAttrs, indexcmds.c:1940
test=# create table t5(id int primary key, info text);
CREATE TABLE
test=# insert into t5 select generate_series(1,100000),md5(random()::text);
INSERT 0 100000
test=# alter function abs(int) volatile;
ALTER FUNCTION
test=#
test=# \d+ t4
数据表 "public.t4"
栏位 | 类型 | 校对规则 | 可空的 | 预设 | 存储 | 统计目标 | 描述
------+--------------------------------+----------+--------+------+-------+----------+------
id | integer | | | | plain | |
info | timestamp(0) without time zone | | | | plain | |
访问方法 heap
test=# \d+ t5
数据表 "public.t5"
栏位 | 类型 | 校对规则 | 可空的 | 预设 | 存储 | 统计目标 | 描述
------+---------+----------+----------+------+----------+----------+------
id | integer | | not null | | plain | |
info | text | | | | extended | |
索引:
"t5_pkey" PRIMARY KEY, btree (id)
访问方法 heap
索引扫描时, 用于过滤条件的表达式只被评估一次后, 再与索引值进行比较判断是否满足条件.
test=# explain select * from t5 where id<abs(10);
QUERY PLAN
----------------------------------------------------------
Seq Scan on t5 (cost=0.00..2341.00 rows=33333 width=37)
Filter: (id < abs(10))
(2 行记录)
只有stable函数和immutable函数符合索引扫描的刚性需求.
test=# alter function abs(int) stable;
ALTER FUNCTION
test=# explain select * from t5 where id<abs(-100);
QUERY PLAN
--------------------------------------------------------------------
Index Scan using t5_pkey on t5 (cost=0.29..9.99 rows=97 width=37)
Index Cond: (id < abs('-100'::integer))
(2 行记录)
test=# alter function abs(int) immutable;
ALTER FUNCTION
test=# explain select * from t5 where id<abs(-100);
QUERY PLAN
--------------------------------------------------------------------
Index Scan using t5_pkey on t5 (cost=0.29..9.99 rows=97 width=37)
Index Cond: (id < 100)
(2 行记录)
volatile函数同样的输入参数可能返回不同值,在一个查询中将被多次调用,不符合索引扫描规则.
stable和immutable同样的参数返回值不变,因此可以作为索引扫描的比较值,优化器允许走索引扫描.
理解函数的三态3
函数内的每条查询语句的数据可见性:
VOLATILE
snapshot为函数内的每个query开始时的snapshot. 因此对外部已提交的数据时可见的.
STABLE
snapshot为外部调用函数的QUERY的snapshot, 函数内部始终保持这个snapshot.
IMMUTABLE
同stable
测试:
test=# create or replace function f_t6() returns void as $$
test$# declare
test$# r record;
test$# begin
test$# for i in 1..10 loop
test$# for r in select * from t6 loop
test$# raise notice 'loop:%,t6:%.',i,r;
test$# end loop;
test$# perform pg_sleep(5);
test$# end loop;
test$# end;
test$# $$ language plpgsql volatile;
CREATE FUNCTION
test=# create table t6(id int,info text);
CREATE TABLE
--执行过程中,新建连接往t6表插入新数据,volatile函数察觉新增行
test=# select f_t6();
NOTICE: loop:2,t6:(1,test).
NOTICE: loop:3,t6:(1,test).
NOTICE: loop:4,t6:(1,test).
NOTICE: loop:5,t6:(1,test).
NOTICE: loop:5,t6:(1,test1).
NOTICE: loop:6,t6:(1,test).
NOTICE: loop:6,t6:(1,test1).
NOTICE: loop:6,t6:(1,test3).
NOTICE: loop:7,t6:(1,test).
NOTICE: loop:7,t6:(1,test1).
NOTICE: loop:7,t6:(1,test3).
NOTICE: loop:7,t6:(1,test5).
NOTICE: loop:8,t6:(1,test).
NOTICE: loop:8,t6:(1,test1).
NOTICE: loop:8,t6:(1,test3).
NOTICE: loop:8,t6:(1,test5).
NOTICE: loop:9,t6:(1,test).
NOTICE: loop:9,t6:(1,test1).
NOTICE: loop:9,t6:(1,test3).
NOTICE: loop:9,t6:(1,test5).
NOTICE: loop:10,t6:(1,test).
NOTICE: loop:10,t6:(1,test1).
NOTICE: loop:10,t6:(1,test3).
NOTICE: loop:10,t6:(1,test5).
NOTICE: loop:10,t6:(1,hello).
f_t6
------
(1 行记录)
test=# insert into t6 values(1,'test');
INSERT 0 1
test=# insert into t6 values(1,'test1');
INSERT 0 1
test=# insert into t6 values(1,'test3');
INSERT 0 1
test=# insert into t6 values(1,'test5');
INSERT 0 1
test=# insert into t6 values(1,'hello');
INSERT 0 1
test=#
--更改稳定态为 stable immutable
test=# alter function f_t6() stable;
ALTER FUNCTION
test=# truncate table t6;
TRUNCATE TABLE
test=# select f_t6();
f_t6
------
(1 行记录)
--f_t6()执行过程中对t6变更, 函数不感知.
test=# insert into t6 values(1,'hello');
INSERT 0 1
test=# insert into t6 values(1,'test5');
INSERT 0 1
test=# insert into t6 values(1,'test3');
INSERT 0 1
test=# insert into t6 values(1,'test3');
INSERT 0 1
test=#
理解函数的三态4
STABLE和IMMUTABLE的区别:
在SELECT子句中,优化器对stable和immutable区别对待.
测试:
test=# create table t7(id int);
CREATE TABLE
test=# insert into t7 values (1),(2),(3);
INSERT 0 3
test=# create or replace function f_t7(i int) returns int as $$
test$# declare
test$# begin
test$# raise notice 'called';
test$# return i;
test$# end;
test$# $$ language plpgsql stable;
CREATE FUNCTION -- SELECT子句中, stable函数不被优化器优化
test=# select f_t7(1),* from t7;
NOTICE: called --多次调用
NOTICE: called
NOTICE: called
f_t7 | id
------+----
1 | 1
1 | 2
1 | 3
(3 行记录) test=# alter function f_t7(int) immutable;
ALTER FUNCTION -- immutable函数被替换成常量, 只执行一次.
test=# select f_t7(1),* from t7;
NOTICE: called --一次调用
f_t7 | id
------+----
1 | 1
1 | 2
1 | 3
(3 行记录) --当函数为stable时, 优化器没有处理这种过滤条件, 理论上是可以优化为一次调用f_t7(1)的. 目前仅仅immutable被优化.
test=# alter function f_t7(int) stable;
ALTER FUNCTION 当使用索引扫描时, stable在这里只会执行一次. test=# select * from t7 where id=f_t7(1);
NOTICE: called --有一次为explain的评估输出.
NOTICE: called
NOTICE: called
NOTICE: called
id
----
1
(1 行记录) -- 将id=f_t7(1)替换成f_t7(1)=1
test=# select * from t7 where f_t7(1)=1;
NOTICE: called
id
----
1
2
3
(3 行记录) test=# explain select * from t7 where f_t7(1)=1;
QUERY PLAN
------------------------------------------------------------
Result (cost=0.25..35.65 rows=2540 width=4)
One-Time Filter: (f_t7(1) = 1)
-> Seq Scan on t7 (cost=0.25..35.65 rows=2540 width=4)
(3 行记录) test=# alter function f_t7(int) immutable;
ALTER FUNCTION
test=# explain select * from t7 where f_t7(1)=1;
NOTICE: called
QUERY PLAN
------------------------------------------------------
Seq Scan on t7 (cost=0.00..35.40 rows=2540 width=4)
(1 行记录)
理解函数的三态
VOLATILE
1)volatile函数没有限制, 可以修改数据(如执行delete, insert , update).
2)使用同样的参数调用可能返回不同的值.
3)volatile函数不能被优化器选择作为优化条件.(例如减少调用, 函数索引, 索引扫描不允许使用volatile函数)
4)在同一个查询中, 同样参数的情况下可能被多次执行(QUERY有多行返回/扫描的情况下).
5)snapshot为函数内的每个query开始时的snapshot. 因此对在函数执行过程中, 外部已提交的数据可见.(仅限于调用函数的事务隔离级别为read committed)
STABLE
1)stable和immutable函数, 函数内不允许修改数据.(如PGver>=8.0 函数内不可执行非SELECT|PERFORM语句.)
2)使用同样的参数调用返回同样的结果, 在事务中有这个特性的也归属stable.
3)优化器可根据实际场景优化stable函数的调用次数, 同样的参数多次调用可减少成单次调用.
4)stable和immutable函数可用于优化器选择合适的索引扫描, 因为索引扫描仅评估被比较的表达式一次, 后多次与索引值进行比较.
5)stable和volatile函数都不能用于创建函数索引, 只有immutable函数可以用于创建函数索引.
6)stable和immutable函数, snapshot为外部调用函数的QUERY的snapshot, 函数内部始终保持这个snapshot, 外部会话带来的的数据变更不被反映到函数执行过程中.
IMMUTABLE
1)不允许修改数据, 使用同样的参数调用返回同样的结果.
2)优化器在处理immutable函数时, 先评估函数结果, 将结果替换为常量.
3)因此使用约束优化查询的场景中也只识别immutable函数.
STABLE和IMMUTABLE的区别
1)STABLE函数在SELECT和WHERE子句中不被优化, 仅仅当使用索引扫描时WHERE子句对应的STABLE函数才会被优化为1次调用.
2)在PREPARED STATEMENT中的使用区别:
3)IMMUTABLE函数在PLAN时以常量替代,STABLE函数在EXECUTE阶段被执行.
4)因此IMMUTABLE函数参数为常量时,在PREPARED STATEMENT场景只执行一次,而STABLE函数被多次执行.
函数稳定性通过查看PG_PROC/SYS_PROC.PROVOLATILE得到
PROVOLATILE 说明函数是仅仅只依赖 于它的输入参数 ,还是会被外部 因素影响。
值 I 表示“不变的”函数,它对于相同的输入总是输出相同的结果。
值 S 表示“稳定的”函数,它的结果(对于固定输入在一次扫描内不会变化。
值 V 表示“不稳定的”函数,它的结果在任何时候都可能变化。
使用V页表示函数具有副作用,所以对它们的调用无法得到优化。
KingbaseES函数三态的更多相关文章
- KingbaseES 函数稳定性与SQL性能
背景:客户现场的一次艰苦的调优过程(https://www.cnblogs.com/kingbase/p/16015834.html),让我觉得非常有必要让数据库用户了解函数的不同稳定性属性,及其对于 ...
- KingbaseES函数如何返回结果集
函数返回值一般是某一类型值,如int,varchar,date等,返回结果集时就需要用到setof语法. 创建数据 create table class(id number primary key, ...
- KingbaseES 与Oracle 函数稳定性对于性能影响差异比较
一.函数的属性 KingbaseES 函数在定义时有三种稳定性级别:volatile.stable 和 immutable.默认情况下,创建函数的稳定性为volatile.以下是这三种函数的区别: V ...
- KingbaseES时间函数的比较
KingbaseES提供了多种的时间函数,这些函数在使用过程中存在哪些不同? **同一事务** test=# begin test-# for i in 1.. 10 loop test-# rais ...
- KingbaseES 格式化函数
KingbaseES格式化函数提供一套有效的工具用于把各种数据类型(日期/时间.integer.floating point和numeric)转换成格式化的字符串以及反过来从格式化的字符串转换成指定的 ...
- KingbaseES 实现MYSQL hex/unhex 函数
MySQL 的hex 和 unhex 函数类似于KingbaseES 的encode 和 decoding,实现字符与16进制之间的转换. 一.先看MySQL例子 mysql> select h ...
- KingbaseES 行列转换函数
关键字: 行专列,列转行, pivot, unpivot 行列转换是在数据分析中经常用到的一项功能,KingbaseES从V8R6C3B0071版本开始通过扩展插件(kdb_utils_func ...
- KingbaseES sys_blocking_pids 函数
会话出现了锁等待,想要快速查询到堵塞的会话,可以使用 sys_blocking_pids 函数来实现这一目的. sys_blocking_pids:获取哪些会话阻塞了某个会话(输入参数). sys_b ...
- KingbaseES DENSE_RANK 函数用法
DENSE_RANK()函数用于为结果集分区内的每一行分配一个排名,排名值之间没有差距,函数为结果集的每个分区中的每一行分配一个等级. 与 RANK() 函数不同的是,DENSE_RANK() 函数总 ...
- KingbaseES 中可以通过构造一个聚集函数来实现mysql的any_value功能。
示例 创建函数 create function coalesce_("anyelement","anyelement") returns "anyel ...
随机推荐
- jupyter初体验
安装: 1.若是已经安装了anaconda,则通过 jupyter notebook 命令进入: 2.若是只安了python: pip3 install --upgrade pip 对pip进行 ...
- mybatis一对多映射分页的问题
一对多可能会出现分页错误 条数不对的问题 解决方法: 将主表分页查询一次 SELECT aa.id,aa.name,bb.name FROM (SELECT * from tab1 ORDER BY ...
- beego-orm-sqlite
dao.go ``` package daoimport ( "fmt" "github.com/astaxie/beego/orm" "html&q ...
- 09 | 从容器到容器云:谈谈Kubernetes的本质
你好,我是张磊.今天我和你分享的主题是:从容器到容器云,谈谈Kubernetes的本质. 在前面的四篇文章中,我以Docker项目为例,一步步剖析了Linux容器的具体实现方式.通过这些讲解你应该能够 ...
- go cookie session
https://astaxie.gitbooks.io/build-web-application-with-golang/content/zh/06.1.html
- VS 新版本无法打开旧项目问题处理
问题 最近想阅读 WorkflowCore 的源码,苦于代码量巨大,就想将项目回退到 Init Commit 版本 但是在回退版本后,工程内Project 显示已卸载 重新加载后 提示: 不支持 Th ...
- 轻松玩转awk
awk的处理方式 awk一次处理一行内容 awk对每行可以进行切片处理 例如 awk -F ':' '{print $1}' /etc/password -F指定每一行分割符号,这样就把被每行被:分割 ...
- 【Java SE】Day09 继承、super、this、抽象类
一.继承 1.概述 多个类具有相同属性和行为,共性抽取到一个类中(父类) 父类更通用,子类更具体 2.继承后的成员变量 本类:this.成员变量名 父类:super.成员变量名 3.继承后的成员方法 ...
- 【Day04】Spring Cloud 升华篇:容器化技术docker和kurbernetes
一.介绍 1.要考虑的问题 微服务数量有很多 中间件的部署-nacos-server sentinel-server 如何部署多个服务和中间件? 2.存在问题---机器上直接解压使用 资源利用率的问题 ...
- WPF中的“资源”
WPF中的"资源" 资源概述 WPF中的资源的概念有点类似 web 技术中的静态资源的概念.可以是一个样式,也可以是一个button的边框设置集合. 可以简单的将资源分为如下几个类 ...