TiDB 和 MySQL 的锁一些分析比对
【是否原创】是
【首发渠道】TiDB 社区
【首发渠道链接】其他平台首发请附上对应链接
【正文】
图 1 锁分类图
一、悲观锁和乐观锁
TiDB 一开始是乐观锁,但自 TiDB3.0 版本开始,支持悲观事务,并且在 3.0.8 版本开始默认使用悲观事务,支持悲观锁,查看事务:show variables like ‘%tidb_txn_mode%’;
悲观锁的详细介绍:
在上一篇《白话悲观锁》中我们介绍了什么是悲观锁,悲观锁的使用场景,以及与 MySQL 的区别和联系。本文我们将深入底层,从开发者的角度,分享悲观锁的实现细节,希望能够让大家在熟悉悲观锁的同时,具备参与到相关优化中来的能力。 此外,TiDB 先有乐观锁后有悲观锁,两者共享了不少逻辑,本文将重点关注悲观锁独有的实现细节,与乐观锁相关的更多逻辑部分大家可以在《乐观事务》查阅。 鸟瞰悲观锁 TiDB …
作者:Shirly 如果说在 TiDB 3.0 中,悲观锁是 “千呼万唤始出来,犹抱琵琶半遮面”。那么在 TiDB 4.0 中,悲观锁在经历了市场与时光的考验后,无论是性能还是稳定性都能够 “轻拢慢撚抹复挑,初为《霓裳》后《六幺》”,欢迎大家尝鲜与反馈。本文将从使用者的角度,介绍悲观锁的使用与注意事项,主要分为以下几方面: 白话悲观锁 TiDB 悲观锁的使用和常见现象 TiDB 悲观锁与…
二、共享锁和排他锁
2.1 基本介绍
共享锁 (Share Lock),又称读锁,简称 S 锁;当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁。
排他锁(eXclusive Lock),又称写锁,简称 X 锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁。
这里的特性与 MySQL 一致,即,可以多个事务加读锁,只能一个事务加写锁,兼容性表格如下所示:
图 2 读锁和写锁兼容表
其中 ok 表示可以兼容,即,可以一个事务对表格加读锁后,另一个事务继续加读锁,no 则表示部兼容
2.2 实验验证
图 3
图 4
读锁兼容性测试
上面一幅图 3 是事务 1,下面一幅图 4 是事务 2,从上面的实验可以看出,加读锁后,可以继续加读锁,读锁相互兼容,读锁和写锁不兼容。
图 5
图 6
写锁兼容性测试
上图 5 是事务 1,下图 6 是事务 2,从上面实验可以看出,先加写锁后,后续不可以再添加读锁和写锁了。
三、表锁
TiDB 中的表锁默认是关闭的,show config where type = ‘tidb’ and name like ‘%enable-table-lock%’;
图 7
3.1 开启表锁
tiup cluster edit-config < 集群名字 >
在 tidb 中设置表锁为 true
图 8
tiup cluster reload < 集群名字 > -R tidb
图 9
3.2 表锁测试
TiDB 中设置表锁为 true 后,与 MySQL 有点不一样,MySQL 的根据有无命中索引,分别加行锁(命中索引)和表锁(未命中索引),TiDB 中不管有没有索引,均只锁定 update 的行
3.2.1 MySQL 表锁测试
person 表的主键和索引均是 ID 列
图 10
图 11
图 12
从图 11 和图 12 对比可以看出,MySQL 中当未命中索引时,会使用表锁,锁住整个表
3.2.2 TiDB 表锁测试
图 13
图 14
从图 13 和图 14 可以看出,TiDB 并没有自动加表锁,事务 2 可以对其他行照常更新,MySQL 则会加表锁。
3.2.3 手动对表加锁
图 15 TiDB 事务 1 手动对表加读锁
图 16 TiDB 事务 2,加读锁后测试
图 17 TiDB 事务 1 手动对表加写锁
图 18 TiDB 事务 2 手动加写锁后测试
从图 15,16,17,18 中,可以看出,在事务 1 中,手动对 TiDB 的表格加锁后,也是会锁住整张表,此时在其他事务中,不可以对该表增,删、改操作
TiDB 与 MySQL 的表锁的区别:
1、MySQL 表锁由 update 语句未使用索引,自动添加,TiDB 默认表锁是关闭的,需要手动开启,并且手动添加;
2、MySQL 执行更新操作时,由于未命中索引,会主动对整张表加表锁,TiDB 则是只会对那一行数据加行锁,
我理解这么做的原因是:TiDB 是分布式数据库,一个大表的数据分布在多个节点的 tikv 实例是,如果要对整张表加锁,这样对 CPU、IO 的资源都是一种损耗,同时这样加表锁,也不利于并发,是这样嘛,或者还有什么其他原因?
四、行锁
4.1 记录锁
记录锁是在行锁之上引申的锁,记录锁锁的是表中的某一条记录,MySQL 中记录锁的出现条件必须是精准命中索引并且索引是唯一索引,如主键 id,TiDB 好像不需要索引
4.1.1MySQL 记录锁测试
使用主键 ID 列做筛选条件
图 19 事务 1 中更新一条记录
图 20 事务 2 中更新数据
从图 20 中可以看出,图 19 事务 1 中更新的一行被锁住了,其余行都没有被锁住,可以照常更新。
4.1.2 TiDB 记录锁测试
TiDB 中的数据没有索引:
图 21 TiDB 事务 1 更新一行
图 22 TiDB 事务 2 更新数据
从图 22 中可以看出,TiDB 对事务 1 中的 1 行数据加了记录锁
记录锁相同和区别:
1、都会对一行数据加记录锁,
2、MySQL 加记录锁需要限制条件为索引列,TiDB 则不需要为索引列
4.2 间隙锁
间隙锁又称之为区间锁,每次锁定都是锁定一个区间,隶属行锁,MySQL 中照样需要限制条件为索引列
4.2.1 MySQL 间隙锁测试
图 23 事务 1 MySQL 间隙锁测试
图 24 事务 2 MySQL 间隙锁
从图 23 和图 24 中可以看出,MySQL 中间隙锁,是一个左开右闭的区间,并且对应区间内的范围都被锁住了,不可以执行插入操作。
4.2.2 TiDB 间隙锁测试
图 25 事务 1TiDB 间隙锁
图 26 事务 2TiDB 间隙锁
从图 25 和 26 可以看出,TiDB 中没有间隙锁
间隙锁相同和区别:
1、MySQL 中有间隙锁,并且是左开右闭区间
2、TiDB 中没有间隙锁
4.3 临键锁
临键锁即记录锁 + 间隙锁,mysql 的行锁默认就是使用的临键锁。
4.3.1 MySQL 临键锁测试
图 27 MySQL 事务 1 临键锁
图 28 MySQL 事务 2 临键锁
从图中测试可以看出,MySQL 中临键锁和间隙锁一样都是左开右闭的,并且当 id<8, 这个 8 不存在时,会自动向后锁住最近的 id=9。
4.3.2 TiDB 临键锁测试
图 29 TiDB 事务 1 临键锁测试
图 30 TiDB 事务 2 临键锁测试
从上面图中,可以发现,TiDB 并不会锁住区间,只会锁住限制条件区间中有的记录,命中的记录会被上锁,插入操作,可以正常进行。
临键锁相同和区别:
1、MySQL 临键锁会锁住限制条件中的区间和记录,并且和间隙锁一样都是左开右闭的,并且当 id<8, 这个 8 不存在时,会自动向后锁住最近的 id=9。
2、TiDB 中没有临键锁,只会锁住限制条件中的记录。
五、意向共享锁和意向排他锁
意向锁是表级锁,可以分为意向共享锁和意向排他锁。当事务要在记录上加上读锁或写锁时,要首先在表上加上意向锁。通过看表上是否有意向锁,判断表中是否有记录加锁。意向锁是数据库自动完成的,即事务 1 加锁时,数据库会自动先开始申请表的意向锁,事务 2 就可以检测这个意向锁,判断对应的记录有没有锁,从而不需要遍历整个表,提高效率。
注:整理了这些,欢迎大家看后提供意见,交流,谢谢!
版权声明: 本文为 InfoQ 作者【TiDB 社区干货传送门】的原创文章。
原文链接:【http://xie.infoq.cn/article/4cca26150a2091f2b65fe90bd】。文章转载请联系作者。
评论