工作面试老大难-锁(下)

2023-02-28 14:03:22 浏览数 (1)

大家好,我是热心的大肚皮,皮哥。

InnoDB锁的内存结构

直接上图,我们看看InnoDB中锁的结构。

  • 锁所在的事务信息:任何锁都属于一个事务,这里记载对应的事务信息。
  • 索引信息:对于行级锁来说,需要记录加锁的记录属于哪个索引。
  • 表/行锁信息:记录的对应的信息。
  • type_mode:32比特的数,包含lock_mode、lock_type、rec_lock_type这三部分。
    • lock_mode(锁模式)占用低4比特。
      • LOCK_IS(十进制的0):共享意向锁。
      • LOCK_IX(十进制的1):独占意向锁。
      • LOCK_S(十进制的2):共享锁。
      • LOCK_X(十进制的3):独占锁。
      • LOCK_AUTO_INC(十进制的4):AUTO_INC锁,轻量级锁。
    • lock_type(锁类型)占用第5-8位。
      • LOCK_TABLE(十进制的16):也就是当第五比特设置为1时,表示表级锁。
      • LOCK_REC(十进制的32):也就是当第六比特设置为1时,表示行级锁。
    • rec_lock_type代表行锁的具体类型,不过多讲述了。不过这里面有个关键的属性。
      • LOCK_WAIT(10进制的256):也就是第九比特设置为1时,表示is_waiting为true,即当前事务尚未获取到锁,等待中,反之is_waiting为false,即事务获取锁成功。
  • 其他信息:不用关注。
  • 比特位:映射到页中的记录。

InnoDB行锁的分类

  • Record Lock:记录锁。只对记录本身加锁。
  • Gap Lock:锁住记录前的间隙,防止别的事物向该间隙插入新纪录。在repeatable read隔离级别下可以很大程度解决幻读,解决方案有两种:一种是MVCC,另一种就是使用这种锁。
  • Next-Key Lock:Record Lock与Gap Lock的结合体,既保护记录本身,也防止别的事务插入记录。
  • Insert Intention Lock:某个事务获取这种锁后,不会阻止别的事务继续获取这个记录上任何类型的锁,这个可以忽略。
  • 隐式锁:依靠记录的trx_id属性来保护不被别的事务改动记录。

在InnoDB存储引擎中,锁都对应一个结构,为了节约内存,会把符合要求的锁放到同一个锁结构中:

  • 在同一个事物中进行加锁操作;
  • 被加锁的记录在同一个页面中;
  • 加锁的类型都是一样的;
  • 等待状态是一样的。

死锁

假设我们开启了两个事务T1和T2,如下。

执行顺序

T1

T2

1

begin;

2

begin;

3

select * from hero where id=1 for update;

4

select * from hero where id=3 for update;

5

select * from hero where id=3 for update;

6

select * from hero where id=1 for update;

通过上面的执行顺序可以发现,事务T1第5步执行时,在等待事务T2中的第4步,而事务T2中第6步在等待事务T1中的第3步,它们在互相等待,造成死锁,InnoDB中有个死锁检测机制,当检测到死锁时,会选择一个较小的事务进行回滚(指事务执行过程中增删改操作记录少的事务),而且会给客户端发送Deadlock found when trying to get lock。发生了死锁,我们一般会采用SHOW ENGINE INNODB STATUS命令来查看最近一次的死锁信息。

具体应用场景

读操作

  • 这种普通的读操作在READ COMMITTED 和 REPEATABLE READ(默认的)级别下,都是利用MVCC来控制,只是拍快照的时机不一样。
代码语言:javascript复制
SELECT ....FROM .. 
  • 特殊的select会加锁
代码语言:javascript复制
SELECT ... LOCK IN SHARE MODE;  -- 加读锁
SELECT ... FOR UPDATE;  --加写锁

写操作

  • 只有INSERT特殊,一般不加锁(但是别的事务有干扰的情况下,别的事务会来帮执行INSERT的这个事务的行记录加锁)
  • 其他普遍都得加锁,比如UPDATE(一般是先删除后插入),DELETE
    • 有索引:
      • 在READ COMMITTED :普通的写锁
      • 在REPEATABLE READ
        • 除非是唯一索引且等值的条件:加普通的写锁
        • 其他普遍情况加的都是next-key锁(普通写锁 间隙锁)
    • 无索引:全表都加锁

0 人点赞