上线前的压力测试,是系统上线过程中重要一环 ;
一轮好的压力测试,不仅能发现系统配置方面的不足,更重要的是能发现应用设计上的缺陷; 压测时发现问题之时,事实上也是我们结合应用逻辑提出有效解决方案的大好机会; 这个时候的改动,代价更小、调整更方便,而且还能直接看到压力环境下的测试效果;也就能验证你提出的解决方案是否有效了。
今天笔者要跟大家分享的问题,是 一个压测过程中与"enq: TX - allocate ITL entry"等待事件有关的CASE ; 这类等待事件出现的正是与应用处理逻辑紧密相关;在处理过程中,还需要结合数据库的原理来分析;
Oracle 数据库问题,还是找不到原因?
不妨找中亦科技试试,我们将尽最大努力为您找到导致故障和性能问题的根本原因。
问题来了
01
某客户,压力测试正在进行中,在高压环境下,系统的性能表现并不好; 在数据库中,我们可以看到出现了各种在高压环境下会出现的等待事件:
客户对此提出,"enq:TX - allocate ITL entry"等待事件是否可以通过什么方式消除,目前分析看来,正是这个等待事件导致整个测试的压力上不去。
切记草率处理
02
科普一下
1
)什么时候会出现等待事件
enq: TX - allocate ITL entry
呢?
在数据库进行
DML
操作修改一个数据块时,通常会在数据块的头部占用一个事务槽,存放该
DML
操作的相关事务信息,如果
DML
操作无法获取某个块上的事务槽,就会出现这个等待事件
;
2
)
对于该等待事件常见的解决方案是什么呢
?
Ø
在数据块上保留足够空间留给事务槽
n
保留足够的
pctfree
n
初始化足够大的
inittrans
Ø
降低同一个块上出现过多事务的可能性
n
加快事务的完成,避免出现长时间未提交的事务
n
减少数据块上的数据量,避免在同一个块上出现过多事务
n
结合业务,离散同一数据块上的数据,避免在同一个块上出现过多事务
关注源头
1) 是哪个语句哪个对象上产生了这个等待事件
出现了异常等待事件,我们需要定位是导致等待事件的直接原因 ;
可以清楚的看到,导致问题的主要语句是 g52nu1qdyvq46 ,等待涉及的对象是 74140 ;
SQL
内容如下
:
等待的对象:
看起来,等待涉及的对象是一个索引;
2) 索引的列值是什么
从业务逻辑看, MSGSNO 是一个业务的流水号,是一串单向增长的数据,而第二列 DLFADM 列则是一个机构代码,目前业务情况下基本就是同一值;
查询数据得到的结果类似如下 :
对于这样的数据,如果高并发的事务出现,则极有可能出现流水号接近,生成的索引数据集中到某一个数据块中,导致单个块上的事务槽被占满的情况 ;
3) 对象的大小有多大
再查看索引对象的大小,为 856MB
常规方案来解决----第一次头脑风暴
面对这种在索引块上的事务热点竞争,结合我们上面说的原理,其实会有很多解决方案;
针对这一具体案例,我们可以考虑下面的几种方案
:
1.
在确认该索引不会被范围扫描时使用,将索引改造为
hash
分区索引, 避免热点数据;
2.
在确认该索引不会被范围扫描时使用,将索引改造为反向索引,避免热点数据;
3.
保留更大的事务槽空间
;
对于这一 CASE ,项目组试着多管齐下,来解决该问题:
再次测试效果不佳
—
我们遗漏了什么
重新测试,我们发现,问题并没有得到缓解,按照原理来说,上面做的看起来已经足够多了,但是为什么解决不了问题呢?看起来,似乎这里头还存在一些特殊之处,我们需要重新捋一遍,才能找到问题的根本原因。
别着急,多思考个三五分钟,问题原因就在后面,什么时候往下翻,由你决定…
根因分析过程
03
从头来过
我们再从头来查出现等待事件的详细信息:
出现问题的依旧还是同样的 SQL ,只不过,这一次我们分析到该等待事件都集中到了该索引的同一个数据块上(注意:这里索引被重建,所以 current_obj# 发生了变化);
按理说,业务数据在持续的插入,那么热点的 MSGSNO 是在不断变化的,经过我们 hash/reserve 之后,热点的数据块应该也是在不断变化的;可是为什么到了这里,热点块为什么始终落在一个数据块上呢?难道业务这么巧?还是其他的什么原因?
别着急,多思考个三五分钟,问题原因就在后面,什么时候往下翻,由你决定…
看看他的特殊之处
很多同学看到这,肯定第一个想到的是该块是否为与段空间管理相关的位图块 ; 我们可以很快的查询到 :
该块为索引分区 SYS_P1576 第一个 extent 中的数据块 ;
很显然,该分区只有少数几个 extent ,每个 extent 的大小也都不大; segment 的位图块通常是在第 个 extent 中,这个块存在与 extent 1 中,也就不是该索引分区的位图块 ;
数据块分析
分析一个数据块,我们可以将数据块直接 DUMP 出来; SQL> alter system dump datafile 48 block4173106;
得到的 trace 文件内容如下:
首先从内容上看,确实存放的就是索引数据的内容; 事务槽从 0x01 到 0xa9 全部用完,十六进制 A9 换算为十进制,正好 169 个 。
可以看出上面的数据块中, 1 个默认事务槽是 C 代表已经提交,剩下的 168 个事务槽都是 Active Transaction 状态;
接着看行记录,通过数据记录来分析热块的形成原因 ;
回顾我们所建立的索引,是由两个列组合而成;
DLFADM
代表法人代码,默认都是本银行,基本为
0000
。
而这里的数据,可以看到:
col0
全是
null
;
col1
为
4
个
30
,这里
30
正是“
”的
16
进制
ASCII
码;
col2
则显然存放的是对应数据的
rowid
信息
;
那么这里的疑点来了,这里 col0 ,也就是表中的 MSGSNO 为什么会全部都是 null 呢 ?
猜想与验证
按照业务逻辑,似乎这里的 MSGSNO 列不应该为 null 值,当我们 dump 出来的数据跟我们预期的数据不一致的时候,我们不妨直接到表中去查询真实数据 :
看来,表中的数据完全没有 MSGSNO 为 null 值的情况; 那么,为什么 dump 出来的都是 null 数据呢?我们不得不怀疑 :
要排除上面两个可能的 bug 看起来很难,我们就需要换个思路去想了;
但是我们要结合实际情况来分析;我们假设一开始插入进来的数据确实是 null ,那么即使经过 hash/reserve ,这些数据还是会集中存储到某一个索引块中;我们在数据表中无法查找到该列值为 null 的情况,恰恰是因为它还没有提交,而它在提交时又将该列数据修改为非空的一个序列值;
上述的假设看起来天衣无缝,不妨与开发进一步确认。
真相大白
04
经过与开发人员沟通,了解到这部分的操作确实如上所述,其业务逻辑伪代码类似如下 :
业务这样做是因为,需要在业务处理过程中经过一系列操作后,才能获取到MSGSNO的真实值,在此之前,需要先将表的数据插入到XXXVJX表中,插入时先用null值代入,后续再将MSGSNO值更新过来;
这样,业务逻辑的设计似乎是造成这个问题的根本原因;
解决方案
05
理解了问题的原因,要给出对应的解决方案也就不难了;我们同样给出解决方案,在不改变业务逻辑的前提下,我们先使用一个 sequence 生成一个非空值进行插入,这样就能避免 null 值插入时,无法通过 hash/reserve 方式将数据打散的情况 ;
通过上述调整,我们再次进行压力测试,该类 SQL 引起的 ITL 事务槽的问题就已经得到圆满解决 。 本文转载于中亦安图
