MySQL锁总结

104 阅读5分钟

MySQL锁

初衷: 是处理并发问题。

全局锁(数据库级别的锁)

  1. 对数据库实例加锁
  2. 加全局读锁:Flush tables with read lock (FTWRL)
  3. 整个库处于只读状态的时候,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。
  4. 全局锁的典型使用场景是,做全库逻辑备份。
  5. mysqldump 使用参数–single-transaction的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。
  6. single-transaction 方法只适用于所有的表使用事务引擎的库。如果有的表使用了不支持事务的引擎,那么备份就只能通过 FTWRL 方法。

表级锁

表锁

  1. lock tables … read/write
  2. 可以用 unlock tables 主动释放锁,也可以在客户端断开的时候自动释放。

元数据锁(meta data lock,MDL)

  1. MDL 不需要显式使用,在访问一个表的时候会被自动加上。
  2. MDL 的作用是,保证读写的正确性。防止在对数据库进行操作时,数据库的表结构发生了变化 3.在 MySQL 5.5 版本中引入了 MDL,当对一个表做增删改查操作的时候,加 MDL读锁;当要对表做结构变更操作的时候,加 MDL 写锁。

行锁(记录锁)

  1. 行锁就是针对数据表中行记录的锁
  2. 引擎层实现,InnoDB支持、MyISAM不支持,这也是MyISAM被InnoDB替换的重要原因
  3. 两阶段锁协议
    • 在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
    • 如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
  4. 死锁和死锁检测
    • 一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout 来设置。
    • 另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
  5. 快照读、当前读
    • 更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。
    • select 语句如果加锁,也是当前读。
      • select k from t where id=1 lock in share mode;
      • select k from t where id=1 for update;

间隙锁

幻读

  1. 幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。幻读仅专指“新插入的行”。
  2. 在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,幻读在“当前读”下才会出现。
  3. 产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。

Next Key锁

  1. 间隙锁和行锁合称 next-key lock,每个 next-key lock 是前开后闭区间
  2. 加锁规则
    • 原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间。
    • 原则 2:查找过程中访问到的对象才会加锁。
    • 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
    • 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
    • 一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。

其他锁

  1. 读、写锁 X锁、S锁 共享锁、排他锁是一回事
  2. 悲观锁、乐观锁(自行查阅)
  3. 意向锁 分为意向读锁、意向写锁

意向锁

当给某一行增加共享锁、排他锁时,数据库会自动给这一行所处的表添加意向共享锁(IS Lock)、意向排他锁(IX Lock)也就是说,如果想给r行增加锁,需要给r行所在的表先增加意向锁。

事务A锁住了表中的一行,让这一行只能读(数据库自动给该表增加意向共享锁),不能写。之后,当事务B申请整个表的写锁时。

step1:判断表是否已被其他事务用表锁锁表

step2:发现表上有意向共享锁

说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。

如果没有意向锁则是这样的:

step1:判断表是否已被其他事务用表锁锁表。

step2:判断表中的每一行是否已被行锁锁住。

如果没有意向锁的话,则需要遍历所有整个表判断是否有行锁的存在,以免发生冲突如果有了意向锁,只需要判断该意向锁与即将添加的表级锁是否兼容即可。因为意向锁的存在代表了,有行级锁的存在或者即将有行级锁的存在。因而无需遍历整个表,即可获取结果。

注意:IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突

当再向一个表添加表级X锁的时候 如果没有意向锁的话,则需要遍历所有整个表判断是否有行锁的存在,以免发生冲突

如果有了意向锁,只需要判断该意向锁与即将添加的表级锁是否兼容即可。因为意向锁的存在代表了,有行级锁的存在或者即将有行级锁的存在。因而无需遍历整个表,即可获取结果。

  • 源于《MySQL实战45讲》