• 其实一般情况下,MVCC已经可以避免多事物下的读锁操作了,但是对于一些强一致的业务来说,必须要通过加锁才能保证数据的一致性。

共享锁和独占锁(行锁)

  • 共享锁,英文名:Shared Locks,简称S锁。在事务要读取一条记录时,需要先获取该记录的S锁。
  • 独占锁,也常称排他锁,英文名:Exclusive Locks,简称X锁。在事务要改动一条记录时,需要先获取该记录的X锁。

表锁

  • 给表加S锁:如果一个事务给表加了S锁,那么:
    • 别的事务可以继续获得该表的S锁
    • 别的事务可以继续获得该表中的某些记录的S锁
    • 别的事务不可以继续获得该表的X锁
    • 别的事务不可以继续获得该表中的某些记录的X锁
  • 给表加X锁:如果一个事务给表加了X锁(意味着该事务要独占这个表),那么:

    • 别的事务不可以继续获得该表的S锁
    • 别的事务不可以继续获得该表中的某些记录的S锁
    • 别的事务不可以继续获得该表的X锁
    • 别的事务不可以继续获得该表中的某些记录的X锁
  • 给表加锁时都必须判断表中是否有行已经加锁了,如果有加锁的记录,则要等待锁全部释放才能加锁。

  • 意向锁:可以用来判断表中是否还存在锁
    • 意向共享锁,英文名:Intention Shared Lock,简称IS锁。当事务准备在某条记录上加S锁时,需要先在表级别加一个IS锁。
    • 意向独占锁,英文名:Intention Exclusive Lock,简称IX锁。当事务准备在某条记录上加X锁时,需要先在表级别加一个IX锁。

InnoDB中的表级锁

  • 表级别的S锁、X锁
  • 不过请尽量避免在使用InnoDB存储引擎的表上使用LOCK TABLES这样的手动锁表语句,它们并不会提供什么额外的保护,只是会降低并发能力而已
  • 表级别的IS锁、IX锁
    • IS锁和IX锁的使命只是为了后续在加表级别的S锁和X锁时判断表中是否有已经被加锁的记录
  • 表级别的AUTO-INC锁:主键ID自增就是依赖这个保证线程安全

InnoDB中的行级锁

  • Record Locks:记录锁
  • Gap Locks:解决幻读,在对应记录加上Gap锁,只能保证在该记录之前不能插入数据,所以还需要在页的最大记录上加上Gap锁
  • Next-Key Locks:next-key锁的本质就是一个Record 锁和一个gap锁的合体,它既能保护该条记录,又能阻止别的事务将新记录插入被保护记录前边的间隙
  • Insert Intention Locks:规定事务在等待的时候也需要在内存中生成一个锁结构,表明有事务想在某个间隙中插入新记录
  • 隐式锁

锁结构

  • 锁所在的事务信息
  • 索引信息:对于行锁来说,需要记录一下加锁的记录是属于哪个索引的。
  • 表锁/行锁信息:表锁结构和行锁结构在这个位置的内容是不同的:
    • 表锁:记载着这是对哪个表加的锁,还有其他的一些信息。
    • 行锁:记载了三个重要的信息:
      • Space ID:记录所在表空间。
      • Page Number:记录所在页号。
      • n_bits:对于行锁来说,一条记录就对应着一个比特位,一个页面中包含很多记录,用不同的比特位来区分到底是哪一条记录加了锁。
  • type_mode:被分成了lock_mode、lock_type和rec_lock_type三个部分
    • 锁的模式(lock_mode):
      • LOCK_IS(十进制的0):表示共享意向锁,也就是IS锁。
      • LOCK_IX(十进制的1):表示独占意向锁,也就是IX锁。
      • LOCK_S(十进制的2):表示共享锁,也就是S锁。
      • LOCK_X(十进制的3):表示独占锁,也就是X锁。
      • LOCK_AUTO_INC(十进制的4):表示AUTO-INC锁。
    • 锁的类型(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(十进制的0):表示next-key锁。
      • LOCK_GAP(十进制的512):也就是当第10个比特位置为1时,表示gap锁。
      • LOCK_REC_NOT_GAP(十进制的1024):也就是当第11个比特位置为1时,表示正经记录锁。
      • LOCK_INSERT_INTENTION(十进制的2048):也就是当第12个比特位置为1时,表示插入意向锁。
      • 其他的类型:还有一些不常用的类型我们就不多说了。