强人“锁”难,MySQL到底有多少锁?

前言读锁写锁意向锁,表锁行锁页面锁 。
在学习JAVA并发编程的时候,肯定少不了学习锁 。最常见的就是synchronized,锁的概念不是很好理解,有的地方说是锁住了一段代码,有的地方说是锁住了一个对象 。弄得初学者都是丈二和尚——摸不着头脑 。
抛开这些结论性的说法,说一下我对锁的理解(不管是Java中的锁还是数据库中的锁,还是分布式锁) 。当我们需要限制某段程序在同一时刻,最多能被1个线程同时执行的时候就需要锁 。这个幸运的线程怎么选出来呢?那就让他们去抢一个许可证吧,重点是需要保证这个许可证是唯一的,一次最多只能被一个线程抢到 。许可证不要拘泥于是this,还有可能是数据库设置了唯一键的列、缓存中唯一的key或者一个文件目录 。只有抢到了这个许可证的线程,才可以执行这段代码,执行完成或者异常退出后自动释放许可证 。
理解了这点,再说锁住的是一段代码、一个对象,甚至是一个线程都无所谓了,因为锁的作用就是在某一段时间内将一段代码、一个对象(许可证)、一个线程绑定在一起 。
MySQL中的锁与存储引擎有关,MyISAM只支持表级锁,InnoDB既支持表级锁,又支持行级锁 。

强人“锁”难,MySQL到底有多少锁?

文章插图
 
InnoDB中的锁InnoDB实现了行级锁,可以分为两种类型:共享(S)锁定和排他(X)锁定 。
  • 共享(S)锁允许持有该锁的事务读取一行,所有又叫读锁
  • 排他(X)锁允许持有该锁的事务更新或删除行,所以又叫写锁 。
多个事务并发执行时,如果事务T1在某一行r上持有共享(S)锁,那么事务T2的对这行r的锁请求将按以下方式处理:
  • T2对S锁的请求可以立即获得批准 。T1和T2都在r上保持了S锁 。
  • T2对X锁的请求不能获取批准 。
如果事务T1在某一行r上拥有排他(X)锁,则事务T2不能获取锁(不论是S锁还是X锁)
换言之,S锁和S锁是兼容的,S锁和X锁是冲突的,X锁和X锁是冲突的
强人“锁”难,MySQL到底有多少锁?

文章插图
 
但是共享锁和排它锁并不是指具体的两种锁,而是指两类锁 。
同样的乐观锁和悲观锁也不是指具体的锁,而是指两类锁 。乐观锁是乐观的认为每次都不会发生冲突,只会在更新的时候检查要更新的值有没有被别人修改过,因为没有加锁,所以乐观锁又叫无锁 。在数据库中一般是用MVCC实现乐观锁,在Java中用CAS实现乐观锁 。至于悲观锁就是悲观的认为每次都会发生冲突,所以每次修改都需要加锁 。
表锁表锁是MySQL中粒度最大的一种锁,简单粗暴的锁住整张表,实现简单所以支持的并发度低 。InnoDB和MyIASM都支持表锁,表锁分为共享(S)锁和排他(X)锁 。
表锁的特点是实现简单,并发度低 。加锁快,开销小 。不会出现死锁 。
行锁行锁是MySQL中粒度最细的一种锁,每次只锁住要操作的那一行 。实现复杂,支持的并发度高 。只有InnoDB支持行锁 。行锁也分为共享(S)锁和排他(X)锁 。行锁是对索引的锁定 。例如SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE,防止任何其他事务插入,更新或删除t.c1值为10的行 。行锁始终锁定索引,对于没有创建索引的表,InnoDB创建一个隐藏的聚簇索引并将该索引用于行锁 。
行锁的特点是实现复杂,并发度高 。加锁慢,开销大 。会出现死锁 。
意向锁(Intention Locks)InnoDB支持多种粒度锁定,允许行锁和表锁并存 。为了使在多个粒度级别上的锁定变得切实可行,InnoDB实现了意图锁 。意向锁是表级锁,表示事务稍后对表中的行需要上哪种类型的锁(共享锁或排他锁) 。有两种类型的意图锁:
* 意向共享锁(IS)表示事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁 。
* 意向排他锁(IX)表示事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁 。
SELECT ... LOCK IN SHARE MODE设置IS锁定,而SELECT ... FOR UPDATE设置IX锁定 。
表级锁之间的兼容性如下
强人“锁”难,MySQL到底有多少锁?

文章插图
 
有的同学一看到这么多种情况就头晕,死记硬背是不可能死记硬背的,这辈子都不会死记硬背 。既然这样,那就干脆不记,花点时间深入了解一下为什么要设计成表中这样 。
要想理解这个表,首先得理解为什么要有意向锁(Intention Locks),关于意向锁的作用,官方文档上给出了这么一句话:


推荐阅读