介绍一下MySQL锁相关的内容。

共享锁和独占锁(Shared and Exclusive Locks)

  1. InnoDB 通过共享锁和独占锁两种方式实现了标准的行锁。
共享锁(S 锁):允许事务获得锁后去读数据。
独占锁(X 锁):允许事务获得锁后去更新或删除数据。
  1. 一个事务获取共享锁 S 后,允许其他事务获取 S 锁,此时两个事务都持有共享锁 S,但是不允许其他事务获取 X 锁。如果一个事务获取的独占锁(X),则不允许其他事务获取 S 或者 X 锁,必须等到该事务释放锁后才可以获取到。

意向锁(Intention Locks)

  1. InnoDB 支持行锁和表锁。意向锁是一种表级锁,用来指示接下来的一个事务将要获取的是什么类型的锁(共享还是独占)。
  2. 意向锁分为意向共享锁(IS)和意向独占锁(IX),依次表示接下来一个事务将会获得共享锁或者独占锁。
  3. 意向锁不需要显示的获取,而是在获取共享锁或者独占锁的时候会自动的获取。
  4. 意向锁不会锁住任何东西,除非有进行全表请求的操作,否则不会锁住任何数据。
  5. 意向锁存在的意义只是用来表示有事务正在锁某一行的数据,或者将要锁某一行的数据。

记录锁(record Locks)

  1. 锁住某一行,如果表存在索引,那么记录锁是锁在索引上的,如果表没有索引,那么 InnoDB 会创建一个隐藏的聚簇索引加锁。
  2. 所以在进行查询的时候尽量采用索引进行查询,这样可以降低锁的冲突。

间隙锁(Gap Locks)

  1. 间隙锁是一种记录行与记录行之间存在空隙或在第一行记录之前或最后一行记录之后产生的锁。
  2. 间隙锁可能占据的单行,多行或者是空记录。
  3. 通常的情况是我们采用范围查找的时候,比如在学生成绩管理系统中,如果此时有学生成绩 60,72,80,95,一个老师要查下成绩大于 72 的所有同学的信息,采用的语句是 select * from student where grade > 72 for update,这个时候 InnoDB 锁住的不仅是 80,95,而是所有在 72-80,80-95,以及 95 以上的所有记录。为什么会这样呢?实际上是因为如果不锁住这些行,那么如果另一个事务在此时插入了一条分数大于 72 的记录,那会导致第一次的事务两次查询的结果不一样,出现了幻读。所以为了在满足事务隔离级别的情况下需要锁住所有满足条件的行。

Next-Key Locks

  1. NK 是一种记录锁和间隙锁的组合锁。
  2. NK 既锁住行也锁住间隙。
  3. NK 采用的左开右闭的原则。
  4. InnoDB 对于查询都是采用这种锁的。

MyISAM 和 InnoDB

  1. MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待;而在InnoDB中,锁是逐步获得的,这就造成了死锁的可能。
  2. InnoDB引擎既支持行锁也支持表锁,那么什么时候会锁住整张表,什么时候或只锁住一行呢?只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
  3. 由于 MySQL 的行锁是针对索引加的锁,不是针对记录加的锁,所以即使是访问不同行的记录,如果使用相同的索引键,仍然是会出现锁冲突的。
  4. 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行。另外,不论是使用主键索引、唯一索引或普通索引,InnoDB 都会使用行锁来对数据加锁。
  5. 即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同执行计划的代价来决定的。如果 MySQL 认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引。这种情况下InnoDB 将使用表锁,而不是行锁,因此,在分析锁冲突时,,别忘了检查 SQL 的执行计划,以确认是否真正使用了索引。
  6. 在MySQL中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。 在UPDATE、DELETE操作时,MySQL不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking。
  7. 当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引,另一个锁定了非主键索引,在等待主键索引,这样就会发生死锁。
  8. 发生死锁后,InnoDB一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务。