Mysql锁实战

mysql版本:8.0.32
通过实战验证mysql的Record lock 与 Gap lock原理

锁类型

Shared and Exclusive Locks

类似读写锁,S锁为读锁,X锁为写锁

Intention Locks

意向锁,例如:

  1. SELECT … FOR SHARE sets an IS lock,
  2. and SELECT … FOR UPDATE sets an IX lock.
    为什么有意向锁?
    假设事务A锁定1行数据,事务B需要表扫描,那么加表锁需要判断每行是否存在锁?意向锁可以避免全扫描

Record Locks

行锁日志案例:lock_mode X locks rec but not gap

RECORD LOCKS space id 15 page no 477 n bits 128 index PRIMARY of table `gallant`.`my_test_user` trx id 18793 lock_mode X locks rec but not gap
Record lock, heap no 55 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 00164233; asc   B3;;
 1: len 6; hex 000000004969; asc     Ii;;
 2: len 7; hex 820000009d0110; asc        ;;
 3: len 5; hex 616c696365; asc alice;;
 4: len 4; hex 80000013; asc     ;;

Gap Locks

gap锁日志案例:lock_mode X locks gap before rec

Next-Key Locks

日志案例:lock_mode X,注意next-key lock是前开后闭集合
例如:索引包含值: 10, 11, 13, and 20. next-key lock的范围如下

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

官方日志案例

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10080 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;

Insert Intention Locks

日志案例:lock_mode X locks gap before rec insert intention waiting

RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000066; asc    f;;
 1: len 6; hex 000000002215; asc     " ;;
 2: len 7; hex 9000000172011c; asc     r  ;;...

准备工作

设置隔离级别为:RR,以及innodb状态输出锁相关信息

show variables like '%innodb_status_output_locks%';
show variables like '%isolation%';

SET GLOBAL innodb_status_output_locks=ON;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ

建立测试表

CREATE TABLE `my_test_user` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `user_name` varchar(45) DEFAULT NULL,
  `user_age` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_user_age` (`user_age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

关闭自动提交

set autocommit=OFF;

实战

数据库数据分布

# user_agecount(0)
121
131
141
151
161
171
181
191
401
411

insert-存在唯一索引

session 1:

INSERT INTO `gallant`.`my_test_user`
(`id`,
`user_name`,
`user_age`)
values
(1,'jack1','12'),
(2,'jack2','13'),
(3,'jack3','14'),
(4,'jack4','15'),
(5,'jack5','16'),
(6,'jack6','17'),
(7,'jack7','18'),
(8,'jack8','19'),
(9,'jack9','40'),
(10,'jack10','41')
;

查看锁信息

show engine innodb status\G

没有写入冲突,加:意向排他锁:IX

---TRANSACTION 18795, ACTIVE 3 sec
1 lock struct(s), heap size 1128, 0 row lock(s), undo log entries 1
MySQL thread id 10, OS thread handle 6253998080, query id 229 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 18795 lock mode IX
---TRANSACTION 18794, ACTIVE 11 sec
1 lock struct(s), heap size 1128, 0 row lock(s), undo log entries 1
MySQL thread id 9, OS thread handle 6252883968, query id 228 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 18794 lock mode IX

开启第2个session,同时写入一个id数据,IX锁额外增加排他锁X,行锁,并且session2将会因为IX锁等待超时:TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED
session 2:

INSERT INTO `gallant`.`my_test_user`
(`id`,
`user_name`,
`user_age`)
values
(1,'jack1','12');

锁日志

---TRANSACTION 246806, ACTIVE 3 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1128, 1 row lock(s)
MySQL thread id 371, OS thread handle 123145543245824, query id 5984255 localhost root update
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (1,'jack1','12')
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 438 page no 4 n bits 80 index PRIMARY of table `gallant`.`my_test_user` trx id 246806 lock mode S locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 00000001; asc     ;;
 1: len 6; hex 00000003c40d; asc       ;;
 2: len 7; hex 82000000e50110; asc        ;;
 3: len 5; hex 6a61636b31; asc jack1;;
 4: len 4; hex 8000000c; asc     ;;

------------------
TABLE LOCK table `gallant`.`my_test_user` trx id 246806 lock mode IX
RECORD LOCKS space id 438 page no 4 n bits 80 index PRIMARY of table `gallant`.`my_test_user` trx id 246806 lock mode S locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 00000001; asc     ;;
 1: len 6; hex 00000003c40d; asc       ;;
 2: len 7; hex 82000000e50110; asc        ;;
 3: len 5; hex 6a61636b31; asc jack1;;
 4: len 4; hex 8000000c; asc     ;;

---TRANSACTION 246797, ACTIVE 66 sec
2 lock struct(s), heap size 1128, 1 row lock(s), undo log entries 10
MySQL thread id 370, OS thread handle 123145545375744, query id 5984253 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 246797 lock mode IX
RECORD LOCKS space id 438 page no 4 n bits 80 index PRIMARY of table `gallant`.`my_test_user` trx id 246797 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 00000001; asc     ;;
 1: len 6; hex 00000003c40d; asc       ;;
 2: len 7; hex 82000000e50110; asc        ;;
 3: len 5; hex 6a61636b31; asc jack1;;
 4: len 4; hex 8000000c; asc     ;;

insert-不存在唯一索引

加:意向排他锁,无锁冲突,可以并行写入
session 1:

INSERT INTO `my_test_user`
(`user_name`,`user_age`)
VALUES
('alice','40');

session 2:

INSERT INTO `my_test_user`
(`user_name`,`user_age`)
VALUES
('alice_1','40');

锁日志

---TRANSACTION 18797, ACTIVE 3 sec
1 lock struct(s), heap size 1128, 0 row lock(s), undo log entries 1
MySQL thread id 10, OS thread handle 6253998080, query id 235 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 18797 lock mode IX
---TRANSACTION 18796, ACTIVE 11 sec
1 lock struct(s), heap size 1128, 0 row lock(s), undo log entries 1
MySQL thread id 9, OS thread handle 6252883968, query id 234 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 18796 lock mode IX

update-根据唯一索引

where条件数据不存在

session1:

update `my_test_user` set user_age=41 where id = 1867767;

锁日志

---TRANSACTION 18798, ACTIVE 3 sec
2 lock struct(s), heap size 1128, 1 row lock(s)
MySQL thread id 9, OS thread handle 6252883968, query id 241 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 18798 lock mode IX
RECORD LOCKS space id 15 page no 477 n bits 128 index PRIMARY of table `gallant`.`my_test_user` trx id 18798 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

锁定范围
[最大主键,+∞)。与非主键场景完全一致,详情看后面的场景案例

where条件数据存在

session1:

update `my_test_user` set user_age=41 where id = 1;

锁日志

---TRANSACTION 246810, ACTIVE 4 sec
2 lock struct(s), heap size 1128, 1 row lock(s), undo log entries 1
MySQL thread id 370, OS thread handle 123145545375744, query id 5984278 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 246810 lock mode IX
RECORD LOCKS space id 438 page no 4 n bits 80 index PRIMARY of table `gallant`.`my_test_user` trx id 246810 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 00000001; asc     ;;
 1: len 6; hex 00000003c41a; asc       ;;
 2: len 7; hex 01000002d31b13; asc        ;;
 3: len 5; hex 6a61636b31; asc jack1;;
 4: len 4; hex 80000029; asc    );;

锁定范围
record lock:id=1->hex 00000001; asc ;;

update-根据非唯一索引

user_age=11

session1:

update `my_test_user` set user_age=41 where user_age=11;

锁日志:

---TRANSACTION 246875, ACTIVE 3 sec
2 lock struct(s), heap size 1128, 1 row lock(s)
MySQL thread id 370, OS thread handle 123145545375744, query id 5984433 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 246875 lock mode IX
RECORD LOCKS space id 438 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 246875 lock_mode X locks gap before rec
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000c; asc     ;;
 1: len 4; hex 00000001; asc     ;;

锁定范围
gap lock:(negative infinity,12)->hex 8000000c; asc ;;
验证
session2:

INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (116,'jack111','10'); -- 失败
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (116,'jack111','11'); -- 失败
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (116,'jack111','12'); -- 成功
user_age=12

session1:

update `my_test_user` set user_age=41 where user_age=12;

锁日志:

---TRANSACTION 246873, ACTIVE 5 sec
4 lock struct(s), heap size 1128, 3 row lock(s), undo log entries 1
MySQL thread id 370, OS thread handle 123145545375744, query id 5984430 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 246873 lock mode IX
RECORD LOCKS space id 438 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 246873 lock_mode X
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 8000000c; asc     ;;
 1: len 4; hex 00000001; asc     ;;

RECORD LOCKS space id 438 page no 4 n bits 80 index PRIMARY of table `gallant`.`my_test_user` trx id 246873 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 00000001; asc     ;;
 1: len 6; hex 00000003c459; asc      Y;;
 2: len 7; hex 01000001421f94; asc     B  ;;
 3: len 5; hex 6a61636b31; asc jack1;;
 4: len 4; hex 80000029; asc    );;

RECORD LOCKS space id 438 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 246873 lock_mode X locks gap before rec
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000e; asc     ;;
 1: len 4; hex 00000003; asc     ;;

锁定范围
gap lock:(13,14)->hex 8000000e; asc ;;
record lock:=12(ID=1)->hex 00000001; asc ;;
next-key-lock:(-∞, 12]->hex 8000000c; asc ;;
综上所述:(-∞,14)
验证
session2:

INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (116,'jack111','11'); -- 失败
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (116,'jack111','12'); -- 失败
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (116,'jack111','13'); -- 失败
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (116,'jack111','14'); -- 成功
user_age=13

session1:

update `my_test_user` set user_age=41 where user_age=13;

锁日志:

---TRANSACTION 246980, ACTIVE 358 sec
4 lock struct(s), heap size 1128, 3 row lock(s), undo log entries 1
MySQL thread id 370, OS thread handle 123145545375744, query id 5984667 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 246980 lock mode IX
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 246980 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 8000000d; asc     ;;
 1: len 4; hex 00000002; asc     ;;

RECORD LOCKS space id 439 page no 4 n bits 80 index PRIMARY of table `gallant`.`my_test_user` trx id 246980 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 00000002; asc     ;;
 1: len 6; hex 00000003c4c4; asc       ;;
 2: len 7; hex 020000140621e9; asc      ! ;;
 3: len 5; hex 6a61636b32; asc jack2;;
 4: len 4; hex 80000029; asc    );;

RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 246980 lock_mode X locks gap before rec
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000e; asc     ;;
 1: len 4; hex 00000003; asc     ;;

锁定范围
gap lock:(13,14)->hex 8000000e; asc ;;
record lock:=13(ID=2)->hex 00000002; asc ;;
next-key-lock:(12, 13]->hex 8000000c; asc ;;
综上所述:(12,14)
验证
session2:

INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (113,'jack111','11'); -- 成功
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (114,'jack111','12'); -- 失败 原因:session2的插入意向锁IX与session1的next-key lock(X)冲突(user_age=13),日志如下
------- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 246992 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 8000000d; asc     ;;
 1: len 4; hex 00000002; asc     ;;

下面的日志案例不应该是行锁冲突吗?

INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (115,'jack111','13');  -- 失败 原因:session2的插入意向锁IX与session1的gap lock(X)冲突(user_age=13),日志如下
------- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 246992 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000e; asc     ;;
 1: len 4; hex 00000003; asc     ;;
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (116,'jack111','14'); -- 成功
user_age=40

session1:

update `my_test_user` set user_age=18 where user_age=40;

锁日志:

---TRANSACTION 246994, ACTIVE 2 sec
4 lock struct(s), heap size 1128, 3 row lock(s), undo log entries 1
MySQL thread id 370, OS thread handle 123145545375744, query id 5984701 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 246994 lock mode IX
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 246994 lock_mode X
Record lock, heap no 10 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 80000028; asc    (;;
 1: len 4; hex 00000009; asc     ;;

RECORD LOCKS space id 439 page no 4 n bits 80 index PRIMARY of table `gallant`.`my_test_user` trx id 246994 lock_mode X locks rec but not gap
Record lock, heap no 10 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 00000009; asc     ;;
 1: len 6; hex 00000003c4d2; asc       ;;
 2: len 7; hex 02000000a704d9; asc        ;;
 3: len 5; hex 6a61636b39; asc jack9;;
 4: len 4; hex 80000012; asc     ;;

RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 246994 lock_mode X locks gap before rec
Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 80000029; asc    );;
 1: len 4; hex 0000000a; asc     ;;

锁定范围
gap lock:(40,41)->hex 80000029; asc );;
record lock:=40(ID=9)->hex 00000009; asc ;;
next-key-lock:(39, 40]->hex 80000028; asc (;;
综上所述:(39,41)
验证
session2:
"("符号表示左侧也是闭区间吗?包含39?

INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (215,'jack111','39'); --失败 原因:session2的插入意向锁IX与session1的next-key lock冲突(user_age=39)
------- TRX HAS BEEN WAITING 4 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247001 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 10 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 80000028; asc    (;;
 1: len 4; hex 00000009; asc     ;;
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (216,'jack111','40'); --失败 原因:session2的插入意向锁IX与session1的gap lock冲突(user_age=40)
------- TRX HAS BEEN WAITING 7 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247001 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 80000029; asc    );;
 1: len 4; hex 0000000a; asc     ;;
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (216,'jack111','41'); -- 成功
user_age=41

session1:

update `my_test_user` set user_age=18 where user_age=41;

锁日志:

---TRANSACTION 247011, ACTIVE 5 sec
3 lock struct(s), heap size 1128, 3 row lock(s), undo log entries 1
MySQL thread id 370, OS thread handle 123145545375744, query id 5984741 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 247011 lock mode IX
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247011 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 80000029; asc    );;
 1: len 4; hex 0000000a; asc     ;;

RECORD LOCKS space id 439 page no 4 n bits 80 index PRIMARY of table `gallant`.`my_test_user` trx id 247011 lock_mode X locks rec but not gap
Record lock, heap no 11 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 0000000a; asc     ;;
 1: len 6; hex 00000003c4e3; asc       ;;
 2: len 7; hex 02000000d10dc2; asc        ;;
 3: len 6; hex 6a61636b3130; asc jack10;;
 4: len 4; hex 80000012; asc     ;;

锁定范围
next-key lock:(40,41]->hex 80000029; asc );;,[41,+∞)->hex 73757072656d756d; asc supremum;;
record lock:id=10->hex 0000000a; asc ;;
综上所述:(40,+∞)
验证
session2:

INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (215,'jack111','39'); -- 成功

左闭?[40,41]

INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (216,'jack111','40'); -- 失败 原因:session2的插入意向锁IX与session1的next-key lock冲突(user_age=40)
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247012 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 11 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 80000029; asc    );;
 1: len 4; hex 0000000a; asc     ;;
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (217,'jack111','41'); -- 失败 原因:session2的插入意向锁IX与session1的next-key lock冲突(user_age=41)
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247017 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (219,'jack111','66'); -- 失败 原因 同user_age=41更新时的冲突
------- TRX HAS BEEN WAITING 6 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247018 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;
user_age=66

session1:

update `my_test_user` set user_age=41 where user_age=66;

锁日志:

---TRANSACTION 247020, ACTIVE 3 sec
2 lock struct(s), heap size 1128, 1 row lock(s)
MySQL thread id 370, OS thread handle 123145545375744, query id 5984761 localhost root
TABLE LOCK table `gallant`.`my_test_user` trx id 247020 lock mode IX
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247020 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

锁定范围
next-key lock:[41,+∞)->hex 73757072656d756d; asc supremum;;
验证
session2:

INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (216,'jack111','40'); -- 成功
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (217,'jack111','41'); -- 失败 原因:session2的插入意向锁IX与session1的next-key lock冲突(user_age=41)
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247022 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (219,'jack111','66'); -- 失败 原因:session2的插入意向锁IX与session1的next-key lock冲突(user_age=66)
------- TRX HAS BEEN WAITING 21 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247023 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;
INSERT INTO `gallant`.`my_test_user` (`id`, `user_name`, `user_age`) values (215,'jack111','77'); -- 失败 原因:session2的插入意向锁IX与session1的next-key lock冲突(user_age=77)
------- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 439 page no 5 n bits 80 index idx_user_age of table `gallant`.`my_test_user` trx id 247024 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

幻读

RR隔离级别下会有幻读吗?
会:MVCC快照读,未加锁,其他事务写入/更新新数据,再次使用当前读(for update)。均会出现幻读

场景1

session 1session 2
select * from my_test_user where user_age>41;(无数据返回)
INSERT INTO gallant.my_test_user (id, user_name, user_age) values (14,‘jack10’,‘42’);
commit(执行成功)
select * from my_test_user where user_age>41 for update;(返回数据)

场景2

session 1session 2
select * from my_test_user where user_age>41;(无数据返回)
update my_test_user set user_age=42 where user_age=41;
commit(执行成功)
select * from my_test_user where user_age>41 for update;(返回数据)

总结

  1. 基于唯一索引更新,行锁
  2. 基于非唯一索引更新,next-key-lock,行锁+gap锁,锁范围前后均为闭区间,以user_age索引字段为例:
    1. between 18 and 40, 锁范围:[18,40]
    2. 大于> 19,锁范围:(19,+∞)
    3. <13,锁范围:(-∞,13)
    4. =40,锁范围:[19,40],锁的是距离user_age=40最近的一条数据至40的范围
    5. =44,数据不存在,加意向排他锁IX,此时如果要并发更新数据库中存在的数据会锁超时
  3. 基于唯一索引插入,IX锁,可以并行写入,只有索引冲突时会失败
  4. 基于非唯一索引插入,IX锁,可以并行写入,无冲突
  5. MVCC或者next-key-lock均可以解决幻读,但是RR隔离级别下,某些场景可以脱离MVCC或者next-key-lock,因此RR隔离级别下可能会出现幻读
  6. 很多博客强调幻读是针对insert场景,其实不然,幻读场景2中就是update产生的幻读,幻读是指在同一个事务相同条件下的两次读的数据结果不一致
  7. 普通索引加record锁,每次都需要两把锁,普通索引一把锁(可以看作为读写锁中的读锁),primary key一把锁(可以看作为读写锁中的写锁),普通索引的锁可以重入不排斥,写锁不可以
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值