ORACLE索引分裂(enq: TX - index contention)

oracle索引分裂探索
背景:近期生产环境业务高峰期库压力突增,表现为CPU使用率升高,sql执行时间长,执行sql的会话阻塞大概100多条。等待事件是enq:TX-index contention 和 buffer busy waits

enq:TX-index contention是一个非常常见的等待事件,其专指由于索引分裂产生的竞争等待。
索引分裂:
什么是索引分裂呢?索引分裂是指:当某个索引块中的空闲空间不足以容纳新加入的记录时,寻找一个新的索引块来存放记录。一般来说insert是引起索引块分裂的主要操作。
索引分裂可以分为三种:
leaf block split(叶节点分裂)
branch block split(枝节点分裂)
root block split(根节点分裂)
按照 leaf Block Split 分裂时的行为 又可以分为:
leaf node 90-10 splits 插入到索引leaf block叶子块中的索引键是该块中最大的键值(包括块中已删除的索引键值)。 在此种情况下实施 90-10 split( 实际是 99-1 ),原叶子块仍保持99%的full, 而到另一个空的叶子块中插入该条新的最大键值记录。
leaf node 90-10 splits 行为的次数可通过v$sysstat视图 中leaf node 90-10 splits获得,AWR中也有对应记录:
在这里插入图片描述
leaf node 50-50 splits 当插入到索引叶子块中的索引键值不是该块中的最大值时(包括块中已删除的索引键值), 将发生 50/50 split分裂, 这意味着有一半索引记录仍存在当前块,而另一半数据移动到新的叶子块中。
leaf node 50-50 splits的次数也是通过AWR中获取
在这里插入图片描述
知道了造成索引分裂的主要原因是insert,那就找一下,看到底是哪些索引上产生的分裂。通过AWR/ASH/ADDM,找到了两个热点表的两个索引。分析发现,这两个索引的列,取值都是从sequence中取的,也就是列值是顺序增长的。对比前面的分析,顺序增长的列值,新插入的值肯定是最大值,自然就会产生叶子节点90-10的分裂。知道了原因,就好办了。找到对应的开发人员,咨询该列取值是否可以改为不用sequence,而是采用随机数。经过沟通,开发人员将列的取值直接用sys_guid()函数来产生,修改过后,索引分裂明显降低,修改前后高峰期数据库压力图如下:
ORACLE索引分裂(enq: TX - index contention)_第1张图片
ORACLE索引分裂(enq: TX - index contention)_第2张图片
可以看出高峰期并发争用显著降低,数据库稳定。

后续探索:
对比索引列从序列取值 和 随机取值 的效率
测试方法如下:
1、创建表1和表2,各有5个列,其中id列取值,表1用sequence,表2用dbms_random.value()
2、写存储过程1和2,分别往表1和表2插入500万数据
3、开9个窗口,同时执行存储过程1,记录对应的时间和等待事件
4、刷新shared_pool和buffer_pool
5、开9个窗口,同时执行存储过程2,记录对应的时间和等待事件
测试结果如下:
执行存储过程1和执行存储过程2时CPU使用率对比ORACLE索引分裂(enq: TX - index contention)_第3张图片
执行存储过程1和执行存储过程2时并发争用对比ORACLE索引分裂(enq: TX - index contention)_第4张图片
执行存储过程1所消耗的时间ORACLE索引分裂(enq: TX - index contention)_第5张图片
执行存储过程2所消耗的时间ORACLE索引分裂(enq: TX - index contention)_第6张图片
结论如下:
第一种方式,索引列从序列取值,递增的。 执行时间26分,EM上的并发争用2.4, CPU使用5左右。
第二种方式,索引列 随机数生成,无序的,执行时间5分钟,EM上并发争用0.8,CPU使用10左右
由此可以发现:高并发写入的系统,如果列上需要用到索引,列尽量不要从序列中取值,否则很有可能产生索引分裂等待。如果索引列必须从序列取值,可根据业务情况,在对应列建立hash索引,以避免索引分裂产生等待。

你可能感兴趣的:(ORACLE,数据库)