TX锁(Transaction Lock)分析

2024-04-07 08:18
文章标签 分析 lock transaction tx

本文主要是介绍TX锁(Transaction Lock)分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

TX锁(Transaction Lock)分析

作者: fuyuncat

来源: www.HelloDBA.com

前两天看到现场alert日志中有一些00060(Deadlock)的告警。查了一下日志文件,发现一些奇怪的现象,比如有些锁在Insert时产生的,有些死锁是对同一个对象产生的。于是在解决这些问题的同时,仔细研究了一下TX锁,总结了产生TX锁的各种情况。

数据记录被锁

    我们知道,Oracle中事务产生的索都是行级锁。也就说,事务在对表做更新操作(UpdateDelete)时,只在针对数据块中需要更新的数据记录加锁。这种类型的锁就是我们最常见的锁。看下面的例子:

SQL> create table t_lock(a number, b varchar2(20), c char(10)) initrans 1 maxtrans 3;
 
Table created
 
SQL> insert into t_lock values(1,1,1);
 
1 row inserted
 
SQL> commit;
 
Commit complete
 

会话1:

SQL> update t_lock set b='2' where a=1;
 
1 row updated
 

会话2:

SQL> delete t_lock where a=1;
 

会话1锁住了a=1的记录,会话试图删除该记录时,被hung住。

SQL> select * from dba_waiters;
 
WAITING_SESSION HOLDING_SESSION LOCK_TYPE MODE_HELD MODE_REQUESTED LOCK_ID1 LOCK_ID2
--------------- --------------- --------- --------- -------------- -------- --------
28            20              Transaction Exclusive Exclusive    1048671  40361
    

    并且,如果将t_lock的数据块dump出来的话,也可以看到在记录行上的锁标志

tl: 19 fb: --H-FL-- lb: 0x1  cc: 3

    其中,0x1对应的是产生锁的事务在数据块itl列表中的条目号。

数据块中itl条目达到最大限制时产生的锁

 

    ITL(Interested Transaction List)直白点说就是对该数据块“有兴趣”的事务列。也就是会对该数据块进行修改的事务。一个表的数据块上同时最多可以有多少个事务对其进行更新,是由创建表时的参数maxtrans指定的。但是,如果表建立在基于9i新特性ASSM(自动段空间管理)的表空间上的话,那么指定的maxtrans就无效,一律都是255。比如上面的例子中,我们指定了maxtrans3(表空间不是ASSM),所以同时最多只能有3个事务对一个数据块进行更新。看下面的例子。

    先给上面的表多插入一些数据:

SQL> insert into t_lock values(2,2,2);
 
1 row inserted
 
SQL> insert into t_lock values(3,3,3);
 
1 row inserted
 
SQL> insert into t_lock values(4,4,4);
 
1 row inserted
 
SQL> insert into t_lock values(5,5,5);
 
1 row inserted
 
SQL> commit;
 
Commit complete
    

会话1:

SQL> update t_lock set b='2' where a=1;
 
1 row updated
    

会话2:

SQL> update t_lock set b='2' where a=2;
 
1 row updated
    

会话3:

SQL> update t_lock set b='2' where a=3;
 
1 row updated
    

会话4:

SQL> update t_lock set b='2' where a=4;
 
1 row updated
    

前面已经占用了3itl slot,第4个事务再申请itl时被hung住了。

SQL> select * from dba_waiters;
 
WAITING_SESSION HOLDING_SESSION LOCK_TYPE MODE_HELD MODE_REQUESTED LOCK_ID1 LOCK_ID2
--------------- --------------- --------- --------- -------------- -------- --------
19            27              Transaction Exclusive Exclusive    1048671  40361
 

    Dump出数据块,可以看到itl已经达到最大限制条目数:

seg/obj: 0x87c0  csc: 0x00.a337dee9  itc: 3  flg: -  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01
 
Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0008.053.00009ef3  0x00802e6a.71b4.2b  ----    1  fsc 0x0000.00000000
0x02   0x000d.037.0000a98a  0x0080655a.ac1f.2b  ----    1  fsc 0x0000.00000000
0x03   0x000c.004.0000acb9  0x008006a6.06bc.28  ----    1  fsc 0x0000.00000000
 

    这里我们需要提出另外一个问题,如果事务操作不是updatedelete,而是进行Insert操作,会不会也会达到maxtrans的限制呢?做个试验看下吧:

会话1:

SQL> update t_lock set b='2' where a=1;
 
1 row updated
    

会话2:

SQL> update t_lock set b='2' where a=2;
 
1 row updated
    

会话3:

SQL> update t_lock set b='2' where a=3;
 
1 row updated
 

会话4:

SQL> insert into t_lock values(6,6,6);
 
1 row inserted
    

这时,尽管已经分配itl条目给三个事务,但是第四个事务在做insert插入操作时并没有被hung住。

看下数据块dump内容:

Object id on Block? Y
 seg/obj: 0x87c0  csc: 0x00.a337df1b  itc: 3  flg: O  typ: 1 - DATA
     fsl: 0  fnx: 0xa03000c ver: 0x01
 
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0003.00b.0000a3ab  0x0080397c.0b0f.2a  ----    1  fsc 0x0000.00000000
0x02   0x0000.000.00000000  0x00806a5a.as1e.2b  ----    1  fsc 0x0000.00000000
0x03   0x000e.037.0000a458  0x14401ff2.17d8.2b  ----    1  fsc 0x0000.00000000
 

我们发现,确实只有三个transaction在这个数据块上。那么还有一个事务呢?别急,把下一个数据块dump出来看看:

Object id on Block? Y
 seg/obj: 0x87c0  csc: 0x00.a337df22  itc: 1  flg: O  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01
 
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0004.038.0000a6dc  0x00806fbf.bb4e.0e  ----    1  fsc 0x0000.00000000
 

原来如此!在做insert操作时,如果发现数据块已经达到maxtrans的最大限制,就从freelist中取出下一空闲数据块进行插入操作,而不管是否达到pctused的限制了。

Table上有Index时达到maxtrans限制

以上的测试是针对table上没有index时做的测试。当在table上建了index,情况就变得复杂多了。

 

·        场景一:

SQL> drop table t_lock;
 
Table dropped
 
SQL> create table t_lock(a number, b varchar2(20), c char(10)) initrans 1 maxtrans 5;
 
Table created
 
SQL> insert into t_lock values(1,1,1);
 
1 row inserted
 
SQL> insert into t_lock values(2,2,2);
 
1 row inserted
 
SQL> insert into t_lock values(3,3,2);
 
1 row inserted
 
SQL> insert into t_lock values(4,4,2);
 
1 row inserted
 
SQL> insert into t_lock values(5,5,1);
 
1 row inserted
 
SQL> commit;
 
Commit complete
 
SQL> create index t_lock_idx on t_lock(a) maxtrans 3;
 
Index created
 

会话1:

SQL> delete from t_lock where a=1;
 
1 row deleted      
 

会话2:

SQL> delete from t_lock where a=2;
 
1 row deleted 

(注意:以上操作都会索引列)

 

会话3:

SQL> update t_lock2 set b=2 where b=2;
 
1 row updated

(注意:这个会话没有涉及索引列)

 

会话4

SQL> delete from t_lock where a=3;

 

4个会话被hung住了:

SQL> select * from dba_waiters;
 
WAITING_SESSION HOLDING_SESSION LOCK_TYPE MODE_HELD MODE_REQUESTED LOCK_ID1 LOCK_ID2
--------------- --------------- --------- --------- -------------- -------- --------
19            20              Transaction Exclusive Share         393284   42733

 

我们的table上的maxtrans5,这里并没有达到,而index上的maxtrans3,第4Transactionhung。这里似乎可以下一个结论:table上有index的话,如果达到index上的maxtrans限制,后面的事务就会被hung住。但是,请注意第3个会话,他的操作并没有涉及到索引列,那么去掉第3个会话会怎样?

 

·        场景二:

会话1:

SQL> delete from t_lock where a=1;
 
1 row deleted      

 

会话2:

SQL> delete from t_lock where a=2;
 
1 row deleted 

(注意:以上操作都会索引列)

 

会话3:

SQL> delete from t_lock where a=3;

 

第3个会话被hung住了:

SQL> select * from dba_waiters;
 
WAITING_SESSION HOLDING_SESSION LOCK_TYPE MODE_HELD MODE_REQUESTED LOCK_ID1 LOCK_ID2
--------------- --------------- --------- --------- -------------- -------- --------
27            20              Transaction Exclusive Share         1048605  40351

 

这里,第3个会话就被hung住了。那就是说对有index得情况来说,itl数的限制是minx(maxtrans_of_index1 - 1, maxtrans_of_index2 – 1..., maxtrans_of_table) 。

 

我们前面提到,如果在insert时发现数据块的itl数已经达到maxtrans的限制,那么会在一个新的数据块进行插入,以避免会话阻塞。那么有了index以后,是否还是这样呢?看下面这种情况。

 

·        场景三:

 

会话1:

SQL> delete from t_lock where a=1;
 
1 row deleted      

 

会话2:

SQL> delete from t_lock where a=2;
 
1 row deleted 

(注意:以上操作都会索引列)

 

会话3:

SQL> insert into t_lock values(6,6,6);

 

第3个会话被hung住了:

SQL> select * from dba_waiters;
 
WAITING_SESSION HOLDING_SESSION LOCK_TYPE MODE_HELD MODE_REQUESTED LOCK_ID1 LOCK_ID2
--------------- --------------- --------- --------- -------------- -------- --------
19            20              Transaction Exclusive Share        131099    40859

 

我们看到,尽管达到maxtrans限制的会话是一个insert操作,但它还是被阻塞了。并不是想没有index时那样重新分配一块block。我想,oracle这样做的目的是为了保持index块的紧凑把。否则,试想,如果index块像data 块一样分布,index scan的意义就不大了。

 

有index情况比较复杂,很难知道Oracle内部是怎么处理的。但是,尽管从逻辑上分析,被lock住的对象应该是index,但从v$locked_object视图中查到的却是table。莫非oracle在做加锁时,是把index看作是table的一部分,而不是独立的资源(事实上,将index dump出来的话,它的行锁机制应该和数据块上差不多,不同的是,在做update操作时,index块上是先delete,然后insert,而数据块上是直接update)?

Bitmap Index条目被锁

如果Index是bitmap index,那么还有一种情况能产生tx锁。

我们知道,bitmap index一般建立在distinct值数目比较少的字段上。Oracle会建立一个类似于二进制位图(所以叫bitmap index)的结构,位图上每个条目对应一个值。而每个值会对应多个rowid。类似以下:

0rowid_1, rowid_5, rowid_11, rowid_20 ...
1: rowid_2, rowid_3, rowid_7, rowid_13 ...
2: rowid_4, rowid_6, rowid_8, rowid_12 ...
...

 

而如果多个会话需要同时更新一个条目对应的不同记录中位图索引列的话,就会产生争用,导致之中的会话被阻塞。看以下例子:

SQL> create bitmap Index t_lock_bmidx on t_lock(c);
 
Index created
 

会话1:

SQL> update t_lock set c=0 where a=1;
 
1 row updated

 

会话2:

SQL> update t_lock set c=0 where a=5;

 

会话2被hung住。

SQL> select * from dba_waiters;
 
WAITING_SESSION HOLDING_SESSION LOCK_TYPE MODE_HELD MODE_REQUESTED LOCK_ID1 LOCK_ID2
--------------- --------------- --------- --------- -------------- -------- --------
27            20              Transaction Exclusive Share         196635    41890

 

请注意以上两个transaction更新的记录是a=1和a=5,它们对应的c值都是1,因此第2个会话被hung住。

 

唯一性约束条件导致的tx

唯一性约束很简单,就是要保证被约束的字段不会有多于1条的重复记录。所以,事务会加上相应的tx锁,以保证约束不被违反。而如果两个事务提交后的结果会导致约束冲突的话,那么后来的会话就会被阻塞。

 
SQL> alter table t_lock
  2    add constraint t_lock_pk primary key (a);
 
Table altered

 

会话1:

SQL> insert into t_lock values(6,6,6);
 
1 row inserted

 

会话2:

SQL> update t_lock set a=6 where a=1;

 

可以看到,第2个会话被hung住了。

SQL> select * from dba_waiters;
 
WAITING_SESSION HOLDING_SESSION LOCK_TYPE MODE_HELD MODE_REQUESTED LOCK_ID1 LOCK_ID2
--------------- --------------- --------- --------- -------------- -------- --------
27            28              Transaction Exclusive Share         196683   41866

 

其它

事实上,产生tx锁还有一些其它情况。以上我所分析的都是常见的情况以及我所遇到的情况。

Tx锁是为了控制并发进程以及数据完整性而产生的。系统中产生tx锁应该是很常见的事(如果你去生产系统中查询v$lock视图,就能看到不少tx锁)。关键的问题是我们如何保证小事务不被大事务的tx锁阻塞,以及怎样避免死锁。希望以上的分析能够帮助你定位和解决这些问题。

 

这篇关于TX锁(Transaction Lock)分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/882092

相关文章

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

Spring中Bean有关NullPointerException异常的原因分析

《Spring中Bean有关NullPointerException异常的原因分析》在Spring中使用@Autowired注解注入的bean不能在静态上下文中访问,否则会导致NullPointerE... 目录Spring中Bean有关NullPointerException异常的原因问题描述解决方案总结

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit