为了避免多次对相同SQL进行重复解析造成性能损耗,数据库会将解析后的执行计划进行缓存,针对后续执行的SQL进行HASH值对比,确认是否能够进行计划重用。达梦数据库提供了参数USE_PLN_POOL控制是否开启计划重用功能,参数默认为1,即启用执行计划重用。参数值定义如下:
计划重用功能大大减少了SQL的硬解析,提升了数据库性能,但也可能对我们进行SQL优化工作产生影响,比如应用反馈一条SQL执行耗时很久,但运维人员客户端执行很快。本文将构造一个例子对场景进行复现并提供对应的排查方法。
构造测试表:
CREATE TABLE DMTEST1 AS SELECT LEVEL C1,'TESTABCD' || LEVEL C2 FROM DUAL CONNECT BY LEVEL < 10000001;
CREATE TABLE DMTEST2 AS SELECT LEVEL C1,'TESTABCD' || LEVEL C2 FROM DUAL CONNECT BY LEVEL < 10000001;
CREATE INDEX IDX_T1_C2 ON DMTEST1(C2);
CREATE INDEX IDX_T2_C1 ON DMTEST2(C1);
查看语句执行计划:
SELECT * FROM DMTEST1 A,DMTEST2 B WHERE A.C2 LIKE 'TESTABCD9999999%' AND A.C1 =B.C1 ;
可以看出因为我们没有更新统计信息,所以系统使用默认选择率估算A表过滤后仍有375000行记录,故选择使用HASH连接对B表全表扫描,此计划执行耗时0.484S:
ET结果如下:
查询V$CACHEPLN确认已生成对应SQL的缓存计划:
至此我们已模拟出一个应用端执行SQL缓慢的场景,此时我们对表DMTEST1的C2列进行统计信息更新,更新后客户端查看执行计划如下:
此时系统对表A的估算已准确,其中A表过滤后仅有1行数据,所以此时执行计划使用NLIJ,客户端再次执行SQL查看耗时和ET结果:
此时我们发现语句执行耗时没有改变且ET结果中仍是全表扫描加HASH连接,即语句真实执行计划和我们当前看到的不符。
获取SQL执行的真实计划我们可以使用disql工具,设置aotutrace为trace后执行SQL,具体操作如下:
可以看出此时我们SQL的执行计划仍是使用的缓存的最开始的执行计划,缓存计划一般只在SQL缓冲区(参数CACHE_POOL_SIZE控制)不足或基表存在DDL操作时才会产生淘汰。我们可以通过TRACE事件导出缓存计划,步骤如下:
1.通过视图V$CACHEPLN查看SQL对应的CAHCE_ITEM
2.通过命令将缓存计划导出到文件,注意路径为数据库服务端路径
ALTER SESSION SET EVENTS 'IMMEDIATE TRACE NAME PLNDUMP LEVEL 140676293052528,DUMP_FILE ''/home/dmdba/dm/sqltrace.log''';
查看对应目录可以发现已有对应文件生成,且文件中可以看到SQL具体缓存计划:
可以看出如果应用在我们优化SQL前就缓存了对应计划,那就可能对我们优化后结果产生误导,所以一般我们对SQL优化后都需要对对应SQL的缓存计划进行清理,达梦提供了存储过程SP_CLEAR_PLAN_CACHE来清理执行计划,此过程如果不传入参数,则会清空所有的缓存计划,如果需要清空指定SQL的执行计划,则只需要传入对应计划的CACHE_ITEM即可,下面我们将上面缓存的SQL计划进行清理:
可以看出此时执行计划已被完成清理,我们重新执行SQL进行验证:
此时SQL执行耗时为1ms,查看et结果如下:
说明SQL已采用新的执行计划。
计划缓存在SQL优化中容易对我们产生误导,因为日常的优化我们常对SQL进行格式化后查看,然而缓存计划的查找是通过SQL的HASH值进行比对的,所以在我们遇到应用和优化后SQL执行耗时存在差异时,我们应及时的对比缓存执行计划。
文章
阅读量
获赞