MySQL事务处理与并发控制( 五 )


因为InnoDB并不会将整个页面的内容写入重做日志 , 而是记录的对页面的操作 , 例如将某个偏移量处的值加2 , 使用重做日志进行恢复的基础是表空间中的实际数据页面在内部是完整的一致的 , 它是哪个页面版本无关紧要  , 但是如果页面不一致 , 则无法继续恢复 , 因为你的基础数据就是不一致的 。为此引入了Doublewrite Buffer来解决问题 。
但是为什么会出现“表空间的数据页面不完整呢?” 这是因为会出现InnoDB部分页面写入的情况:InnoDB中 , 在将缓冲池中的数据刷新到磁盘时是以页面(InnoDB的页面 , 通常为16KB)为单位的 , 这时可能会出现部分页面写入的问题 。所谓部分页面写入是指向操作系统提交的页面写入请求仅部分完成 。例如 , 在16K 的Innodb页面中 , 只有第一个4KB(文件系统的块通常为4KB)的块被写入磁盘 , 其他部分保持原来的状态 。最常见的部分页面写入一般在发生电源故障时发生 。也可能发生在操作系统崩溃时 。另外 , 如果使用软件RAID , 页面可能会出现在需要多个IO请求的条带边界上 。如果硬件RAID没有电池备份 , 电源故障时也会发生这种情况 。如果对磁盘本身发出单个写入 , 即使电源掉电 , 在理论上也应完成写入 , 因为驱动器内部应该有足够的电源来完成它 。但是真的很难检查是否总是这样 , 因为它不是部分页面写入的唯一原因 。
理解了为什么需要Doublewrite Buffer , 也就不难理解Doublewrite Buffer如何工作了 。具体来说就是:你可以将Doublewrite Buffer视为系统表空间中的一个短期日志文件 , 它包含100个页的空间 。当Innodb从Innodb缓冲池中刷新页面时 , InnoDB首先会将页面写入双写缓冲区(顺序) , 然后调用fsync()以确保它们保存到磁盘 , 然后将页面写入真正的数据文件并第二次调用fsync()) 。现在Innodb恢复的时候会检查表空间中数据页面的内容和Doublewrite Buffer中页面的内容 。如果在双写缓冲区中的页面不一致 , 则简单地丢弃它 , 如果表空间中的数据页面不一致 , 则从双写缓冲区中恢复 。
4 MySQL并发控制前面我们已经探讨了需要并发控制技术的原因 , 本节介绍InnoDB使用的并发控制技术 。
InnoDB的并发控制技术包含两种:

  • 1 基于锁的并发控制;
  • 2 MVCC(多版本并发控制) 。
    其中 , 锁是并发控制的基础 , 在此基础上实现了MVCC机制 , 改善了基于锁的方式带来的效率低的问题 。使得读写之间互补阻塞 , 提高了单纯的基于锁的并发效率 。
4.1 基于锁的并发Innodb提供四种粒度的锁:
  • S:共享锁 , 也称为读锁;
  • X:排他锁 , 也称为写锁;
  • IS:意向共享锁 , 也称为意向读锁;
  • IX:意向排他锁 , 也称为意向写锁 。
InnoDB锁的想容性矩阵如下:
MySQL事务处理与并发控制

文章插图
 
InnoDB的加锁机制符合SS2PL(增强两阶段封锁协议) , 即:
每个事务分为两个阶段:
  • 增长阶段:事务只能获得锁 , 但不能释放锁;
  • 缩减阶段:事务可以释放锁 , 但不可获取锁 。
  • 事务提交之前不能释放任何锁 。
    不过不同粒度的锁的施加时机和持锁时间与隔离级别和MVCC相关 。“串行化”隔离级别下 , MVCC不发生作用 , InnoDB严格遵循SS2PL 。
    可以通过:
show status like 'innodb_row_lock%'; select * from information_schema.innodb_locks; -- 5.7即以前select * from performance_schema.data_locks; -- 8.0即以后来查看行锁的争用情况 。更多信息 , 可以参考MySQL官方手册 。
意向锁作用解读
InnoDB 支持多粒度锁(multiple granularity locking) , 它允许行级锁与表级锁共存 , 而意向锁就是其中的一种表锁 。
意向锁分为两种: 意向共享锁(intention shared lock, IS):事务有意向对表中的某些行加共享锁(S锁)
-- 事务要获取某些行的 S 锁 , 必须先获得表的 IS 锁 。SELECT column FROM table ... LOCK IN SHARE MODE;意向排他锁(intention exclusive lock, IX):事务有意向对表中的某些行加排他锁(X锁)


推荐阅读