TiDB和MySQL的锁一些分析比对

【是否原创】是
【首发渠道】TiDB 社区
【首发渠道链接】其他平台首发请附上对应链接
【正文】


图1 锁分类图

一、悲观锁和乐观锁
TiDB一开始是乐观锁,但自TiDB3.0版本开始,支持悲观事务,并且在3.0.8版本开始默认使用悲观事务,支持悲观锁,查看事务:show variables like ‘%tidb_txn_mode%’;
image
悲观锁的详细介绍:


二、共享锁和排他锁
2.1 基本介绍
共享锁(Share Lock),又称读锁,简称S锁;当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁。
排他锁(eXclusive Lock),又称写锁,简称X锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁。
这里的特性与MySQL一致,即,可以多个事务加读锁,只能一个事务加写锁,兼容性表格如下所示:


图2 读锁和写锁兼容表
其中ok表示可以兼容,即,可以一个事务对表格加读锁后,另一个事务继续加读锁,no则表示部兼容
2.2 实验验证
image
图3

图4
读锁兼容性测试
上面一幅图3是事务1,下面一幅图4是事务2,从上面的实验可以看出,加读锁后,可以继续加读锁,读锁相互兼容,读锁和写锁不兼容。
image
图5

图6
写锁兼容性测试
上图5是事务1,下图6是事务2,从上面实验可以看出,先加写锁后,后续不可以再添加读锁和写锁了。

三、表锁
TiDB中的表锁默认是关闭的,show config where type = ‘tidb’ and name like ‘%enable-table-lock%’;
image
图7
3.1 开启表锁
tiup cluster edit-config <集群名字>
在tidb中设置表锁为true
image
图8
tiup cluster reload <集群名字> -R tidb
image
图9
3.2 表锁测试
TiDB中设置表锁为true后,与MySQL有点不一样,MySQL的根据有无命中索引,分别加行锁(命中索引)和表锁(未命中索引),TiDB中不管有没有索引,均只锁定update的行
3.2.1 MySQL表锁测试
person表的主键和索引均是ID列


图10
image
图11
image
图12
从图11和图12对比可以看出,MySQL中当未命中索引时,会使用表锁,锁住整个表
3.2.2 TiDB表锁测试
image

图13

图14
从图13和图14可以看出,TiDB并没有自动加表锁,事务2可以对其他行照常更新,MySQL则会加表锁。

3.2.3 手动对表加锁
image
图15 TiDB事务1手动对表加读锁


图16 TiDB事务2,加读锁后测试
image
图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列做筛选条件
image
图19 事务1中更新一条记录
image
图20 事务2中更新数据
从图20中可以看出,图19事务1中更新的一行被锁住了,其余行都没有被锁住,可以照常更新。
4.1.2 TiDB记录锁测试
TiDB中的数据没有索引:
image
image
图21 TiDB事务1更新一行
image
图22 TiDB事务2更新数据
从图22中可以看出,TiDB对事务1中的1行数据加了记录锁

记录锁相同和区别:
1、都会对一行数据加记录锁,
2、MySQL加记录锁需要限制条件为索引列,TiDB则不需要为索引列

4.2 间隙锁
间隙锁又称之为区间锁,每次锁定都是锁定一个区间,隶属行锁,MySQL中照样需要限制条件为索引列
4.2.1 MySQL间隙锁测试
image
图23 事务1 MySQL间隙锁测试
image
图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临键锁测试
image
图27 MySQL事务1临键锁
image
图28 MySQL事务2临键锁
从图中测试可以看出,MySQL中临键锁和间隙锁一样都是左开右闭的,并且当id<8,这个8不存在时,会自动向后锁住最近的id=9。
4.3.2 TiDB临键锁测试
image
图29 TiDB事务1临键锁测试
image
图30 TiDB事务2临键锁测试
从上面图中,可以发现,TiDB并不会锁住区间,只会锁住限制条件区间中有的记录,命中的记录会被上锁,插入操作,可以正常进行。

临键锁相同和区别:
1、MySQL临键锁会锁住限制条件中的区间和记录,并且和间隙锁一样都是左开右闭的,并且当id<8,这个8不存在时,会自动向后锁住最近的id=9。
2、TiDB中没有临键锁,只会锁住限制条件中的记录。

五、意向共享锁和意向排他锁
意向锁是表级锁,可以分为意向共享锁和意向排他锁。当事务要在记录上加上读锁或写锁时,要首先在表上加上意向锁。通过看表上是否有意向锁,判断表中是否有记录加锁。意向锁是数据库自动完成的,即事务1加锁时,数据库会自动先开始申请表的意向锁,事务2就可以检测这个意向锁,判断对应的记录有没有锁,从而不需要遍历整个表,提高效率。

注:整理了这些,欢迎大家看后提供意见,交流,谢谢!

2赞