KingbaseES 函数稳定性与子查询提升

背景:客户现场的一次艰苦的调优过程(https://www.cnblogs.com/kingbase/p/16015834.html),让我觉得非常有必要让数据库用户了解函数的不同稳定性属性,及其对于SQL性能可能带来的影响。很多DBA、开发人员根本就不在意,或者根本就没意识到函数稳定性对于性能的影响,这就导致出现性能问题时没有头绪。以下的例子都是实际客户现场问题的提炼,为了让用户有直观的了解。

一、函数的三种稳定态

函数的稳定性状态,简单地说就是相同的输入参数情况下,函数返回值是否相同。稳定性使得优化器可以判断不同函数的行为。为了得到最佳的优化结果,在创建函数时我们应该指定与函数功能相对应稳定性级别。

  • 如果本应该是 volatile ,而设置成 immutable,会导致结果错误。如:currtid 函数,不同时刻执行的结果可能是不同的,必须是volatile 。
  • 如果本应该是 immutable,而设置成 volatile , 就会导致性能问题。如:trunc 函数,不同时刻执行结果应该是相同的,只需执行一次即可。

PostgreSQL 或 KingbaseES 函数在定义时有三种稳定态级别:volatile、stable 和 immutable。默认情况下,创建函数的稳定性为volatile。以下是这三种函数的区别:

  • Volatile 函数可以做任何事情,包括修改数据库。在调用中,输入同样的参数可能会返回不同的结果,比如:currtid 。在一个Query中,对于每一行都会重新计算该函数。
  • Stable 函数不能修改数据库,单个Query中所有行给定同样的参数确保返回相同的结果。这种稳定级别允许优化器将多次函数调用转换为一次。在索引扫描的条件中使用这种函数是可行的,因为索引扫描只计算一次比较值(comparison value),而不是每行都计算一次。
  • Immutable 函数不能修改数据库,在任何情况下,只要输入参数相同,返回结果就相同。这种级别的函数,优化器可以提前进行计算,在查询过程中作为常量参数。比如:SELECT...WHERE x=2+2 可以简化为SELECT...WHERE x=4。

KingbaseES 为了兼容Oracle,增加了 Deterministic稳定态,等价于 immutable 。

以下例子同时在PostgreSQL 与 KingbaseES 进行过验证。

二、Volatile 函数影响子查询提升

1、构建例子

create table tab1(id1 integer,name1 varchar(9),addr1 text);
create table tab2(id2 integer,name2 varchar(9),addr2 text);
insert into tab1 select generate_series(1,1000000),generate_series(1,1000000),'abc';
insert into tab2 select generate_series(1,1000000),generate_series(1,1000000),'abc';
create index ind_tab1 on tab1(id1);
create index ind_tab2 on tab2(id2);

2、volatile 函数影响子查询提升

对于 tab2 表的访问无法使用索引。

test=# \df+ replace                                                                                                              
                                                                                      List of functions
 Schema |  Name   | Result data type  | Argument data types | Type | Volatility | Parallel | Owner  | Security | Access privileges | Language |           Source code           | Description 
--------+---------+-------------------+---------------------+------+------------+----------+--------+----------+-------------------+----------+---------------------------------+-------------
 sys    | replace | character varying | text, text, text    | func | volatile   | safe     | system | invoker  |                   | c        | ora_replace_text                | 


test=# explain select id1,name1 from tab1 a ,(select id2,replace(id2,'b','B') name2 from tab2 ) b where a.id1=b.id2 and name1='123'; 
                             QUERY PLAN                              
---------------------------------------------------------------------
 Hash Join  (cost=17935.01..52120.02 rows=1 width=10)
   Hash Cond: (tab2.id2 = a.id1)
   ->  Seq Scan on tab2  (cost=0.00..20435.00 rows=1000000 width=36)
   ->  Hash  (cost=17935.00..17935.00 rows=1 width=10)
         ->  Seq Scan on tab1 a  (cost=0.00..17935.00 rows=1 width=10)
               Filter: ((name1)::text = '123'::text)
(6 rows)

不使用子查询情况下的,可以使用索引:

test=# explain select  id1,name1,id2,replace(id2,'b','B') name2 from tab1 a,tab2 b where a.id1=b.id2 and name1='123'; 
                                  QUERY PLAN                                  
------------------------------------------------------------------------------
 Nested Loop  (cost=0.42..17943.46 rows=1 width=46)
   ->  Seq Scan on tab1 a  (cost=0.00..17935.00 rows=1 width=10)
         Filter: ((name1)::text = '123'::text)
   ->  Index Only Scan using ind_tab2 on tab2 b  (cost=0.42..8.44 rows=1 width=4)
         Index Cond: (id2 = a.id1)
(5 rows)

3、immutable 函数与执行计划

改成immutable 函数后,子查询可以提升,从而能够使用索引。

test=# \df+ replace
                                                                                      List of functions
 Schema |  Name   | Result data type  | Argument data types | Type | Volatility | Parallel | Owner  | Security | Access privileges | Language |           Source code           | Description 
--------+---------+-------------------+---------------------+------+------------+----------+--------+----------+-------------------+----------+---------------------------------+-------------
 sys    | replace | character varying | text, text, text    | func | immutable  | safe     | system | invoker  |                   | c        | ora_replace_text                | 


test=# explain select id1,name1 from tab1 a ,(select id2,replace(id2,'b','B') name2 from tab2 ) b where a.id1=b.id2 and name1='123'; 
                                 QUERY PLAN                                 
----------------------------------------------------------------------------
 Nested Loop  (cost=0.42..17943.45 rows=1 width=10)
   ->  Seq Scan on tab1 a  (cost=0.00..17935.00 rows=1 width=10)
         Filter: ((name1)::text = '123'::text)
   ->  Index Only Scan using ind_tab2 on tab2  (cost=0.42..8.44 rows=1 width=4)
         Index Cond: (id2 = a.id1)
(5 rows)

 四、结论

函数的稳定性特性不是PostgreSQL 独有的,Oracle 也有同样的机制(默认volatile , 可以加 deterministic),不同的稳定态会影响性能及数据的准确性。用户在创建函数时,必须对了解自己所创建的函数应归属的属性,错误的属性设置可能导致函数返回结果错误,以及函数执行的性能问题。

 

posted @ 2022-03-02 15:24  KINGBASE研究院  阅读(158)  评论(0编辑  收藏  举报