PostgreSQL 数据库性能 函数稳定性影响函数调用次数
1 函数的稳定性级别
KingbaseES 函数有三种稳定性级别:volatile、stable 和 immutable。默认情况下,创建函数的稳定性为volatile。以下是这三种函数的区别:
1、Volatile 函数。
(1)Volatile 函数体内可以做任何事情,包括修改数据库。
(2)在调用时,输入同样的参数可能会返回不同的结果,比如:currtid 。
(3)在一个Query中,对于每一行都会重新计算该函数。
2、Stable 函数。
(1)Stable 函数内不能修改数据库。
(2)单个Query中针对所有行,给定同样的参数确保返回相同的结果。这种稳定级别允许优化器将多次函数调用转换为一次。
(3)在索引扫描的条件中使用这种函数是可行的,因为索引扫描只计算一次比较值(comparison value),而不是每行都计算一次。
3、Immutable 函数
(1)不能修改数据库。
(2)在任何情况下,只要输入参数相同,返回结果就相同。
(3)优化器可以提前进行计算,在查询过程中作为常量参数。
比如:SELECT...WHERE x=2+2 可以简化为SELECT...WHERE x=4。
4、Deterministic 这是KingbaseES 为了与 Oracle 兼容而增加的一个属性,等价于 immutable 。
稳定性属性影响优化器的行为,为了得到最佳的优化结果,在创建函数时我们应该指定严格的稳定性级别。
2 kingbase 稳定性与函数调用次数
以下举例说明函数的稳定性和相同参数情况下的多次执行的效率。
2.1 环境准备
2.1.1 测试数据
DROP TABLE t01;
CREATE TABLE t01(id integer);
INSERT INTO t01 SELECT 1 FROM generate_series(1,500);
2.1.2 构建函数
1、构建 volatile 函数。
CREATE OR REPLACE FUNCTION test_volatile(v_id integer)
returns bigint
volatile
language sql
AS
$ SELECT count(*) FROM public.t01 WHERE id=v_id $ ;
2、构建 stable 函数。
CREATE OR REPLACE FUNCTION test_stable(v_id integer)
returns bigint
stable
language sql
AS
$ SELECT count(*) FROM public.t01 WHERE id=v_id $ ;
3、构建 immutable 函数。
CREATE OR REPLACE FUNCTION test_immutable(v_id integer)
returns bigint
immutable
language sql
AS
$ SELECT count(*) FROM public.t01 WHERE id=v_id $;
2.2 传入参数为字段,函数的调用次数
因为,传入的参数是id 变量值,三个函数调用时间基本没有差别。
观察时间输出,所有的行实际值是相同的,但都会增加调用函数次数,导致执行时间较长。
SELECT count(*) FROM public.t01 WHERE test_volatile(id)=500;
//屏幕输出:
count
-------
500
(1 行记录)
时间:36.391 ms
SELECT count(*) FROM t01 WHERE test_stable(id)=500;
//屏幕输出:
count
-------
500
(1 行记录)
时间:49.279 ms
SELECT count(*) FROM t01 WHERE test_immutable(id)=500;
//屏幕输出:
count
-------
500
(1 行记录)
时间:36.160 ms
2.3 传入参数为常量,函数的调用次数
观察时间输出,对于常量值,stable 和 immutable 类型的函数实际只需调用一次。
SELECT count(*) FROM t01 WHERE test_volatile(1)=500;
//屏幕输出:
count
-------
500
(1 行记录)
时间:37.372 ms
SELECT count(*) FROM t01 WHERE test_stable(1)=500;
//屏幕输出:
count
-------
500
(1 行记录)
时间:0.985 ms
SELECT count(*) FROM t01 WHERE test_immutable(1)=500;
//屏幕输出:
count
-------
500
(1 行记录)
时间:0.794 ms
3 Immutable 与 Stable 差异
1、Immutable 函数执行发生在规则优化时,也就是在SQL parse 时执行,只要执行一次;
2、而Stable函数则在execute时执行,针对每条tuple,函数都要执行一次。
3.1 SELECT 字段包含函数时,函数的调用次数
immutable 类型的函数实际只需调用一次。
EXPLAIN ANALYZE SELECT test_volatile(1) FROM t01;
//屏幕输出:
QUERY PLAN
---------------------------------
Seq Scan on t01 (cost=0.00..133.00 rows=500 width=8) (actual time=0.235..35.448 rows=500 loops=1)
Planning Time: 0.123 ms
Execution Time: 35.528 ms
(3 行记录)
时间:36.183 ms
EXPLAIN ANALYZE SELECT test_stable(1) FROM t01;
//屏幕输出:
QUERY PLAN
-------------------------------------------------------------------
Seq Scan on t01 (cost=0.00..133.00 rows=500 width=8) (actual time=0.239..37.429 rows=500 loops=1)
Planning Time: 0.211 ms
Execution Time: 37.512 ms
(3 行记录)
时间:38.174 ms
EXPLAIN ANALYZE SELECT test_immutable(1) FROM t01;
//屏幕输出:
QUERY PLAN
--------------------------
Seq Scan on t01 (cost=0.00..8.00 rows=500 width=8) (actual time=0.007..0.045 rows=500 loops=1)
Planning Time: 0.262 ms
Execution Time: 0.065 ms
(3 行记录)
时间:0.704 ms
3.2 函数在等式右边的场景,函数的调用次数
1、观察时间输出,immutable 类型的函数用时最少。
2、对于函数在等式右边时,immutable 类型的函数实际只需调用一次。
EXPLAIN ANALYZE SELECT count(*) FROM t01 WHERE id=test_volatile(1);
//屏幕输出:
QUERY PLAN
--------------------------------------------
Aggregate (cost=135.50..135.51 rows=1 width=8) (actual time=48.234..48.234 rows=1 loops=1)
-> Seq Scan on t01 (cost=0.00..134.25 rows=500 width=0) (actual time=48.232..48.232 rows=0 loops=1)
Filter: (id = test_volatile(1))
Rows Removed by Filter: 500
Planning Time: 0.173 ms
Execution Time: 48.266 ms
(6 行记录)
时间:48.830 ms
EXPLAIN ANALYZE SELECT count(*) FROM t01 WHERE id=test_stable(1);
//屏幕输出:
QUERY PLAN
----------------------------------
Aggregate (cost=134.25..134.26 rows=1 width=8) (actual time=35.539..35.540 rows=1 loops=1)
-> Seq Scan on t01 (cost=0.00..134.25 rows=1 width=0) (actual time=35.538..35.538 rows=0 loops=1)
Filter: (id = test_stable(1))
Rows Removed by Filter: 500
Planning Time: 0.447 ms
Execution Time: 35.562 ms
(6 行记录)
时间:36.462 ms
EXPLAIN ANALYZE SELECT count(*) FROM t01 WHERE id=test_immutable(1);
//屏幕输出:
QUERY PLAN
--------------------------------------
Aggregate (cost=9.25..9.26 rows=1 width=8) (actual time=0.046..0.047 rows=1 loops=1)
-> Seq Scan on t01 (cost=0.00..9.25 rows=1 width=0) (actual time=0.045..0.045 rows=0 loops=1)
Filter: (id = '500'::bigint)
Rows Removed by Filter: 500
Planning Time: 0.267 ms
Execution Time: 0.059 ms
(6 行记录)
时间:0.758 ms
4 volatile 与 immutable 差异
请参考 volatile 函数与 immutable函数差异
5 PLSQL 内循环调用的场景,函数的调用次数
5.1 当传入参数值相同时
1、对于相同的输入参数, test_immutable 在同一查询只执行一次。
2、volatile 与 stable 则要执行多次。
\set SQLTERM /
begin
for i in 1..100 loop
perform test_volatile(1);
end loop;
end;
/
//屏幕输出:
ANONYMOUS BLOCK
时间:17.801 ms
begin
for i in 1..100 loop
perform test_stable(1);
end loop;
end;
/
//屏幕输出:
ANONYMOUS BLOCK
时间:16.133 ms
begin
for i in 1..100 loop
perform test_immutable(1);
end loop;
end;
/
//屏幕输出:
ANONYMOUS BLOCK
时间:0.979 ms
5.2 当传入参数值不同时
1、函数调用次数与循环次数相同。
begin
for i in 1..100 loop
perform test_volatile(i);
end loop;
end;
/
//屏幕输出:
ANONYMOUS BLOCK
时间:17.963 ms
begin
for i in 1..100 loop
perform test_stable(i);
end loop;
end;
/
//屏幕输出:
ANONYMOUS BLOCK
时间:18.233 ms
begin
for i in 1..100 loop
perform test_immutable(i);
end loop;
end;
/
//屏幕输出:
ANONYMOUS BLOCK
时间:14.785 ms
6 小结
关于 Oracle 函数稳定性,目前不在kingbase 数据库讨论范围之内。