InnoDB 的行锁和表锁

Jun 15, 2020


本文参考:这里这里

行锁

  • 优势:锁粒度小,发生锁冲突的概率低;处理并发的能力强
  • 劣势:开销大;加锁慢;会出现死锁
  • 加锁方式:自动加锁。查询操作不加任何锁;更新操作会自动给涉及的记录加排他锁。
  • 显式加锁:
    • 共享锁 select * from 【tablename】 where …… lock in share more;
    • 排他锁 select * from 【tablename】 where …… for update;
  • 行锁优化:
    • 尽可能让所有数据检索都通过索引来完成,避免无索引或索引失效导致行锁升级为表锁。
    • 尽可能避免间隙锁带来的性能下降,减少或使用合理的检索范围。
    • 尽可能减少事务的粒度,比如控制事务大小,从而减少锁定资源量和时间长度,从而减少锁的竞争,提高性能。
    • 尽可能使用低级别的事务隔离级别,隔离级别越高,并发的处理能力越低。

表锁

  • 优势:开销小;加锁快;无死锁
  • 劣势:锁粒度大,发生锁冲突的概率高;处理并发的能力弱
  • 加锁方式:自动加锁。查询操作会自动给涉及的所有表加读锁;更新操作会自动给涉及的表加写锁。
  • 显式加锁:
    • 共享读锁:lock table 【tablename】 read;
    • 独占写锁:lock table 【tablename】 write;
    • 批量解锁:unlock tables;
  • 表锁中:读锁会阻塞写,不会阻塞读;而写锁则会把读写都阻塞。

行锁变表锁

  1. 当索引生效时使用行锁;没有索引或索引失效时使用表锁
  2. 当生效的索引是唯一索引,则使用行锁;当生效的索引不是唯一索引,则使用表锁。(用索引字段做为条件进行修改时, 是否表锁的取决于这个索引字段能否确定记录唯一,当索引值对应记录不唯一,会进行锁表,相反则行锁。)
  3. 当表的大部分数据需要修改时,行锁会升级为表锁
  4. 多表复杂关联查询时,行锁会升级为表锁。因为复杂的关联查询,事务涉及多个表,很可能引起死锁,造成大量事务回滚。这种情况若能一次性锁定事务涉及的表,从而可以避免死锁、减少数据库因事务回滚带来的开销。

锁定义

间隙锁

  • 定义:当使用范围条件检索数据并请求共享或排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做间隙(GAP)。InnoDB 也会对这个间隙加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。
  • 危害:若执行的条件范围过大,则 InnoDB 会将整个范围内所有的索引键值全部锁定,很容易对性能造成影响。

排他锁

  • 定义:排他锁也称为写锁、独占锁,当前写操作没有完成前,会阻断其他写锁和读锁。
  • 排他锁是行锁

共享锁

  • 定义:共享锁也称为读锁,多用于判断数据是否存在。多个读操作可以同时进行而不会互相影响,但会阻塞对同一表的写操作,只有当读锁释放后,才能执行其他进程的写操作。如果事务对读锁进行修改操作,很可能会造成死锁。
  • 共享锁是行锁

意向锁

  • 意向锁是表锁
  • 意向共享锁:事务在请求共享锁时,需要先获取意向共享锁
  • 意向排他锁:事务在请求排他锁时,需要先获取意向排他锁
  • 为什么意向锁是表锁:当需要一个排他锁时,需要根据意向锁判断表中有没有数据行被锁定:
    • 如果意向锁是行锁,则需要遍历每一行去确认
    • 如果意向锁是表锁,则只需要一次即可知道有没有数据行被锁定,提升性能

MyISAM

MyISAM 的读写锁调度是写优先,这也是 MyISAM 不适合做写为主表的存储引擎。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永久阻塞。