MySQL 加锁的底层真相:锁结构里都存了什么?

MySQL 加锁的底层真相:锁结构里都存了什么? 目录一.摘要二. 加锁的本质三. 锁结构详解3.1 锁所在的事务信息3.2 索引信息3.3 表锁/行锁信息3.4 type_mode3.5 其他信息3.6 一堆比特位一. 摘要在 MySQL 中有很多种锁例如行锁表锁页锁全局锁它们锁的粒度都不一样。更多情况下我们往往只是关注这些锁的区别和作用却很少关注它们本身下面我们就来简单了解一下 MySQL 中锁的内存结构锁中都包含了哪些信息二. 加锁的本质在 Java 中我们对一个代码块或一个方法进行加锁锁住的都是唯一资源是需要多个线程去进行争抢的而在MySQL中我们对一行记录去进行加锁实质上是在内存结构中创建了一个锁结构与之相关联。但这样就会出现一个问题。如果一个事务要对多条记录进行加锁难道要生成多个锁结构吗如果数据量过多锁结构也随之增多会出现大量的冗余这么做合理吗当然是不合理的。因此MySQL在对不同的记录进行加锁时会对需要加锁的记录去做判断如果符合下面这些条件就会放在同一个锁结构中。一在同一个事务中进行加锁操作二被加锁的记录在同一个页面中三加锁的类型是一样的四等待的状态是一样的可以先简单及以下这四句话下面我会进行详细说明。三. 锁结构详解MySQL 中有很多种存储引擎这里我们以最为常用的 InnoDB 存储引擎为例说明这也是因为只有InnoDB存储引擎支持行级锁。InnoDB 存储引擎的锁结构如下所示行级锁与表级锁的锁信息略有不同其余信息都是一样的。3.1 锁所在的事务信息记录操作当前数据的事务信息(这里只是一个指针通过指针我们可以获取当前事务的详细信息例如事物的id事务的隔离级别)3.2 索引信息对于行锁来说需要记录加锁的记录属于哪个索引这里也是一个指针3.3 表锁/行锁信息表锁结构和行锁结构略有不同。表锁主要记录对哪个表进行加锁行锁则记录了三个重要信息分别是表空间Space ID页号Page Numbern_bits(对于行锁来说一条记录就对应着一个比特位一个页面中包含很多记录用不同的比特位来区分到底是哪一条记录加了锁。为此在行锁结构的末尾放置了一堆比特位这个n_bits属性代表使用了多少比特位这里只是一个记录数但不存放真正的比特位真正的比特位是由最下面一堆比特位存放的)3.4 type_mode锁的模式(lock_mode)占用了低四位可选值如下LOCK_IS(十进制的0)表示共享意向锁即 IS锁LOCK_IX(十进制的1)表示独占意向锁即 IX锁LOCK_S(十进制的2)表示共享锁即 S锁LOCK_X(十进制的3)表示独占锁即 X锁LOCK_AUTO_INC(十进制的4)表示 AUTO-INC锁在InnoDB存储引擎中LOCK_ISLOCK_IXLOCK_AUTO_INC 都算是表级锁的模式LOCK_SLOCK_X 既可以算是表级锁模式也可以算作行级锁模式锁的类型(lock_type)占用了第5~8位不过现阶段只有第5位和第6位被使用LOCK_TABLE(十进制的16)也就是第5个比特位为1时表示表级锁LOCK_REC(十进制的32)也就是第6个比特位为1时表示行级锁锁的具体行为(rec_lock_type)使用其余位表示只有在lock_type 值为LOCK_REC时也就是该锁为行级锁时才会细分为更多的类型如下LOCK_ORDINARY(十进制的8)表示 next-key 锁LOCK_GAP(十进制的512)也就是当第十个比特位为1时表示GAP锁LOCK_REC_NOT_GAP(十进制的1024)即当第11个比特位为1时表示正经记录锁LOCK_INSERT_INTENTION(十进制的2048)即当第12个比特位为1时表示插入意向锁也在这个32位数字中LOCK_WAIT(十进制的256)即我们上面提到的等待状态IS_WAITING(是否处于等待状态)表示第九个比特位为1时为true当前事务还尚未获取到锁正处于等待状态为0即false时表示当前事务获取锁成功3.5 其他信息为了更好地管理系统运行过程中生成的各种锁结构而设计了各种哈希表和链表3.6 一堆比特位对应上面第三条行锁信息在行锁结构中末尾还放置了一堆比特位比特位的数量就是有上面的 n_bits 来决定的InnoDB数据页中的每条记录在记录头信息中都包含了一个heap_no 属性伪记录Infimum的heap_no值为0Supermum的heap_no值为1之后插入每条记录heap_no的值就增加1。锁结构最后的一堆比特位就对应着一个页面中的记录一个比特位映射一个heap_no即一个比特位映射到业内的一条记录。