0、参考

MySQL锁详解:https://www.yuque.com/yanzipang-wf7ur/oiyc8o/qtavo9#WSPXV
深入学习MySQL事务:ACID特性的实现原理https://www.cnblogs.com/kismetv/p/10331633.html
MySQL中的锁(表锁、行锁):https://blog.csdn.net/zdklhh/article/details/89766379

一、概述

  1. 根据加锁的范围,MySQL里面的锁大致可以分成全局锁、表级锁和行锁三类。
  2. 锁是计算机协调多个进程或纯线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所在有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
  3. 相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。
  4. MySQL大致可归纳为以下3种锁:
    1. 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
      1. 意向共享锁,也称为IS锁:事务打算给数据行加S锁,就先取得表的IS锁。简单来说,就是先获取整张表的全局锁(IS),然后再给每条数据加行锁(S锁)。
      2. 意向排它锁,也称为IX锁:事务打算给数据行加X锁,就先取得表的IX锁。简单来说,就是先获取整张表的全局锁(IX),然后再给每条数据加行锁(X锁)。
    2. 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
      1. 共享锁,也称为S锁:S锁允许多个事务去读取同一行数据,阻止其它事务获得X锁更新同一行数据。简单来说,读读不互斥、读写互斥。
      2. 排它锁,也称为X锁:只允许获得X锁的事务更新数据,阻止其它事务获取S锁和X锁,也就是不能读也不能更新。简单来说,读写互斥,写写互斥。
    3. 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

      二、InnoDB 行级锁与表级锁

      InnoDB 存储引擎支持行和表级锁,那么,什么场景下会锁住行、什么场景下又会锁住表呢?
      InnoDB 行级锁是通过给索引上的索引项加锁来实现的,所以,只有通过索引条件检索的数据,InnoDB 才会使用行级锁。否则,使用表级锁。总结下来:
  • 不通过索引条件查询时,InnoDB 一定会使用表锁,而不是行锁(因为没有索引只能全表扫描)(innodb下数据加锁没使用到索引,行锁会升级为表锁。)
  • 查询时,不论是使用主键索引、唯一索引或者普通的索引,InnoDB 都会使用行锁来对数据加锁

对于第二条,在有些特殊情况下是不成立的。例如,对于数据量很少的表,MySQL 会认为全表扫描更快,此时,即使使用索引字段查询,InnoDB 也会使用表锁,而不是行锁。因此,如果想确定当前使用的是哪一种锁,检查下 SQL 的执行计划(EXPLAIN),确认是否在查询时使用了索引。

三、间隙锁(Next-Key锁)

  • 当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制不是所谓的间隙锁(Next-Key锁)。
  • 举例来说,假如emp表中只有101条记录,其empid的值分别是1,2,…,100,101,下面的SQL:SELECT * FROM emp WHERE empid > 100 FOR UPDATE
  • 是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。
  • InnoDB使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求,对于上面的例子,要是不使用间隙锁,如果其他事务插入了empid大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读;另一方面,是为了满足其恢复和复制的需要。有关其恢复和复制对机制的影响,以及不同隔离级别下InnoDB使用间隙锁的情况。
  • 很显然,在使用范围条件检索并锁定记录时,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待。因此,在实际开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。

    四、什么时候使用表锁

  1. 对于InnoDB表,在绝大部分情况下都应该使用行级锁,因为事务和行锁往往是我们之所以选择InnoDB表的理由。但在个另特殊事务中,也可以考虑使用表级锁。
  2. 第一种情况是:事务需要更新大部分或全部数据,表又比较大,如果使用默认的行锁,不仅这个事务执行效率低,而且可能造成其他事务长时间锁等待和锁冲突,这种情况下可以考虑使用表锁来提高该事务的执行速度。
  3. 第二种情况是:事务涉及多个表,比较复杂,很可能引起死锁,造成大量事务回滚。这种情况也可以考虑一次性锁定事务涉及的表,从而避免死锁、减少数据库因事务回滚带来的开销。
  4. 当然,应用中这两种事务不能太多,否则,就应该考虑使用MyISAM表。 (MyISAM仅支持表锁)

    五、关于死锁

  • MyISAM表锁是deadlock free的,这是因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。但是在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了InnoDB发生死锁是可能的。
  • 发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并退回,另一个事务获得锁,继续完成事务。但在涉及外部锁,或涉及锁的情况下,InnoDB并不能完全自动检测到死锁,这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决。需要说明的是,这个参数并不是只用来解决死锁问题,在并发访问比较高的情况下,如果大量事务因无法立即获取所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖垮数据库。我们通过设置合适的锁等待超时阈值,可以避免这种情况发生。
  • 通常来说,死锁都是应用设计的问题,通过调整业务流程、数据库对象设计、事务大小、以及访问数据库的SQL语句,绝大部分都可以避免。



[

](https://blog.csdn.net/l09120204/article/details/108095354)