事务中记录锁的范围

隔离级别 RC
测试数据

CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a` varchar(4) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `b` varchar(4) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `c` varchar(4) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_a` (`a`),
  KEY `idx_b` (`b`)
) ENGINE=InnoDB;

insert into test (id,a,b,c) values (null,'1','1','1');
insert into test (id,a,b,c) values (null,'2','2','2');
insert into test (id,a,b,c) values (null,'3','3','3');
insert into test (id,a,b,c) values (null,'2-1','2','2-1');

一.未使用索引的当前读

事务A:

begin;
select * from test
where c = '2'
    for update;
commit;

事务B:

begin;
select * from test
where c = '3'
    for update;
commit;

这时事务B第二行需要等待事务A commit后才能执行。所以这里是锁了全表吗?不,再看下事务C:

begin;
select * from test
where id = 3
    for update;
commit;

这里事务C并不需要等待事务A提交。所以,这里的锁是应该锁在聚集索引上。也就是聚集索引上id为2的这条记录,事务3走的聚集索引查找(type=ref),所以不需要去获取聚集索引上id为2的这条记录的锁。而事务2走的是全表扫描(type=all),则需要去依次获取聚集索引各行的记录锁。
事务A的加锁过程
1.先依次在聚集索引各行的记录上加记录锁。
2.根据where后面的条件过滤后,将未在范围内的记录锁释放。范围内的记录锁不释放直到事务提交

二.使用的非聚集索引的当前读

事务A:

begin;
select * from test
force index (idx_b)
where b = '2'
and c = '2-1'
for update;
commit;

事务B:

begin;
select * from test
force index (idx_b)
where b = '2'
and c = '2'
for update;
commit;

这里用了强制索引force index,强制指定使用b列上的索引,避免这里数据太少mysql会优化成全表扫描。

事务B第二行也会等待事务A commit后才能继续执行。
两个事务的加锁过程都为:

1.先在非聚集索引索引b中,值为2的记录加记录锁。
2.然后会去聚集索引将相应的记录上记录锁
3.最后,会根据where后面其他条件,将未在范围内的聚集索引的记录锁释放,但是非聚集索引上的记录锁不释放,范围内的记录锁不释放,直到事务提交。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值