searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Mysql Innodb 行锁的算法

2023-11-30 02:10:11
19
0

一、锁的3种算法

InnoDB引擎有3种行锁算法,分别是:

​ • Record Lock : 单个记录上锁;
​ • Gap Lock : 间隙锁,锁定一个范围,但不包含记录本身;
​ • Next-key Lock : Gap Lock+Record Lock,锁定一个范围并包含记录本身。
Record Lock总是会锁定索引记录,如果建表是没有设置索引,将使用隐式的主键来锁定。InnoDB对行的查询都是采用Next-key Lock算法。如果一个索引有10,11,13,20的值,那么可能锁定的区间为
(-无穷大,10],(10,11],(11,13],(13,20],(20,+无穷大)
使用Next-key Lock是为了解决幻读(Phantom Read)的问题。若事务T1已经锁定了(10,11],(11,13],如果插入记录12时,则锁定的范围会变为(10,11],(11,12],(12,13],当查询的索引有唯一属性时,会对Next-key Lock算法优化,降级为Record Lock,即锁住索引本身,而不是范围。

示例:
创建以下表:
CREATE TABLE t5(a INT PRIMARY KEY);
INSERT INTO t5 SELECT 1;
INSERT INTO t5 SELECT 2;
INSERT INTO t5 SELECT 5;
然后在会话A中执行以下SQL:
BEGIN;
SELECT * FROM t5 WHERE a=5 FOR UPDATE;
在会话B中执行以下SQL
BEGIN;
INSERT INTO t5 SELECT 4;
COMMIT;
可以看到顺利地插入了,因为a中主键唯一索引,锁的只是a=5这条记录;最后把会话A commit;
再看看辅助索引的情况:
执行以下SQL:
CREATE TABLE t6(a INT,b INT ,PRIMARY KEY(a),KEY(b));
INSERT INTO t6 SELECT 1,1;
INSERT INTO t6 SELECT 3,1;
INSERT INTO t6 SELECT 5,3;
INSERT INTO t6 SELECT 7,6;
INSERT INTO t6 SELECT 10,8;
在会话A中执行以下SQL:
BEGIN;
SELECT * FROM t6 WHERE a=3 FOR UPDATE;
此时有两个索引,需要分别锁定,对于聚集索引,用Record Lock锁定了列a=5的索引,对于辅助索引,加的是Next-key Lock,锁定的是范围(1,3),注意的是InnoDB还会对辅助索引的下一个键值加Gap Lock,即范围(3,6)也会被锁定,因此以下的SQL将会阻塞:
SELECT * FROM t6 WHERE a=5 LOCK IN SHARE MODE;
INSERT INTO t6 SELECT 4,2;
INSERT INTO t6 SELECT 6,5;
因为For Update加的是X锁;执行以下SQL是正常的插入,没有阻塞:
SELECT INTO t6 SELECT 8,6;
SELECT INTO t6 SELECT 2,0;
SELECT INTO t6 SELECT 6,7;

Gap Lock的作用是为防止并发时多个事务将记录插入到同一范围内,这样会导致幻像问题,如会话A中已经锁定了b=3的记录,如果没有Gap Lock锁定(3,6),那么用户可以插入索引为3的记录,这样会导致会话A的用户再次执行时,会返回不同的记录,导致幻像问题。
注意的是
可以通过以下的方法关闭Gap Lock
• 把事务隔离级别设置为READ COMMITED;
• 将参数innodb_locks_unsafe_for_binlog设置为1;

二、解决Phantom Problem

Phantom Problem是指在同一个事务中,连续执行同一条SQL得到不同的结果。

对于SELECT * FROM t WHERE a > 2 FOR UPDATE的SQL,锁住的不仅是5这个值,而是对(2,+无穷大)范围值因此对这个范围内的插入都是不允许的,从而避免了Phantom Problem。

另外,用户可以利用Next-Key Lock的机制在应用层实现唯一性的检查。

SELECT * FROM table WHERE col_name=xxx LOCK IN SHARE MODE;
If not found any row:
#unique for insert value
INSERT INTO table VALUES(….);
如果用户通过索引查询一个值,并加了S锁,即使查询的值不存在,锁定的也是一个范围,因此若没有任何返回行,那么新插入的值一定是唯一的。

 

0条评论
作者已关闭评论
chuoo
13文章数
0粉丝数
chuoo
13 文章 | 0 粉丝
原创

Mysql Innodb 行锁的算法

2023-11-30 02:10:11
19
0

一、锁的3种算法

InnoDB引擎有3种行锁算法,分别是:

​ • Record Lock : 单个记录上锁;
​ • Gap Lock : 间隙锁,锁定一个范围,但不包含记录本身;
​ • Next-key Lock : Gap Lock+Record Lock,锁定一个范围并包含记录本身。
Record Lock总是会锁定索引记录,如果建表是没有设置索引,将使用隐式的主键来锁定。InnoDB对行的查询都是采用Next-key Lock算法。如果一个索引有10,11,13,20的值,那么可能锁定的区间为
(-无穷大,10],(10,11],(11,13],(13,20],(20,+无穷大)
使用Next-key Lock是为了解决幻读(Phantom Read)的问题。若事务T1已经锁定了(10,11],(11,13],如果插入记录12时,则锁定的范围会变为(10,11],(11,12],(12,13],当查询的索引有唯一属性时,会对Next-key Lock算法优化,降级为Record Lock,即锁住索引本身,而不是范围。

示例:
创建以下表:
CREATE TABLE t5(a INT PRIMARY KEY);
INSERT INTO t5 SELECT 1;
INSERT INTO t5 SELECT 2;
INSERT INTO t5 SELECT 5;
然后在会话A中执行以下SQL:
BEGIN;
SELECT * FROM t5 WHERE a=5 FOR UPDATE;
在会话B中执行以下SQL
BEGIN;
INSERT INTO t5 SELECT 4;
COMMIT;
可以看到顺利地插入了,因为a中主键唯一索引,锁的只是a=5这条记录;最后把会话A commit;
再看看辅助索引的情况:
执行以下SQL:
CREATE TABLE t6(a INT,b INT ,PRIMARY KEY(a),KEY(b));
INSERT INTO t6 SELECT 1,1;
INSERT INTO t6 SELECT 3,1;
INSERT INTO t6 SELECT 5,3;
INSERT INTO t6 SELECT 7,6;
INSERT INTO t6 SELECT 10,8;
在会话A中执行以下SQL:
BEGIN;
SELECT * FROM t6 WHERE a=3 FOR UPDATE;
此时有两个索引,需要分别锁定,对于聚集索引,用Record Lock锁定了列a=5的索引,对于辅助索引,加的是Next-key Lock,锁定的是范围(1,3),注意的是InnoDB还会对辅助索引的下一个键值加Gap Lock,即范围(3,6)也会被锁定,因此以下的SQL将会阻塞:
SELECT * FROM t6 WHERE a=5 LOCK IN SHARE MODE;
INSERT INTO t6 SELECT 4,2;
INSERT INTO t6 SELECT 6,5;
因为For Update加的是X锁;执行以下SQL是正常的插入,没有阻塞:
SELECT INTO t6 SELECT 8,6;
SELECT INTO t6 SELECT 2,0;
SELECT INTO t6 SELECT 6,7;

Gap Lock的作用是为防止并发时多个事务将记录插入到同一范围内,这样会导致幻像问题,如会话A中已经锁定了b=3的记录,如果没有Gap Lock锁定(3,6),那么用户可以插入索引为3的记录,这样会导致会话A的用户再次执行时,会返回不同的记录,导致幻像问题。
注意的是
可以通过以下的方法关闭Gap Lock
• 把事务隔离级别设置为READ COMMITED;
• 将参数innodb_locks_unsafe_for_binlog设置为1;

二、解决Phantom Problem

Phantom Problem是指在同一个事务中,连续执行同一条SQL得到不同的结果。

对于SELECT * FROM t WHERE a > 2 FOR UPDATE的SQL,锁住的不仅是5这个值,而是对(2,+无穷大)范围值因此对这个范围内的插入都是不允许的,从而避免了Phantom Problem。

另外,用户可以利用Next-Key Lock的机制在应用层实现唯一性的检查。

SELECT * FROM table WHERE col_name=xxx LOCK IN SHARE MODE;
If not found any row:
#unique for insert value
INSERT INTO table VALUES(….);
如果用户通过索引查询一个值,并加了S锁,即使查询的值不存在,锁定的也是一个范围,因此若没有任何返回行,那么新插入的值一定是唯一的。

 

文章来自个人专栏
容器
13 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0