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 变量值,三个函数调用时间基本没有差别。

Warning

观察时间输出,所有的行实际值是相同的,但都会增加调用函数次数,导致执行时间较长。

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 传入参数为常量,函数的调用次数

Warning

观察时间输出,对于常量值,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 字段包含函数时,函数的调用次数

Warning

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 函数在等式右边的场景,函数的调用次数

Warning

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 数据库讨论范围之内。