VastbaseG100

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

Menu

PL/pgSQL 编译缓存

本文主要介绍 Vastbase 数据库中 PL/pgSQL 的编译缓存功能。

功能描述

PL/pgSQL 的编译是一个耗时操作,每次运行前都重新编译对性能来说影响非常大。为解决这个问题,对于使用 PL/pgSQL 定义的函数/存储过程,Vastbase 数据库会将编译结果缓存在会话的内存中。在会话运行期间,如果函数/存储过程的定义没有发生变化,则只需要编译一次,后续函数/存储过程的运行会从缓存获取编译结果。

编译缓存判断逻辑

Vastbase 数据库使用 PL/pgSQL 编译缓存的判断逻辑如下:

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

2、计算调用函数的哈希键,并根据结果查询是否存在 PL/pgSQL 编译缓存。

Vastbase 数据库中,哈希键的计算规则受到参数类型与字符集排序规则影响,对于不同的字符集排序规则、存在 ANY 系列参数时,可能存在多个不同的哈希键。

3、若存在 PL/pgSQL 编译缓存,则进一步检查缓存的有效性,如果缓存已失效,则删除失效缓存。

4、若不存在 PL/pgSQL 编译缓存,或缓存已失效,则对函数进行编译。

注意事项

  • PL/pgSQL 编译缓存仅在会话中生效。

  • 对于不同的字符集排序规则、存在 ANY 系列参数时,可能存在多个不同的哈希键,即对应不同的 PL/pgSQL 编译缓存结果。

示例

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

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

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

vi tb_vinegar.sh

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


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

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

sh tb_vinegar.sh

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

vsql -d vastbase -f t_vinegar.sql

执行以下语句,可以打开多个窗口,查看执行时间。

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

返回结果为:

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

Time: 279.024 ms

4、重新执行 pkg_vinegar_99.f_99(),查看执行时间。

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

返回结果为:

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

Time: 19.223 ms

发现执行时间大幅减少,这是由于函数第一次编译的耗时远大于执行耗时。

5、退出当前会话,重新连接 Vastbase。

\q
vsql -d vastbase

6、再次执行 pkg_vinegar_99.f_99(),查看执行时间。

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

返回结果为:

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

Time: 262.737 ms

发现执行时间与第一次执行近似,这是由于编译缓存仅存储在会话中。

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

vi clean_vinegar.sh

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

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

echo "DROP TABLE tb_vinegar;" >> clean_vinegar.sql

执行清理脚本。

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