看应用设计的重要性--技术人生系列第四十七期-我和数据中心的故事

来源:这里教程网 时间:2026-03-03 12:28:46 作者:

上线前的压力测试,是系统上线过程中重要一环

一轮好的压力测试,不仅能发现系统配置方面的不足,更重要的是能发现应用设计上的缺陷; 压测时发现问题之时,事实上也是我们结合应用逻辑提出有效解决方案的大好机会; 这个时候的改动,代价更小、调整更方便,而且还能直接看到压力环境下的测试效果;也就能验证你提出的解决方案是否有效了。

 

今天笔者要跟大家分享的问题,是 一个压测过程中与"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 状态;

  接着看行记录,通过数据记录来分析热块的形成原因

回顾我们所建立的索引,是由两个列组合而成;

        MSGSNO 代表业务序号,单向递增。

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 事务槽的问题就已经得到圆满解决 本文转载于中亦安图

相关推荐

热文推荐