为什么忘记 commit 也会造成 select 查询的性能问题
2016-09-14 09:50
309 查看
今天遇到一个很有意思的问题,一个开发人员反馈在测试服务器ORACLE数据库执行的一条简单SQL语句非常缓慢,他写的一个SQL没有返回任何数据,但是耗费了几分钟的时间。让我检查分析一下原因,分析解决过后,发现事情的真相有点让人哭笑不得,但是也是非常有意思的。我们先简单构造一下类似的案例,当然只是简单模拟。
假设一个同事A,创建了一个表并初始化了数据(实际环境数据量较大,有1G多的数据),但是他忘记提交了。我们简单模拟如下:
另外一个同事B对这个表做一些简单查询操作,但是他不知道同事A的没有提交INSERT语句,如下所示,查询时间用了大概5秒多(这个因为构造的数据量不是非常大的缘故。实际场景耗费了几分钟)
当时是在SQLDeveloper工具里面分析SQL的执行计划,并没有注意到redosize非常大的情况。刚开始怀疑是统计信息不准确导致,手工收集了一下该表的统计信息,执行的时间和执行计划依然如此,没有任何变化。如果我们使用SQL*Plus,查看执行计划,就会看到redosize异常大,你就会有所察觉(见后面分析)
因为ORACLE里面的写不阻塞读,所以不可能是因为SQL阻塞的缘故,然后我想查看这个表到底有多少记录,结果亮瞎了我的眼睛,记录数为0,但是空间用掉了852个数据块
于是我使用Tom大师的show_space脚本检查、确认该表的空间使用情况,如下所示,该表确实使用852个数据块。
分析到这里,那么肯定是遇到了插入数据操作,却没有提交的缘故。用下面脚本检查发现一个会话ID为883的对这个表有一个ROW级排他锁,而且会话还有一个事务排他锁,那么可以肯定这个会话执行了DML操作,但是没有提交。
我们在会话里面提交后,然后重新执行这个SQL,你会发现执行计划里面redosize为0,这是因为redosize表示DML生成的redolog的大小,其实从上面的执行计划分析redosize异常,就应该了解到一个七七八八了,因为一个正常的SELECT查询是不会在redolog里面生成相关信息的。那么肯定是遇到了DML操作,但是没有提交。
分析到这里,我们已经知道事情的前因后果了,解决也很容易,找到那个会话的信息,然后定位到哪个同事,让其提交即可解决。但是,为什么没有提交与提交过后的差距那么大呢?是什么原因呢?我们可以在这个案例,提交前与提交后跟踪执行的SQL语句,如下所示。
假设一个同事A,创建了一个表并初始化了数据(实际环境数据量较大,有1G多的数据),但是他忘记提交了。我们简单模拟如下:
SQL>createtabletest_uncommit
2as
3select*fromdba_objectswhere1=0;
Tablecreated.
SQL>declarerowIndexnumber;
2begin
3forrowIndexin1..70loop
4insertintotest_uncommit
5select*fromdba_objects;
6endloop;
7end;
8/
PL/SQLproceduresuccessfullycompleted.
SQL>
另外一个同事B对这个表做一些简单查询操作,但是他不知道同事A的没有提交INSERT语句,如下所示,查询时间用了大概5秒多(这个因为构造的数据量不是非常大的缘故。实际场景耗费了几分钟)
SQL>SETTIMINGON;
SQL>SETAUTOTRACEON;
SQL>SELECTCOUNT(1)FROMSYS.TEST_UNCOMMITWHEREOBJECT_ID=39;
COUNT(1)
----------
0
Elapsed:00:00:05.38
ExecutionPlan
----------------------------------------------------------
Planhashvalue:970680813
------------------------------------------------------------------------------------
|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
------------------------------------------------------------------------------------
|0|SELECTSTATEMENT||1|13|6931(3)|00:00:10|
|1|SORTAGGREGATE||1|13|||
|*2|TABLEACCESSFULL|TEST_UNCOMMIT|1|13|6931(3)|00:00:10|
------------------------------------------------------------------------------------
PredicateInformation(identifiedbyoperationid):
---------------------------------------------------
2-filter("OBJECT_ID"=39)
Note
-----
-dynamicsamplingusedforthisstatement
Statistics
----------------------------------------------------------
4recursivecalls
0dbblockgets
229304consistentgets
61611physicalreads
3806792redosize
514bytessentviaSQL*Nettoclient
492bytesreceivedviaSQL*Netfromclient
2SQL*Netroundtripsto/fromclient
0sorts(memory)
0sorts(disk)
1rowsprocessed
SQL>
当时是在SQLDeveloper工具里面分析SQL的执行计划,并没有注意到redosize非常大的情况。刚开始怀疑是统计信息不准确导致,手工收集了一下该表的统计信息,执行的时间和执行计划依然如此,没有任何变化。如果我们使用SQL*Plus,查看执行计划,就会看到redosize异常大,你就会有所察觉(见后面分析)
SQL>execdbms_stats.gather_table_stats('SYS','TEST_UNCOMMIT');
PL/SQLproceduresuccessfullycompleted.
Elapsed:00:00:12.29
因为ORACLE里面的写不阻塞读,所以不可能是因为SQL阻塞的缘故,然后我想查看这个表到底有多少记录,结果亮瞎了我的眼睛,记录数为0,但是空间用掉了852个数据块
SQL>SELECTTABLE_NAME,NUM_ROWS,BLOCKSFROMDBA_TABLESWHERETABLE_NAME='TEST_UNCOMMIT';
TABLE_NAMENUM_ROWSBLOCKS
--------------------------------------------------
TEST_UNCOMMIT0852
SQL>
于是我使用Tom大师的show_space脚本检查、确认该表的空间使用情况,如下所示,该表确实使用852个数据块。
SQL>setserverouton;
SQL>execshow_space('TEST_UNCOMMIT');
FreeBlocks.............................852
TotalBlocks............................896
TotalBytes.............................7,340,032
TotalMBytes............................7
UnusedBlocks...........................43
UnusedBytes............................352,256
LastUsedExtFileId....................1
LastUsedExtBlockId...................88,201
LastUsedBlock.........................85
PL/SQLproceduresuccessfullycompleted.
SQL>
分析到这里,那么肯定是遇到了插入数据操作,却没有提交的缘故。用下面脚本检查发现一个会话ID为883的对这个表有一个ROW级排他锁,而且会话还有一个事务排他锁,那么可以肯定这个会话执行了DML操作,但是没有提交。
SETlinesize190
COLosuserformata15
COLusernameformata20wrap
COLobject_nameformata20wrap
COLterminalformata25wrap
COLreq_modeformata20
SELECTB.SID,
C.USERNAME,
C.OSUSER,
C.TERMINAL,
DECODE(B.ID2,0,A.OBJECT_NAME,
'TRANS-'
||TO_CHAR(B.ID1))OBJECT_NAME,
B.TYPE,
DECODE(B.LMODE,0,'WAITING',
1,'NULL',
2,'Row-S(SS)',
3,'ROW-X(SX)',
4,'SHARE',
5,'S/ROW-X(SSX)',
6,'EXCLUSIVE',
'OTHER')"LOCKMODE",
DECODE(B.REQUEST,0,'',
1,'NULL',
2,'Row-S(SS)',
3,'ROW-X(SX)',
4,'SHARE',
5,'S/ROW-X(SSX)',
6,'EXCLUSIVE',
'OTHER')"REQ_MODE"
FROMDBA_OBJECTSA,
V$LOCKB,
V$SESSIONC
WHEREA.OBJECT_ID(+)=B.ID1
ANDB.SID=C.SID
ANDC.USERNAMEISNOTNULL
ORDERBYB.SID,
B.ID2;
我们在会话里面提交后,然后重新执行这个SQL,你会发现执行计划里面redosize为0,这是因为redosize表示DML生成的redolog的大小,其实从上面的执行计划分析redosize异常,就应该了解到一个七七八八了,因为一个正常的SELECT查询是不会在redolog里面生成相关信息的。那么肯定是遇到了DML操作,但是没有提交。
分析到这里,我们已经知道事情的前因后果了,解决也很容易,找到那个会话的信息,然后定位到哪个同事,让其提交即可解决。但是,为什么没有提交与提交过后的差距那么大呢?是什么原因呢?我们可以在这个案例,提交前与提交后跟踪执行的SQL语句,如下所示。
相关文章推荐
- 为什么忘记 commit 也会造成 select 查询的性能问题
- 为什么忘记commit也会造成select查询的性能问题
- 忘记commit造成select查询的性能问题
- 为什么忘记commit也会造成select性能问题
- 用hibernate的性能:插入很快,可查询为什么非常慢
- 小小问题集锦5之---为什么在没有 ORDER BY 子句的情况下无法保证 SELECT 语句所返回结果的顺序
- linux之忘记密码及修改fstab文件造成不能登录问题
- 什么叫n+1次select查询问题? 什么叫n+1次select查询问题?
- SQL Server 2005中Query(查询)Date Time(日期时间)时select不出record的问题
- 代码里直接返回对象造成的可读性、扩展性以及性能问题
- 【小小问题集锦5之---为什么在没有 ORDER BY 子句的情况下无法保证 SELECT 语句所返回结果的顺序。】
- 老生常谈:关于分页查询和性能问题
- 解决有缺陷的布局造成ListView性能大大降低的问题
- MySQL子查询的性能问题
- Dynamic Parameters造成的linked server性能问题
- latch free:cache buffer handles造成的SQL性能问题
- sql查询语句性能问题及编写时需要注意的地方
- n+1次select查询问题
- 在使用Hibernate时,因为一个查询需要更多的表连接而要使用SQL来解决性能问题。然而返回的结果集中包含了没有映射的Entity类中的表字段,在这个SQL中还有使用如何将层次关系的父子结点显示为横行
- 使用Oracle 10g中的等待界面诊断性能问题(查询会话等待,查询慢的现象)