结果集缓是用于存储经常使用的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);显示执行计划详细报告。
由上可见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);显示执行计划详细报告。
与第一查询相比,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);显示执行计划详细报告。
与第二查询相近
查询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);显示执行计划详细报告。
由上可见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次。