hello,我是大都督周瑜,关注我(公众号:IT周瑜),学不一样的IT技术。
相信大家都知道,行锁可以分为X锁(排他锁)和S锁(共享锁),在源码中是这么定义的:
/* Basic lock modes */
enum lock_mode {
LOCK_IS = 0, /* intention shared */
LOCK_IX, /* intention exclusive */
LOCK_S, /* shared */
LOCK_X, /* exclusive */
LOCK_AUTO_INC, /* locks the auto-inc counter of a table in an exclusive mode */
LOCK_NONE, /* this is used elsewhere to note consistent read */
LOCK_NUM = LOCK_NONE, /* number of lock modes */
LOCK_NONE_UNSET = 255
};
LOCK_S表示S锁,值为2,
LOCK_X表示X锁,值为3。
同时,行锁又可以分为临键锁(next-key)、间隙锁、普通行锁,在源码中是这么定义的:
/* Precise modes */
#define LOCK_ORDINARY 0
#define LOCK_GAP 512
#define LOCK_REC_NOT_GAP 1024
LOCK_ORDINARY表示临键锁,既锁记录,又锁记录前的间隙
LOCK_GAP表示间隙锁,不锁记录,只锁记录前的间隙
LOCK_REC_NOT_GAP表示普通行锁,只锁记录,不锁记录前的间隙
源码中是通过组合来生成mode值的,进而表示行锁类型的,比如以下SQL:
update t1 set a = 'IT周瑜' where id = 3;
id为主键,此时就会对id=1的记录加锁,锁mode为1027
从上图可以看出,当前锁的就是t1表的主键索引中heap_no=4的记录(也就是id=3的记录,点赞加关注,后续分享什么是heap_no),mode为1027。
为什么是1027呢?
因为,1027=3+1024,3表示LOCK_X,1024表示LOCK_REC_NOT_GAP,因此1027表示加的是普通行锁的排他锁。
换成以下SQL:
select * from t1 where id = 3 lock in share mode;
mode为1026,1026=2+1024,2表示LOCK_S,1204表示LOCK_REC_NOT_GAP,因此1026表示加的是普通行锁的共享锁。
再换成以下SQL:
select * from t1 where a = 3 for update;
a字段是一个普通索引,加锁情况为
mode为3,3=3+0,3表示LOCK_X,0表示LOCK_ORDINARY,因此3表示加的是临键锁的排他锁。
同时还会给heap_no=5的记录加锁,加锁情况为
mode为515,515=3+512,3表示LOCK_X,512表示LOCK_GAP,因此515表示加的是间隙锁的排他锁。
看到没,一个mode表示了各种不同情况下的行锁,如果是我们来实现MySQL,那么可能会选择用两个属性来实现,一个属性表示S锁或X锁,另外一个属性表示临键锁、间隙锁、普通行锁。
那具体mode是如何做到的呢?
注意看上面的图,mode的类型为ulint
,也就是unsigned long int
,本质上就是一个数字,根据不同操作系统会占用不同的字节,4个字节或8个字节。
我们拿515来举例,3对应的二进制为0000 0000 0000 0011
,512对应的二进制为0000 0010 0000 0000
,两者进行“|”操作,也就是源码中的
就可以得到最终的mode为:0000 0010 0000 0011
,也就是十进制515。
所以本质上就是利用不同的bit位来表示不同的信息,在MySQL中挺多地方都是这么设计的,因为这样更节省空间。
额外说一句,空间对MySQL来说是异常宝贵的,作为一个数据库,一定要做到用最少的空间来保存最多的数据,同时还要保证CRUD的性能,说实话,这是一门编程艺术,我最近在研究MySQL的源码实现,有志同道合的朋友可以联系我(公众号:IT周瑜),相互学习。
好啦,今天的内容就分享到这,希望能得到大家的点赞、关注、分享,谢谢!