InnoDB的7大锁,你知道吗?

自增锁

普通的插入是不相互阻塞的,但是面试官能这么问,肯定有阻塞的情况,既然表里只有自增主键,那么只有一种锁,那就是自增锁,自增锁有3中模式,每一种的阻塞情况都不一样

传统模式(innodb_autoinc_lock_mode=0):表级锁,如果插入位置冲突,多个事务会阻塞,以保证数据一致性

间断模式(innodb_autoinc_lock_mode=1):INSERT 语句能提前确定插入的数据量,把需要的空间留出来,则不必获取自增锁

  • MySQL 8.0 之前默认的模式,之所以提出这种模式,是因为传统模式存在影响性能的弊病,所以才有了间断模式。
  • 在锁模式处于间断模式下时,如果 INSERT 语句提前确定插入的数据量,则不必获取自增锁,举个例子,像 INSERT INTO 这种简略的、能提前确认数量的新增语句,就不会获取自增锁,这个很好了解,在自增值上,我能够间接把这个 INSERT 语句所需要的空间留出来,就能够继续执行下一个语句了。
  • 然而如果 INSERT 语句不能提前确认数据量,则还是会去获取自增锁。例如像 INSERT INTO ... SELECT ... 这种语句,INSERT 的值来源于另一个 SELECT 语句。
  • 穿插模式(innodb_autoinc_lock_mode=2):应用较为轻量的 mutex 锁,多条 INSERT 语句能够并发的执行

  • 穿插模式下,所有的 INSERT 语句,包括 INSERT 和 INSERT INTO ... SELECT ,都不会应用 AUTO-INC 自增锁,而是应用较为轻量的 mutex 锁。这样一来,多条 INSERT 语句能够并发的执行,这也是三种锁模式中扩展性最好的一种。
  • 并发执行所带来的副作用就是单个 INSERT 的自增值并不间断,因为 AUTO_INCREMENT 的值调配会在多个 INSERT 语句中来回穿插的执行。
  • 注意:INSERT INTO ... SELECT 当 insert后面的select操作的表和插入的表如果是一张表,此时使用AUTO-INC lock锁住这张表,如果不是的话,只会锁定select中的表。这是为了防止期间有其他数据写入
  • 穿插模式缺点:间接导致主从之间同行的数据主键 ID 不同
  • 共享/排他锁

  • 读锁 :也称为 共享锁 、英文用 S 表示。针对同一份数据,多个事务的读操作可以同时进行而不会互相影响,相互不阻塞的。
  • 写锁 :也称为 排他锁 、英文用 X 表示。当前写操作没有完成前,它会阻断其他写锁和读锁。这样就能确保在给定的时间里,只有一个事务能执行写入,并防止其他用户读取正在写入的同一资源。
  • 需要注意的是对于 InnoDB 引擎来说,读锁和写锁可以加在表上,也可以加在行上。
  • 意向锁(Intention locks):

    加意向锁的目的是为了表明某个事务正在锁定一行或者将要锁定一行。表名加锁的“意图”。

    意向锁有两种

  • 意向共享锁(IS)表示事务意图在表中的单个行上设置共享锁。
  • 意向排他锁(IX)表明事务意图在表中的单个行上设置独占锁。
  • 意向锁,本质上就是空间换时间的产物,是为了提高行锁效率的一个东西。

    在 InnoDB 中,我们对某条记录进行锁定时,为了提高并发度,通常都只是锁定这一行记录,而不是锁定整个表。而当我们需要为整个表加 X 锁的时候,我们就需要遍历整个表的记录,如果每条记录都没有被加锁,才可以给整个表加 X 锁。而这个遍历过程就很费时间,这时候就有了意向锁的诞生。

    意向锁其实就是标记这个表有没有被锁,如果有某条记录被锁住了,那么就必须获取该表的意向锁。所以当我们需要判断这个表的记录有没有被加锁时,直接判断意向锁就可以了,减少了遍历的时间,提高了效率,是典型的用空间换时间的做法。

    再次重申:这里的S锁和X锁是表级别的,意向锁不会与行级别的S锁和X锁冲突

    记录锁

  • 记录锁是有S锁和X锁之分的,称之为 S型记录锁 和 X型记录锁 。
  • 当一个事务获取了一条记录的S型记录锁后,其他事务也可以继续获取该记录的S型记录锁,但不可以继续获取X型记录锁;
  • 当一个事务获取了一条记录的X型记录锁后,其他事务既不可以继续获取该记录的S型记录锁,也不可以继续获取X型记录锁。
  • 间隙锁

    间隙锁是用来锁住一段索引区间的。例如:select col from t where col between 1 and 10 for update;会锁住1~10之间的间隙,而不管这段间隙内是否存在记录,因此间隙锁可能只锁住了一段空气。此时如果想插入为col为5的记录会被阻塞,即使5的记录不存在。间隙锁的唯一目的即是阻止其他的事务往间隙中插入记录,因此不同的事务可以对同样的间隙重复加锁,没用共享和排他类型之分。

    间隙锁只在特性的隔离级别下使用,关闭间隙锁可以用2种方式:

  • 将事务的隔离级别设置为read committed
  • 将参数innodb_locks_unsafe_for_binlog(过时参数)设置为1
  • 间隙锁是性能和并发之前的一个权衡,关闭间隙锁将破坏事务的隔离性。

    临键锁(Next-Key Locks)

  • Next-key lock 是记录锁和间隙锁的结合,next-key lock会对记录本身和记录之前间隙加锁。
  • Next-key lock只在MySQL的repeatable read隔离级别下使用,主要是用来解决幻读(phantom read)的问题。幻读即在同一个事务中,同样的查询读到了之前不存在的记录(由其他事务提交)。由于读到的是已提交的数据,因此很多DB厂商认为这不是问题,例如Oracle/SQLserver的默认隔离级别是read committed。是允许幻读和不可重复读存在的。而MySQL可以repeatable read隔离级别可以使用next-key lock实现事务的完全隔离。
  • 当对唯一键值进行锁定时,next-key lock将会降级为record lock,即仅锁住唯一记录。而如果唯一键由多个列组成,而查询仅使用其中一列,则其实是range查询,InnoDB会依然使用next-key lock进行锁定。
  • 插入意向锁(Insert intention locks):

  • 插入意向锁是间隙锁的一种,其由inert语句在插入记录前获取,代表将在间隙中插入记录的意向。多个事务可以对同一个间隙重复加insert intention lock,只要插入的记录值不同,事务就不会冲突。例如表中已存在记录1和10,两个事务分别想插入5和6。两个事务都会对1和10记录之间的间隙(2,9)加insert intention lock,但由于插入的记录值不同,因此后续对要插入的记录获取X锁的时候并不会冲突。
  • ©著作权归作者所有,转载或内容合作请联系作者
    平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

    推荐阅读更多精彩内容