结果集缓是用于存储经常使用的SQL语句和函数的查询结果。当相同语句再次执行的时候,DM8就不用再次重复执行(包括扫描索引、回表、计算、逻辑读、物理读等操作),而是直接访问结果集缓存得到结果。从而显著地改进需要多次执行和查询结果相同的SQL语句的性能。

结果集缓存最适合的是静态表。若SQL语句中包含的表做了增删改操作,则相关的所有的SQL缓存结果集将自动失效。所以,该功能只对那些在平时几乎没有任何DML操作的只读表比较有用,可以有效减轻I/O压力。

结果集缓存需要设置如下参数:

RS_CAN_CACHE非0
USE_PLN_POOL非0

属性为 FORWARD ONLY的游标需要设置BUILD_FORWARD_RS=1才能缓存结果集


1、创建测试表

create table T1
(   ID INT, 
    NAME VARCHAR(10) );
create table T2
(   ID INT, 
    NAME VARCHAR(10) );
insert into t1
select trunc(DBMS_RANDOM.value(1,10)),DBMS_RANDOM.string('A',10)
from dual connect by level <=10;
insert into t2
select trunc(DBMS_RANDOM.value(1,10000000)),DBMS_RANDOM.string('A',10)
from dual connect by level <=10000000;

2、未设参数时

2.1 第一次查询

disql上执行查询语句

SQL> select * from t1,t2 where t1.id=t2.id
2   /

行号       ID          NAME       ID          NAME
---------- ----------- ---------- ----------- ----------
1          1           SnHxkYykPF 1           WJWyDebcjM
2          1           TcbakFxIow 1           WJWyDebcjM
3          9           dRSQTiCqKm 9           wolBcvhRBu
4          5           CGsQFRxEdb 5           AMqiUnrICK
5          2           FLFvlWPhYJ 2           DBhZIaVFJj
6          8           EAuysWPjgE 8           ByNitQdMUY
7          8           EAuysWPjgE 8           bXtYMJaOGH
8          5           CGsQFRxEdb 5           NgxNOjRPNN

8 rows got

1   #NSET2: [1856, 989999->8, 104]
2     #PRJT2: [1856, 989999->8, 104]; exp_num(4), is_atom(FALSE)
3       #HASH2 INNER JOIN: [1856, 989999->8, 104];  KEY_NUM(1), MEM_USED(12143KB), DISK_USED(0KB)
4         #CSCN2: [1, 10->10, 52]; INDEX33562606(T1)
5         #CSCN2: [1155, 10000000->10000000, 52]; INDEX33562607(T2)

已用时间: 00:00:19.395. 执行号:500.

由上可见,执行计划对SQL语句涉及的t1和t2表分别进行全表扫描,然后进行HASH连接,共耗时19.395秒

select dbms_sqltune.report_sql_monitor(sql_exec_id=>500);显示执行计划详细报告。

MySQL暂存查询值到一个变量里面_SQL

由上可见98.14%的时间消耗在t2表的扫描上了。

查询v$cachers视图

SQL> select a.sqlcache,b.exec_time from v$cachepln a,v$cachers b
2   where a.sqlstr='select * from t1,t2 where t1.id=t2.id' and b.pln=a.cache_item;
未选定行

由上可见未缓存结果集

查询v$sql_history视图

SQL> select start_time,seq_no,sql_id,time_used,exec_id,n_logic_read,hard_parse_flag
2   from v$sql_history
3   where top_sql_text = 'select * from t1,t2 where t1.id=t2.id';

行号       START_TIME                 SEQ_NO      SQL_ID      TIME_USED            EXEC_ID     N_LOGIC_READ HARD_PARSE_FLAG
---------- -------------------------- ----------- ----------- -------------------- ----------- ------------ ---------------
1          2022-12-21 17:40:52.000000 1           6           19389627             500         241605073    2

由上可见time_used19389627微秒,去除计算误差,与disql中执行语句统计的时间19.395秒相同。HARD_PARSE_FLAG表示硬解析。

查询v$sql_stat_history视图

SQL> select sql_id,hard_parse_cnt,parse_cnt,exec_time,max_mem_used
2   from v$sql_stat_history
3   where sql_txt='select * from t1,t2 where t1.id=t2.id';

行号       SQL_ID      HARD_PARSE_CNT       PARSE_CNT            EXEC_TIME            MAX_MEM_USED
---------- ----------- -------------------- -------------------- -------------------- --------------------
1          6           1                    1                    19395                20800

由上可见exec_time19395毫秒,去除计算误差,与disql中执行语句统计的时间19.395秒相同。

2.2 第二次查询

1   #NSET2: [1856, 989999->8, 104]
2     #PRJT2: [1856, 989999->8, 104]; exp_num(4), is_atom(FALSE)
3       #HASH2 INNER JOIN: [1856, 989999->8, 104];  KEY_NUM(1), MEM_USED(12143KB), DISK_USED(0KB)
4         #CSCN2: [1, 10->10, 52]; INDEX33562606(T1)
5         #CSCN2: [1155, 10000000->10000000, 52]; INDEX33562607(T2)

已用时间: 972.913(毫秒). 执行号:505.

 由上可见,执行计划没有变化,共耗时972毫秒,比第一次执行快了很多。分析原因可能是第一次查询时表数据已经加载到buffercahe。

select dbms_sqltune.report_sql_monitor(sql_exec_id=>505);显示执行计划详细报告。

MySQL暂存查询值到一个变量里面_数据库_02

与第一查询相比,CSCN2(T2)全表扫描t2表的时间大幅缩短。logical read为48313,跟t2表的大小一致。为什么第一次查询时241605073,数字那么大?这个留做以后探索。

查询v$sql_history视图

行号       START_TIME                 SEQ_NO      SQL_ID      TIME_USED            EXEC_ID     N_LOGIC_READ HARD_PARSE_FLAG
---------- -------------------------- ----------- ----------- -------------------- ----------- ------------ ---------------
1          2022-12-21 17:40:52.000000 1           6           19389627             500         241605073    2
2          2022-12-21 18:06:35.000000 11          6           972759               505         48313        0

由上可见增加了一条,time_used 972759微秒,去除计算误差,与disql中执行语句统计的时间972毫秒相同。HARD_PARSE_FLAG等于0表示本次为软解析。

查询v$sql_stat_history视图

行号       SQL_ID      HARD_PARSE_CNT       PARSE_CNT            EXEC_TIME            MAX_MEM_USED
---------- ----------- -------------------- -------------------- -------------------- --------------------
1          6           0                    1                    972                  20864
2          6           1                    1                    19395                20800

由上可见增加了一条,exec_time972毫秒,与disql中执行语句统计的时间972毫秒相同相同。HARD_PARSE_CNT硬分析0次,PARSE_CNT软分析1次。

2.3 第三次查询

1   #NSET2: [1856, 989999->8, 104]
2     #PRJT2: [1856, 989999->8, 104]; exp_num(4), is_atom(FALSE)
3       #HASH2 INNER JOIN: [1856, 989999->8, 104];  KEY_NUM(1), MEM_USED(12143KB), DISK_USED(0KB)
4         #CSCN2: [1, 10->10, 52]; INDEX33562606(T1)
5         #CSCN2: [1155, 10000000->10000000, 52]; INDEX33562607(T2)

已用时间: 00:00:01.076. 执行号:507.

 由上可见,执行计划没有变化,共耗时1.076秒,跟第二次执行相近。

select dbms_sqltune.report_sql_monitor(sql_exec_id=>505);显示执行计划详细报告。

MySQL暂存查询值到一个变量里面_sql_03

与第二查询相近

查询v$sql_history视图

行号       START_TIME                 SEQ_NO      SQL_ID      TIME_USED            EXEC_ID     N_LOGIC_READ HARD_PARSE_FLAG
---------- -------------------------- ----------- ----------- -------------------- ----------- ------------ ---------------
1          2022-12-21 17:40:52.000000 1           6           19389627             500         241605073    2
2          2022-12-21 18:06:35.000000 11          6           972759               505         48313        0
3          2022-12-21 18:13:08.000000 15          6           1075767              507         48313        0

由上可见增加了一条,time_used 1075767微秒,去除计算误差,与disql中执行语句统计的时间1.076秒相同。HARD_PARSE_FLAG等于0表示本次为软解析。

行号       SQL_ID      HARD_PARSE_CNT       PARSE_CNT            EXEC_TIME            MAX_MEM_USED
---------- ----------- -------------------- -------------------- -------------------- --------------------
1          6           0                    1                    1075                 20864
2          6           0                    1                    972                  20864
3          6           1                    1                    19395                20800

由上可见增加了一条,exec_time1075毫秒,与disql中执行语句统计的时间1.076秒相同相同。HARD_PARSE_CNT硬分析0次,PARSE_CNT软分析1次。

3、设置参数

sp_set_para_value(2,'RS_CAN_CACHE',1);
sp_set_para_value(2,'USE_PLN_POOL',1);
sp_set_para_value(2,'BUILD_FORWARD_RS',1);

重启dmsever

SQL> select para_name,para_value from v$dm_ini where para_name in ('RS_CAN_CACHE','BUILD_FORWARD_RS','USE_PLN_POOL');

行号       PARA_NAME        PARA_VALUE
---------- ---------------- ----------
1          USE_PLN_POOL     1
2          RS_CAN_CACHE     1
3          BUILD_FORWARD_RS 1

4、设置参数后

4.1 第一次查询

disql上执行查询语句select * from t1,t2 where t1.id=t2.id

SQL> select * from t1,t2 where t1.id=t2.id
2   /

行号       ID          NAME       ID          NAME
---------- ----------- ---------- ----------- ----------
1          1           SnHxkYykPF 1           WJWyDebcjM
2          1           TcbakFxIow 1           WJWyDebcjM
3          9           dRSQTiCqKm 9           wolBcvhRBu
4          5           CGsQFRxEdb 5           AMqiUnrICK
5          2           FLFvlWPhYJ 2           DBhZIaVFJj
6          8           EAuysWPjgE 8           ByNitQdMUY
7          8           EAuysWPjgE 8           bXtYMJaOGH
8          5           CGsQFRxEdb 5           NgxNOjRPNN

8 rows got

1   #NSET2: [1856, 989999->8, 104]
2     #PRJT2: [1856, 989999->8, 104]; exp_num(4), is_atom(FALSE)
3       #HASH2 INNER JOIN: [1856, 989999->8, 104];  KEY_NUM(1), MEM_USED(12143KB), DISK_USED(0KB)
4         #CSCN2: [1, 10->10, 52]; INDEX33562606(T1)
5         #CSCN2: [1155, 10000000->10000000, 52]; INDEX33562607(T2)

已用时间: 00:00:19.840. 执行号:600.

由上可见,执行计划对SQL语句涉及的t1和t2表分别进行全表扫描,然后进行HASH连接,共耗时19.840秒

select dbms_sqltune.report_sql_monitor(sql_exec_id=>600);显示执行计划详细报告。

MySQL暂存查询值到一个变量里面_SQL_04

由上可见98.09%的时间消耗在t2表的扫描上了。

查询v$cachers视图

SQL> select a.sqlcache,b.exec_time from v$cachepln a,v$cachers b
2   where a.sqlstr='select * from t1,t2 where t1.id=t2.id' and b.pln=a.cache_item;

行号       SQLCACHE             EXEC_TIME
---------- -------------------- -----------
1          6640895272           19843

由上可见,结果集已经缓存,缓存的结果集查询用了19843毫秒。

查询v$sql_history视图

SQL> select sql_id,hard_parse_cnt,parse_cnt,exec_time,max_mem_used
2   from v$sql_stat_history
3   where sql_txt='select * from t1,t2 where t1.id=t2.id';

行号       START_TIME                 SEQ_NO      SQL_ID      TIME_USED            EXEC_ID     N_LOGIC_READ HARD_PARSE_FLAG
---------- -------------------------- ----------- ----------- -------------------- ----------- ------------ ---------------
1          2022-12-21 18:48:41.000000 3           8           19834727             600         241605073    2

由上可见time_used19834727微秒,去除计算误差,与disql中执行语句统计的时间19.840秒相同。HARD_PARSE_FLAG表示硬解析。

查询v$sql_stat_histrory视图

SQL> select sql_id,hard_parse_cnt,parse_cnt,exec_time,max_mem_used
2   from v$sql_stat_history
3   where sql_txt='select * from t1,t2 where t1.id=t2.id';

行号       SQL_ID      HARD_PARSE_CNT       PARSE_CNT            EXEC_TIME            MAX_MEM_USED
---------- ----------- -------------------- -------------------- -------------------- --------------------
1          8           1                    1                    19840                20800

由上可见exec_time19840毫秒,与disql中执行语句统计的时间19.840秒相同。

4.2 第二次查询

1   #NSET2: [1856, 989999->8, 104]
2     #PRJT2: [1856, 989999->8, 104]; exp_num(4), is_atom(FALSE)
3       #HASH2 INNER JOIN: [1856, 989999->8, 104];  KEY_NUM(1), MEM_USED(12143KB), DISK_USED(0KB)
4         #CSCN2: [1, 10->10, 52]; INDEX33562606(T1)
5         #CSCN2: [1155, 10000000->10000000, 52]; INDEX33562607(T2)

已用时间: 0.377(毫秒). 执行号:0.

执行时间0.377毫秒,比上次19.840秒大幅缩短。因为是从结果集缓存直接读取。请注意执行号为0。实际并没有按照原计划执行。

查询v$sql_history视图

行号       START_TIME                 SEQ_NO      SQL_ID      TIME_USED            EXEC_ID     N_LOGIC_READ HARD_PARSE_FLAG
---------- -------------------------- ----------- ----------- -------------------- ----------- ------------ ---------------
1          2022-12-21 18:48:41.000000 3           8           19834727             600         241605073    2

由上可见,从结果集缓存池直接读取的语句在v$sql_history视图不会显示。

查询v$sql_stat_history视图

行号       SQL_ID      HARD_PARSE_CNT       PARSE_CNT            EXEC_TIME            MAX_MEM_USED
---------- ----------- -------------------- -------------------- -------------------- --------------------
1          8           0                    1                    0                    0
2          8           1                    1                    19840                20800

由上可见增加了一条,EXEC_TIME 0毫秒,MAX_MEM_USED 0,代表直接从结果集缓存池中读取。HARD_PARSE_CNT硬分析0次,PARSE_CNT软分析1次。

4.3 第三次查询

1   #NSET2: [1856, 989999->8, 104]
2     #PRJT2: [1856, 989999->8, 104]; exp_num(4), is_atom(FALSE)
3       #HASH2 INNER JOIN: [1856, 989999->8, 104];  KEY_NUM(1), MEM_USED(12143KB), DISK_USED(0KB)
4         #CSCN2: [1, 10->10, 52]; INDEX33562606(T1)
5         #CSCN2: [1155, 10000000->10000000, 52]; INDEX33562607(T2)

已用时间: 0.189(毫秒). 执行号:0.

执行时间0.189毫秒,比第一次查询19.840秒大幅缩短。因为是从结果集缓存直接读取。请注意执行号为0。实际并没有按照原计划执行。

查询v$sql_history视图

行号       START_TIME                 SEQ_NO      SQL_ID      TIME_USED            EXEC_ID     N_LOGIC_READ HARD_PARSE_FLAG
---------- -------------------------- ----------- ----------- -------------------- ----------- ------------ ---------------
1          2022-12-21 18:48:41.000000 3           8           19834727             600         241605073    2

由上可见,从结果集缓存池直接读取的语句在v$sql_history视图不会显示。

查询v$sql_stat_history视图

行号       SQL_ID      HARD_PARSE_CNT       PARSE_CNT            EXEC_TIME            MAX_MEM_USED
---------- ----------- -------------------- -------------------- -------------------- --------------------
1          8           0                    1                    0                    0
2          8           0                    1                    0                    0
3          8           1                    1                    19840                20800

由上可见增加了一条,EXEC_TIME 0毫秒,MAX_MEM_USED 0,代表直接从结果集缓存池中读取。HARD_PARSE_CNT硬分析0次,PARSE_CNT软分析1次。