怎样用SQL给SQL2880特定表加锁解锁

怎样用SQL给SQL2880特定表加锁解锁,第1张

加锁的语句如下:

SELECT * FROM 表名 WITH (TABLOCK)这里没有解锁的概念,只有不加锁的概念,语句如下:

SELECT * FROM 表名 WITH (NOLOCK)加锁的解释:

TABLOCK(表锁)

此选项被选中时,SQL Server 将在整个表上置

共享锁

直至该命令结束。 这个选项保证其他进程只能读取而不能修改数据。

不加锁的解释:

NOLOCK(不加锁)

此选项被选中时,SQL Server 在读取或修改数据时不加任何锁。 在这种情况下,用户有可能读取到

加锁情况与死锁原因分析

为方便大家复现,完整表结构和数据如下:

CREATE TABLE `t3` (

`c1` int(11) NOT NULL AUTO_INCREMENT,

`c2` int(11) DEFAULT NULL,

PRIMARY KEY (`c1`),

UNIQUE KEY `c2` (`c2`)

) ENGINE=InnoDB

insert into t3 values(1,1),(15,15),(20,20)

在 session1 执行 commit 的瞬间,我们会看到 session2、session3 的其中一个报死锁。这个死锁是这样产生的:

1. session1 执行 delete  会在唯一索引 c2 的 c2 = 15 这一记录上加 X lock(也就是在MySQL 内部观测到的:X Lock but not gap);

2. session2 和 session3 在执行 insert 的时候,由于唯一约束检测发生唯一冲突,会加 S Next-Key Lock,即对 (1,15] 这个区间加锁包括间隙,并且被 seesion1 的 X Lock 阻塞,进入等待;

3. session1 在执行 commit 后,会释放 X Lock,session2 和 session3 都获得 S Next-Key Lock;

4. session2 和 session3 继续执行插入 *** 作,这个时候 INSERT INTENTION LOCK(插入意向锁)出现了,并且由于插入意向锁会被 gap 锁阻塞,所以 session2 和 session3 互相等待,造成死锁。

死锁日志如下:

请点击输入图片描述

INSERT INTENTION LOCK

在之前的死锁分析第四点,如果不分析插入意向锁,也是会造成死锁的,因为插入最终还是要对记录加 X Lock 的,session2 和 session3 还是会互相阻塞互相等待。

但是插入意向锁是客观存在的,我们可以在官方手册中查到,不可忽略:

Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap.

插入意向锁其实是一种特殊的 gap lock,但是它不会阻塞其他锁。假设存在值为 4 和 7 的索引记录,尝试插入值 5 和 6 的两个事务在获取插入行上的排它锁之前使用插入意向锁锁定间隙,即在(4,7)上加 gap lock,但是这两个事务不会互相冲突等待。

当插入一条记录时,会去检查当前插入位置的下一条记录上是否存在锁对象,如果下一条记录上存在锁对象,就需要判断该锁对象是否锁住了 gap。如果 gap 被锁住了,则插入意向锁与之冲突,进入等待状态(插入意向锁之间并不互斥)。总结一下这把锁的属性:

1. 它不会阻塞其他任何锁;

2. 它本身仅会被 gap lock 阻塞。

在学习 MySQL 过程中,一般只有在它被阻塞的时候才能观察到,所以这也是它常常被忽略的原因吧...

GAP LOCK

在此例中,另外一个重要的点就是 gap lock,通常情况下我们说到 gap lock 都只会联想到 REPEATABLE-READ 隔离级别利用其解决幻读。但实际上在 READ-COMMITTED 隔离级别,也会存在 gap lock ,只发生在:唯一约束检查到有唯一冲突的时候,会加 S Next-key Lock,即对记录以及与和上一条记录之间的间隙加共享锁。

通过下面这个例子就能验证:

请点击输入图片描述

这里 session1 插入数据遇到唯一冲突,虽然报错,但是对 (15,20] 加的 S Next-Key Lock 并不会马上释放,所以 session2 被阻塞。另外一种情况就是本文开始的例子,当 session2 插入遇到唯一冲突但是因为被 X Lock 阻塞,并不会立刻报错 “Duplicate key”,但是依然要等待获取 S Next-Key Lock 。

有个困惑很久的疑问:出现唯一冲突需要加 S Next-Key Lock 是事实,但是加锁的意义是什么?还是说是通过 S Next-Key Lock 来实现的唯一约束检查,但是这样意味着在插入没有遇到唯一冲突的时候,这个锁会立刻释放,这不符合二阶段锁原则。这点希望能与大家一起讨论得到好的解释。

如果是在 REPEATABLE-READ,除以上所说的唯一约束冲突外,gap lock 的存在是这样的:

普通索引(非唯一索引)的S/X Lock,都带 gap 属性,会锁住记录以及前1条记录到后1条记录的左闭右开区间,比如有[4,6,8]记录,delete 6,则会锁住[4,8)整个区间。

对于 gap lock,相信 DBA 们的心情是一样一样的,所以我的建议是:

1. 在绝大部分的业务场景下,都可以把 MySQL 的隔离界别设置为 READ-COMMITTED;

2. 在业务方便控制字段值唯一的情况下,尽量减少表中唯一索引的数量。

锁冲突矩阵

前面我们说的 GAP LOCK 其实是锁的属性,另外我们知道 InnoDB 常规锁模式有:S 和 X,即共享锁和排他锁。锁模式和锁属性是可以随意组合的,组合之后的冲突矩阵如下,这对我们分析死锁很有帮助:

请点击输入图片描述

在DB2的命令行中输入:

update monitor switches using lock on table on

然后打开另一个DB2命令窗口执行我的那个被吊死的Update语句。

然后在第一个DB2命令窗口执行: [@more@]get snapshot for locks on Database_Name(你的数据库的名字)>locks.TXT

然后,可以看到第一个DB2的窗口有一个信息输出,把这些信息输出到TXT中,大致如下:

应用程序句柄 = 36

应用程序标识 = AC100C47.IC05.00F6C6095828

序号 = 0246

应用程序名 = java.exe

CONNECT 授权标识 = DB2ADMIN

应用程序状态 = UOW 正在等待

状态更改时间 = 未收集

应用程序代码页 = 1208

挂起的锁定 = 0

总计等待时间(毫秒) = 0

应用程序句柄 = 43

应用程序标识 = *LOCAL.DB2.060512054331

序号 = 2273

应用程序名 = java.exe

CONNECT 授权标识 = DB2ADMIN

应用程序状态 = 联合请求暂挂

状态更改时间 = 未收集

应用程序代码页 = 1208

挂起的锁定 = 6

总计等待时间(毫秒) = 0

锁定列表

锁定名称 = 0x031F9052000000000000000055

锁定属性 = 0x00000000

发行版标志 = 0x40000000

锁定计数 = 255

挂起计数 = 0

锁定对象名 = 0

对象类型 = 内部

方式 = S

锁定名称 = 0x26800000000000000000000044

锁定属性 = 0x00000000

发行版标志 = 0x40000000

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 0

对象类型 = 内部

方式 = S

锁定名称 = 0x020006000F1700000000000052

锁定属性 = 0x00000000

发行版标志 = 0x00000001

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 5903

对象类型 = 行

表空间名 = USERSPACE1

表模式 = DB2ADMIN

表名 = C_USER

方式 = NS

锁定名称 = 0x01000000010000000500BC0056

锁定属性 = 0x00000000

发行版标志 = 0x40000000

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 0

对象类型 = 内部变化锁定

方式 = S

锁定名称 = 0x535953534E333030FD965C0641

锁定属性 = 0x00000000

发行版标志 = 0x40000000

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 0

对象类型 = 内部方案锁定

方式 = S

锁定名称 = 0x02000600000000000000000054

锁定属性 = 0x00000000

发行版标志 = 0x00000001

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 6

对象类型 = 表

表空间名 = USERSPACE1

表模式 = DB2ADMIN

表名 = C_USER

方式 = IS

应用程序句柄 = 557

应用程序标识 = *LOCAL.DB2.060512053913

序号 = 1254

应用程序名 = java.exe

CONNECT 授权标识 = DB2ADMIN

应用程序状态 = 联合请求暂挂

状态更改时间 = 未收集

应用程序代码页 = 1208

挂起的锁定 = 6

总计等待时间(毫秒) = 0

锁定列表

锁定名称 = 0x031F9052000000000000000055

锁定属性 = 0x00000000

发行版标志 = 0x40000000

锁定计数 = 255

挂起计数 = 0

锁定对象名 = 0

对象类型 = 内部

方式 = S

锁定名称 = 0x26800000000000000000000044

锁定属性 = 0x00000000

发行版标志 = 0x40000000

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 0

对象类型 = 内部

方式 = S

锁定名称 = 0x02000600071D00000000000052

锁定属性 = 0x00000000

发行版标志 = 0x00000001

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 7431

对象类型 = 行

表空间名 = USERSPACE1

表模式 = DB2ADMIN

表名 = C_USER

方式 = NS

锁定名称 = 0x01000000010000000500BC0056

锁定属性 = 0x00000000

发行版标志 = 0x40000000

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 0

对象类型 = 内部变化锁定

方式 = S

锁定名称 = 0x535953534E333030FD965C0641

锁定属性 = 0x00000000

发行版标志 = 0x40000000

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 0

对象类型 = 内部方案锁定

方式 = S

锁定名称 = 0x02000600000000000000000054

锁定属性 = 0x00000000

发行版标志 = 0x00000001

锁定计数 = 1

挂起计数 = 0

锁定对象名 = 6

对象类型 = 表

表空间名 = USERSPACE1

表模式 = DB2ADMIN

表名 = C_USER

方式 = IS

其中应用程序句柄43和557的状态都是死锁了,猜测是这2个应用争用DB2的表,造成死锁,根据日志提示,在DB2的命令窗口输入:

force application (43)

force application (557)

提示这个 *** 作是异步的,我执行list applicaions,结果进程中还有那2个进程,那2个进程可能是在执行比较大的 *** 作,需要耐心等待,如何还不行,则使用下面的命令来强制所有的应用都停止,然后重启DB2:

force application all

terminate

db2stop force

db2start

如果DB2在Window上,则可以使用“控制中心”->实例->右键“应用程序”,可以看到当前的锁定情况,并且可以强行关闭某个进程,也可以显示“锁定链”。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/bake/11608805.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-17
下一篇 2023-05-17

发表评论

登录后才能评论

评论列表(0条)

保存