VastbaseG100

基于openGauss内核开发的企业级关系型数据库。

Menu

PL/pgSQL 全局缓存

本文主要介绍 Vastbase 数据库的 PL/pgSQL 全局缓存功能。

功能描述

Vastbase 在 Oracle 兼容模式下支持 PL/pgSQL 全局缓存,实现在 PL/pgSQL 在编译过程中,在函数本身和某些依赖项没有发生改变的时候(即判断函数/包有效的情况下),编译得到的产物可以在全局重复使用。且全局缓存支持多会话并发使用。

全局缓存判断逻辑

默认情况下,Vastbase 数据库在编译 PL/pgSQL 后会将编译信息缓存在会话中(以下称为会话缓存),有关 Vastbase 数据库会话缓存的判断逻辑,请参见 PL/pgSQL编译缓存

开启全局缓存后,Vastbase 数据库使用 PL/pgSQL 缓存的判断逻辑发生变化:

1、对于一条 SQL 中复数次使用的函数,Vastbase 数据库会通过存储函数编译信息的方式直接使用,这种情况下不会访问会话缓存。

2、计算调用函数的哈希键,并根据结果查询是否存在会话缓存。

3、若存在会话缓存,则进一步检查缓存的有效性,如果缓存已失效,则删除失效缓存。

4、若不存在会话缓存,或缓存已失效,则根据调用函数的哈希键,查询是否存在全局缓存。

5、检查全局缓存有效性,若有效,则使用全局缓存。

6、若不存在全局缓存,或缓存已失效,则对函数进行编译。

开启全局缓存

PL/pgSQL 全局缓存可通过如下参数控制:

GUC参数 参数功能 参数介绍
enable_plpgsql_global_compile_cache 开启全局缓存功能,若开启本参数,则对函数/包进行编译后,将 PLpgSQL 的类型与语法树放入全局缓存。修改重建函数后,其将在下一次检查编译时重新存入全局缓存。 参数为实例级别,默认值为off,修改后重启数据库生效。
plpgsql_compile_cache_max_size 用于设置允许全局缓存的大小,当全局缓存达到内存上限时,将会进行缓存替换。 参数为实例级别,默认值为100000000,修改后重启数据库生效。

查看全局缓存

PL/pgSQL全局缓存可通过如下函数查看相关信息:

函数名称 函数功能 调用语句
pl_global_compile_cache_entrys 用于查询所有缓存项目的状态、哈希值、类型等信息。
select * from pl_global_compile_cache_entrys();
pl_global_compile_cache_status 用于统计当前全局缓存拥有的缓存项数量和占用内存的大小
select * from pl_global_compile_cache_status();

注意事项

  • 该功能仅在数据库兼容模式为 Oracle 时支持(即数据库实例初始化时指定 DBCOMPATIBILITY='A')。

  • 开启全局缓存后,会影响全局范围内第一次执行函数/存储过程的性能。因此建议在多会话场景下开启本功能。有关该特性的示例,请参见 示例 1

  • 全局缓存仅会获取调用函数过程中编译产生的编译缓存,不会获取创建函数时产生的编译缓存。有关该特性的示例,请参见 示例 2

示例

示例 1:全局缓存对多会话场景首次执行函数的性能提升。

1、创建测试表,并导入数据。

CREATE TABLE tb_dumpling(
breed VARCHAR2(25),
name VARCHAR2(25),
birthdate DATE);

编辑测试脚本,在 sql 脚本中创建 100 个包,每个包中有一个函数,每个函数中包含一个变量。

vi tb_dumpling.sh

#!/bin/bash
for ((i=1;i<=100;i++))
do
if [ $i -eq 100 ]
then
echo "CREATE OR REPLACE package pkg_dumpling_0 as" >> t_dumpling.sql
echo "FUNCTION f_0 () RETURN VARCHAR ;" >> t_dumpling.sql;
echo "v_0 tb_dumpling%rowtype;">>t_dumpling.sql
echo "TYPE rec_type_0 IS RECORD(" >> t_dumpling.sql
echo "col_int int," >> t_dumpling.sql
echo "col_smallint SMALLINT," >> t_dumpling.sql
echo "col_integer INTEGER," >> t_dumpling.sql
echo "col_bigint BIGINT," >> t_dumpling.sql
echo "col_int2 INT2," >> t_dumpling.sql
echo "col_int4 INT4," >> t_dumpling.sql
echo "col_int8 INT8" >> t_dumpling.sql
echo ");" >> t_dumpling.sql
echo "r_0 rec_type_0;">>t_dumpling.sql
echo "END;" >> t_dumpling.sql
echo "/" >> t_dumpling.sql
else
echo "CREATE OR REPLACE package pkg_dumpling_$i as" >> t_dumpling.sql
echo "FUNCTION f_$i () RETURN VARCHAR ;" >> t_dumpling.sql
echo "v_$i tb_dumpling%rowtype;">>t_dumpling.sql
echo "TYPE rec_type_$i IS RECORD(" >> t_dumpling.sql
echo "col_int int," >> t_dumpling.sql
echo "col_smallint SMALLINT," >> t_dumpling.sql
echo "col_integer INTEGER," >> t_dumpling.sql
echo "col_bigint BIGINT," >> t_dumpling.sql
echo "col_int2 INT2," >> t_dumpling.sql
echo "col_int4 INT4," >> t_dumpling.sql
echo "col_int8 INT8" >> t_dumpling.sql
echo ");" >> t_dumpling.sql
echo "r_$i rec_type_$i;">>t_dumpling.sql
echo "END;" >> t_dumpling.sql
echo "/" >> t_dumpling.sql
fi
done


for ((i=1;i<=100;i++))
do
if [ $i -eq 100 ]
then
echo "CREATE OR REPLACE PACKAGE BODY pkg_dumpling_0 as " >> t_dumpling.sql
echo "FUNCTION f_0 () RETURN VARCHAR " >> t_dumpling.sql;
echo "AS" >> t_dumpling.sql
echo "BEGIN">>t_dumpling.sql
echo "RETURN '123'; " >> t_dumpling.sql
echo "END;" >> t_dumpling.sql
echo "END;" >> t_dumpling.sql
echo "/" >> t_dumpling.sql
else
echo "CREATE OR REPLACE PACKAGE BODY pkg_dumpling_$i as " >> t_dumpling.sql
echo "FUNCTION f_$i () RETURN VARCHAR " >> t_dumpling.sql
echo "AS " >> t_dumpling.sql
echo "BEGIN" >> t_dumpling.sql
echo "v_$i.breed=pkg_dumpling_$((i-1)).f_$((i-1));" >> t_dumpling.sql
echo "r_$i.col_int=10;" >> t_dumpling.sql
echo "dbms_output.put_line(v_$i.breed);" >> t_dumpling.sql
echo "dbms_output.put_line(r_$i.col_int);" >> t_dumpling.sql
echo "RETURN r_$i.col_int;" >> t_dumpling.sql
echo "END;" >> t_dumpling.sql
echo "END;" >> t_dumpling.sql
echo "/" >> t_dumpling.sql
fi
done

2、运行脚本,将 SQL 写入 t_dumpling.sql 文件。

sh tb_dumpling.sh

3、连接 Vastbase 数据库,查看查看执行时间。

vsql -d vastbase -f t_dumpling.sql

在会话 1 中执行以下语句。

\timing
SET max_stack_depth='10MB';
SELECT pkg_dumpling_99.f_99();

返回结果为:

 f_99
------
 10
(1 row)

Time: 262.737 ms

4、设置参数开启全局缓存功能。

修改如下参数后应重启数据库使其生效。

ALTER SYSTEM SET plpgsql_compile_cache_max_size=2100000000;
ALTER SYSTEM SET enable_plpgsql_global_compile_cache=on;

5、重启数据库后,在会话 2 中执行重新执行以上 SQL。

\timing
SET max_stack_depth='10MB';
SELECT pkg_dumpling_99.f_99();

返回结果为:

 f_99
------
 10
(1 row)

Time: 321.106 ms

发现执行时间大于未开启全局缓存的执行时间,这是由于 Vastbase 会将编译缓存拷贝到全局缓存中,以减少其他会话第一次执行的时间。

6、在会话 3 中再次执行以上 SQL。

\timing
SET max_stack_depth='10MB';
SELECT pkg_dumpling_99.f_99();

返回结果为:

 f_99
------
 10
(1 row)

Time: 89.362 ms

发现执行时间远小于前一次执行,这是由于全局缓存避免了不同会话间的重复编译。

7、执行上述语句后不退出会话,继续在会话 3 中执行以上 SQL。

\timing
SET max_stack_depth='10MB';
SELECT pkg_dumpling_99.f_99();

返回结果为:

 f_99
------
 10
(1 row)

Time: 19.401 ms

发现执行时间与 PLpgSQL编译缓存 中第二次执行时间近似,这是由于存在会话缓存时,Vastbase 数据库会优先获取会话缓存。

8、编写脚本删除测试包与包体。

vi clean_dumpling.sh

for ((i=1;i<=100;i++))
do
if [ $i -eq 100 ]
then
echo "DROP PACKAGE BODY pkg_dumpling_0;" >> clean_dumpling.sql
else
echo "DROP PACKAGE BODY pkg_dumpling_$i;" >> clean_dumpling.sql
fi
done

for ((i=1;i<=100;i++))
do
if [ $i -eq 100 ]
then
echo "DROP PACKAGE pkg_dumpling_0;" >> clean_dumpling.sql
else
echo "DROP PACKAGE pkg_dumpling_$i;" >> clean_dumpling.sql
fi
done

echo "DROP TABLE tb_dumpling;" >> clean_dumpling.sql

执行清理脚本。

sh clean_dumpling.sh
vsql -d vastbase -f clean_dumpling.sql

示例 2:全局缓存获取方式。

1、设置参数开启全局缓存功能。

修改如下参数后应重启数据库使其生效。

ALTER SYSTEM SET enable_plpgsql_global_compile_cache=1;

2、创建函数,其中在一个函数中调用另一个函数。

CREATE OR REPLACE FUNCTION sp_block7
(
MYINTEGER IN INTEGER ,
MYCHAR OUT VARCHAR2(200)
)
RETURNS VARCHAR2(200)
AS $$
DECLARE
BEGIN
MYCHAR := 'sp_block is called';
RAISE info 'MYINTEGER is %', MYINTEGER;
RETURN ;
END;
$$LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION sp_tempsp7()
RETURNS INTEGER
AS $$
DECLARE
MYINTEGER INTEGER ;
MYCHAR VARCHAR2(20);
PSV_SQL VARCHAR2(200);
BEGIN
MYINTEGER := 1;
PSV_SQL := 'BEGIN CALL sp_block7(:a,:b);END;';
EXECUTE IMMEDIATE PSV_SQL USING IN MYINTEGER, OUT MYCHAR;
RAISE info 'MYCHAR is %', MYCHAR;
RETURN 0;
END;
$$LANGUAGE plpgsql;

3、调用函数。

CALL sp_tempsp7();

返回结果为:

INFO:  MYINTEGER is 1
CONTEXT:  SQL statement "CALL sp_block7(:a,:b)"
PL/pgSQL function inline_code_block line 1 at EXECUTE statement
PL/pgSQL function public.sp_tempsp7() line 9 at EXECUTE statement
INFO:  MYCHAR is sp_block is called
 sp_tempsp7
------------
          0
(1 row)

4、查看当前全局缓存函数拥有的缓存项数量和占用内存的大小。

SELECT * FROM pl_global_compile_cache_status();

返回结果为:

 total_memory | used_memory | entry_count
--------------+-------------+-------------
   2100000000 |           0 |           0
(1 row)

此时没有产生全局缓存,这是由于创建函数时会对函数/存储过程进行编译,因此在同一会话中调用函数/存储过程不会再次编译并产生缓存。而全局缓存仅会获取调用函数时产生的编译缓存,因此此时不能查询到全局缓存。

5、切换会话,重新调用函数,并查看全局缓存(以用户实际情况为准)。

CALL sp_tempsp7();
SELECT * FROM pl_global_compile_cache_status();

返回结果为:

INFO:  MYINTEGER is 1
CONTEXT:  SQL statement "CALL sp_block7(:a,:b)"
PL/pgSQL function inline_code_block line 1 at EXECUTE statement
PL/pgSQL function public.sp_tempsp7() line 9 at EXECUTE statement
INFO:  MYCHAR is sp_block is called
 sp_tempsp7
------------
          0
(1 row)

vastbase=#
 total_memory | used_memory | entry_count
--------------+-------------+-------------
   2100000000 |      239344 |           2
(1 row)

此时产生了全局缓存,这是由于 Vastbase 数据库会在调用并编译函数时产生编译缓存,并将该缓存存储到全局缓存中。

相关链接

Oracle兼容性参数PLpgSQL编译缓存