KingbaseES 与Oracle 函数稳定性对于性能影响差异比较

2022-11-08,,,,

一、函数的属性

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

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

稳定性级别使得优化器可以判断不同函数的行为。为了得到最佳的优化结果,在创建函数时我们应该指定严格的稳定性级别。

二、不同属性对于性能影响

以下举例说明函数的属性影响相同参数情况多次执行的效率。

1、构建函数和数据

create table test(id integer); 并插入100条完全相同的数据。

create or replace function test_volatile(id integer)
returns bigint
volatile
language sql
as
$$ select count(*) from t1 $$ ;
/ create or replace function test_stable(id integer)
returns bigint
stable
language sql
as
$$ select count(*) from t1 $$ ;
/ create or replace function test_immutable(id integer)
returns bigint
immutable
language sql
as
$$ select count(*) from t1 $$ ;
/

2、以列为参数调用函数

可以看到三个函数调用时间基本没有差别,因为,传入的参数是id 变量值(虽然实际值是相同的)。

test=# select count(*) from test where test_volatile(id)=1;
count
-------
0
(1 row) Time: 39652.495 ms (00:39.652)

test=# select count(*) from test where test_stable(id)=1;
count
-------
0
(1 row) Time: 38789.952 ms (00:38.790)

test=# select count(*) from test where test_immutable(id)=1; count
-------
0
(1 row) Time: 38591.957 ms (00:38.592)

3、函数在等式右边的情况

test=# select count(*) from test where id=test_volatile(1);
count
-------
0
(1 row) Time: 40353.777 ms (00:40.354)
test=# select count(*) from test where id=test_stable(1);
count
-------
0
(1 row) Time: 40500.253 ms (00:40.500)
test=# select count(*) from test where id=test_immutable(1);
count
-------
0
(1 row) Time: 374.300 ms

4、传入常量值

可以看到,对于常量值,stable 和 immutable 类型的函数实际只需调用一次。

test=# select count(*) from test where test_volatile(1)=1;
count
-------
0
(1 row) Time: 41647.551 ms (00:41.648)
test=# select count(*) from test where test_stable(1)=1;
count
-------
0
(1 row) Time: 399.161 ms test=# select count(*) from test where test_immutable(1)=1;
count
-------
0
(1 row) Time: 389.367 ms

5、for循环

可以看到,对于相同的输入参数, test_immutable 在同一query 只执行一次。

对于for 循环,实际结果相同。

test=# begin
test-# for i in 1..100 loop
test-# perform test_immutable(1);
test-# end loop;
test-# end;
test-# /
ANONYMOUS BLOCK
Time: 1001.184 ms (00:01.001)

6、等式右边的函数属性也会影响索引的使用

来看以下例子:

test=# create table t2(id integer,name char(999));
CREATE TABLE ^
test=# insert into t2 select generate_series(1,100),repeat('a',999);
INSERT 0 100
test=# create index ind_t2 on t2(id);
CREATE INDEX
test=#
test=# analyze t2;
ANALYZE
test=# explain analyze select count(*) from t2 where id=test_volatile(1);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------
Aggregate (cost=41.25..41.26 rows=1 width=8) (actual time=38735.427..38735.428 rows=1 loops=1)
-> Seq Scan on t2 (cost=0.00..41.25 rows=1 width=0) (actual time=38735.424..38735.425 rows=0 loops=1)
Filter: (id = test_volatile(1))
Rows Removed by Filter: 100
Planning Time: 0.191 ms
Execution Time: 38735.447 ms
(6 rows) test=# explain analyze select count(*) from t2 where id=test_stable(1);
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=8.41..8.42 rows=1 width=8) (actual time=385.449..385.450 rows=1 loops=1)
-> Index Only Scan using ind_t2 on t2 (cost=0.39..8.41 rows=1 width=0) (actual time=385.446..385.446 rows=0 loops=1)
Index Cond: (id = test_stable(1))
Heap Fetches: 0
Planning Time: 398.499 ms
Execution Time: 385.487 ms
(6 rows) test=# explain analyze select count(*) from t2 where id=test_immutable(1);
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Aggregate (cost=8.16..8.17 rows=1 width=8) (actual time=0.012..0.012 rows=1 loops=1)
-> Index Only Scan using ind_t2 on t2 (cost=0.14..8.16 rows=1 width=0) (actual time=0.011..0.011 rows=0 loops=1)
Index Cond: (id = '10000000'::bigint)
Heap Fetches: 0
Planning Time: 383.902 ms
Execution Time: 0.032 ms
(6 rows) 

可以看到volatile 情况下,无法使用索引

四、oracle是怎么处理的?

SQL> create table test(id integer);

Table created.

SQL> insert into test select 1 from dba_objects where rownum<101;

100 rows created.

create or replace function test_deterministic(id integer)
return integer
deterministic
as
cnt integer;
begin
for i in 1..10 loop
select count(*) into cnt from t1 ;
end loop;
return cnt;
end ; create or replace function test_volatile(id integer)
return integer
as
cnt integer;
begin
for i in 1..10 loop
select count(*) into cnt from t1 ;
end loop;
return cnt;
end ;

测试结果如下:

SQL> select * from test where id=test_volatile(1);
no rows selected
Elapsed: 00:00:50.50 SQL> select * from test where id=test_deterministic(1);
no rows selected
Elapsed: 00:00:01.04 SQL> select * from test where test_volatile(id)=1;
no rows selected
Elapsed: 00:00:50.56 --这个与KingbaseES 不同,只需调用一次
SQL> select * from test where test_deterministic(id)=1;
no rows selected
Elapsed: 00:00:01.02 --这个与KingbaseES 不同,只需调用一次
SQL> select * from test where test_volatile(1)=1;
no rows selected
Elapsed: 00:00:00.51 SQL> select * from test where test_deterministic(1)=1;
no rows selected
Elapsed: 00:00:00.52

KingbaseES 与Oracle 函数稳定性对于性能影响差异比较的相关教程结束。

《KingbaseES 与Oracle 函数稳定性对于性能影响差异比较.doc》

下载本文的Word格式文档,以方便收藏与打印。