• MySQL之InnoDB浅析


    InnoDB是一个兼顾高可靠性和高性能的通用存储引擎。在 MySQL 5.7 中,InnoDB是默认的 MySQL 存储引擎。

    InnoDB 的主要优势

    • 支持事物,具备crash-safe的能力
    • 支持行锁以及MVCC,具备良好的多用户并发性和性能
    • Buffer Pool,提升热点数据的查询效率以及数据频繁更新时的效率
    • 聚簇索引,基于主键的查询有更小的IO次数,更加高效
    • 支持外键

    架构图

    在这里插入图片描述

    Buffer Pool

    Buffer Pool用于在 InnoDB访问时缓存表和索引数据,并且对于热点数据的访问有很好的加速效果。

    Buffer Poold在内存中以链表的形式存在,该链表基于变种LRU进行管理。当新的数据页被添加到Buffer Pool时,会被添加到这个链表靠近中间的位置,这个位置被称为“midpoint”。这种插入策略将链表分成了两个子链表:

    • 靠近头部的一端是最近经常访问的热点数据(new区)
    • 靠近尾部的一端是最近较少访问的冷数据(old区)

    在这里插入图片描述

    当数据页被添加到old区后,如果该数据页又被再次访问了,并且其被添加到old区的时间超过一个阈值(默认是一秒,由innodb_old_blocks_time控制),则该数据页会被挪到new区的头部,其他数据向尾部移动。

    随着数据页的不断读入以及链表中数据页访问频率的变化,new区跟old区中的某些数据页会不断的向尾部移动,直到最终被移除。

    Change Buffer

    Change Buffer用于缓存二级非唯一索引页的更新操作,加速insert、update、delete这类语句的执行效率。

    相对于聚集索引,二级索引的插入顺序相相对随机,此时需要将数据页读入内存来判断插入位置,这个过程会产生很多随机IO。

    对于二级索引中的非唯一索引,索引页的更新肯定不会引发唯一约束的冲突。此时便可以直接将更新操作保存在内存中,等到真正需要用到这些更新影响到的索引页时,再将这些更新应用到被影响的索引页即可。这种方式可以避免大量的随机IO访问,并且,读取之前更新的次数越多,收益越高。

    可以通过innodb_change_buffering改变执行策略

    • all

      默认值:缓冲插入、删除和purge。

    • none

      不要缓冲任何操作。

    • inserts

      缓冲插入操作。

    • deletes

      缓冲删除操作。

    • changes

      缓冲插入和删除操作。

    • purges

      缓冲在后台发生的物理删除操作。

    通过innodb_change_buffer_max_size 指定Change Buffer在Buffer Pool中的占比。默认25%,最大50%。

    Redo Log

    Redo Log用于实现事物的持久性(Durability),它让MySQL拥有了崩溃恢复能力。

    Redo Log通过追加的方式进行数据写入,且记录的是数据的变化(表空间号、数据页号、磁盘文件偏移 量、更新值)。这样有两个好处,第一是顺序IO,第二是减少了刷到磁盘的数据量(16KB可以记录许多个数据页的改动)。这也是为什么不直接用数据文件或者Doublewrite Buffer来实现持久性的原因。

    Redo Log由以两部分组成:

    1. Log files(默认100MB,最大128GB):磁盘上的文件,大小固定。通过checkpoint技术实现循环写入。

    2. Log Buffer(默认为 16 MB):内存区域,用于保存事物运行中需要被被写入到redo log的数据。这些数据默认会在事物提交时从内存刷入磁盘,当然,如果事物过大,Log Buffer不足以完全缓存这些数据,则会提前刷盘。

      同时,可以通过指定innodb_flush_log_at_trx_commit来控制Log Buffer里的数据如何写入和刷新到磁盘。有如下三个选项:

      • 1 :每次提交事务时刷盘
      • 0:默认每秒刷盘一次,可通过innodb_flush_log_at_timeout指定间隔。未刷盘事务可能会在crash中丢失
      • 2:事物提交后先写到os cache,并每秒刷盘。MySQL进程crash不会造成数据丢失。但是未刷盘事务可能会在操作系统crash中丢失

      Log Buffer通过对Redo log的批量刷盘(不同事物同时提交时会一起刷),避免了频繁的磁盘IO。

    两阶段提交

    由于BinLog处于Server层,Redo Log处于存储引擎层。为了保证Redo Log跟BinLog的一致性,在InnoDB写完Redo Log后会先通知执行器RedoLog已写入(prepare)。然后执行器会对BinLog执行写入操作。等到BinLog写入成功,执行器通知InnoDB,事物进入commit。

    Doublewrite Buffer

    在刷脏页时,数据页首先会被写到Doublewrite Buffer,然后才会被写到Innodb的数据文件。

    在InnoDB里,页大小默认为16KB,而OS页大小通常默认时4KB。这意味着**InnoDB将Buffer Pool中一页数据刷入磁盘,要写4个OS页。而这并不是一个原子操作。**如果突然断电,就有可能存在只刷了2个OS页的情况。此时,数据页里部分数据(LSN)已经丢失,像这种情况,是无法靠Redo Log恢复的。

    而Doublewrite Buffer里有完整的页信息,所以此时可以从Doublewrite进行恢复。那么,如果在写Doublewrite Buffer的时候断电呢?那就再重启后重新读入数据页并应用Redo Log再重新写DoubleWrite Buffer即可。

    Undo Log

    Undo Log用于实现事物的原子性(atomicity)以及MVCC。

    Undo Log由rollback segments组成,单个表空间默认/最大128个。segmentUndo Slots组成,slot的数量取决于页大小。页大小为16kb时单个segment下slot的数量为1024。slot里存放的是用于回滚的信息。

    segment为Innodb的事物并发提供支持。segment的数量与能够支持的事物并发数正相关,通常一个slot能够支持一个事物。具体计算方式也很简单,参考事物并发数计算

    通过innodb_rollback_segments指定segment数量

    MVCC

    MVCC全称Multiversion concurrency control,及多版本并发控制。是数据库管理系统
    常用的一种并发控制,也用于程序设计语言实现事务内存

    MVCC目的是为了解决读写锁造成的多个、长时间的读操作阻塞写操作的问题。在特定的隔离级别(Read Commited、Repeatable Read)下,事务读操作读到的数据项是一个历史快照,写操作不覆盖已有数据项,而是创建一个新的版本,直至所在操作提交时才变为可见。这种方式使得事务在整个运行过程中看到的都是它启动时的数据状态。

    InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请,按申请顺序严格递增。

    在事物更新数据时,会将transaction id以及更新前的数据保存在undo log中。同时,会将transaction id保存在被修改的行的隐藏列DB_TRX_ID中。行记录中还有一个隐藏列,叫DB_ROLL_PTR,存储的事一个指向rollback segment的指针。rollback segment里存着撤销记录,用于撤销事物对数据的更改。同时,每条撤消记录都包含对其先前撤消记录的引用。这样,当前记录的所有先前版本就组成了一个链表。所以,只要撤消记录(“历史记录”)仍然存在于撤消日志中,就可以轻松构建任何先前版本的记录。

    如果在该事物未提交时有其他事物来读取该行,就会通过这个指针来回溯并重建未修改前的数据。回溯的程度通过比较读事物跟redo log中的transaction id来决定,读事物能看到的数据必须是在其启动时已经提交的事物作出的数据修改
    在这里插入图片描述

    参考

    https://dev.mysql.com/doc/refman/8.0/en
    https://blog.jcole.us/2014/04/16/the-basics-of-the-innodb-undo-logging-and-history-system/
    High Performance MySQL 4th edition

  • 相关阅读:
    python:GUI图形化数据库巡检工具
    【American English】美语的连读规则
    BFD双向转发检测协议理论讲解
    Spark部署模式与作业提交
    加入购物车(springboot项目)
    引擎入门 | Unity UI简介–第2部分(5)
    Mysql学习
    高等数学(第七版)同济大学 总习题五(前10题) 个人解答
    高速USB转JTAG/SPI/I2C/UART/GPIO应用
    【vue设计与实现】内部组件和模块 1 - KeepAlive组件的实现原理
  • 原文地址:https://blog.csdn.net/scientificCommunity/article/details/128047006