「黑马 MySQL」二、InnoDB 引擎
逻辑存储结构

image.png|500
在 InnoDB 存储引擎中,每行数据包含了表中所定义的所有字段的值之外,还包括以下几个重要的东西:
- 6 字节的事务 ID trx id 在 InnoDB 中,每个事务都会被赋予一个唯一的事务 ID,并且每行数据都包含了这个事务 ID 的值,用于实现事务级别的多版本并发控制 MVCC
- 7 字节的回滚指针 roll pointer 为了支持事务的回滚操作,在每个行数据中都会包含一个指向回滚日志里对应记录的回滚指针,当事务需要回滚时,通过回滚指针可以快速定位到对应的回滚记录并恢复数据。
- 非空列位图 非空列位图是 InnoDB 存储引擎中用于提高存储效率的一种机制,由于在一个表中往往会有很多列都是 NULL 值,因此可以将这些列的信息使用位图的方式来进行表示,从而在存储数据时尽可能地压缩存储空间
- 记录头 每个行数据都包含了一个记录头,其中包括了一些元数据信息,如数据是否被删除、是否被锁定等
- 版本号 在 InnoDB 的 MVCC 实现中,每个行数据还会包含一个版本号,用来表示当前行的版本信息,当一个事务更新一行数据时,实际上是在原有行数据的基础上新增了一条新记录,并通过版本号来区分这两条记录
综上所述,InnoDB 行 ROW 中除了表中所定义的字段的值,还包含了很多其他的元数据信息,在 InnoDB 存储引擎实现事务支持、MVCC、存储效率和数据恢复等方面都发挥着重要的作用
架构
下面是 InnoDB 架构图,左侧为内存结构,右侧是磁盘结构

image.png|500
内存结构 - 缓冲池 Buffer Pool
操作缓存池的数据,再以一定频率刷新到磁盘,减少磁盘 IO

image.png|500
缓冲池以页 Page 为单位,底层采用链表管理,分为三种类型:
- free page:空闲 page,未被使用
- clean page: 被使用过的 page,但没有被修改过数据
- dirty page:脏页,被使用过的 page,修改过数据,此时数据与磁盘的数据产生了不一致,需要刷新进磁盘
内存结构 - 更改缓冲区 Change Buffer
将数据的变更缓存在更改缓冲区,当未来有数据被读取时,将数据合并并恢复到缓冲池 Buffer Pool 中,然后合并的数据会被刷入磁盘

image.png|500
更改缓冲区 Change Buffer 存在意义: 二级索引通常非唯一的并且插入的比较随机,删除更改操作的设涉及范围比较大,造成大量磁盘 IO,先在缓冲池 Buffer Pool 中合并数据可以减少磁盘 IO
内存结构 - 自适应哈希索引 Adaptive Hash Index
优化对 Buffer Pool 的数据查询

image.png|500
内存结构 - 日志缓冲区 Log Buffer
日志缓冲区保存 redo log、undo log 这类日志数据,默认 16MB,定期刷到磁盘,减少大量操作频繁磁盘 IO

image.png|500
磁盘结构 - 系统表空间 System Tablespace

image.png|500
磁盘结构 - 文件表空间 File-Per-Table Tablespaces:

image.png|500
磁盘结构 - 通用表空间 General Tablespaces

image.png|500
磁盘结构 - 撤销表空间 Undo Tablespaces 临时表空间 Temporary Tablespaces

image.png|500
磁盘结构 - 双写缓冲区 Doublewrite Buffer Files 重做日志 Redo Log

image.png|500
后台线程
后台线程作用:内存刷到磁盘

image.png|500
事务原理
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败
ACID 特性
- 原子性 Atomicity:事务是不可分割的最小操作单元,要么全部成功,要么全部失败
- 一致性 Consistency:事务完成时,必须使所有的数据都保持一致状态
- 隔离性 Isolation:数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性 Durability:事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
原子性 - undo log 持久性 - redo log 一致性 - undo log + redo log 隔离性 - 锁 + MVCC
redo log - 持久性
当脏页刷入磁盘发生错误时,根据 redo log 进行数据的恢复

image.png|500
WAL write-ahead logging 先写日志,日志时 append 写入,顺序磁盘 IO,而非刷入脏页的随机磁盘 IO
undo log -原子性
回滚日志 undo log 用于记录数据被修改前的信息,提供回滚 rollback 和 MVCC
undo log 主要适用于回滚,所以记录是逻辑日志,会记录相反操作语句,这样 rollback 就可以读到相应内容并回滚

image.png|500
MVCC
RU:直接读最近数据,没 MVCC,没锁,什么都没 RC:每次读都生成一个快照读,可以读到最新的已提交数据 RR:事务开启后的第一个 select 语句生成快照读,此后延续使用这个 readview,保证可重复读 SE:每次都是当前读,会被阻塞,也会上锁
当前读: 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁,如: select..lock in share mode 共享锁,select..for update、update、insert、delete 排他锁都是一种当前读
快照读: 简单的 select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读
- Read Committed:每次select,都生成一个快照读
- Repeatable Read:开启事务后第一个select语句才是快照读的地方
- Serializable:快照读会退化为当前读
MVCC: 全称 Multi-Version Concurrency Control,多版本并发控制,指维护一个数据的多个版本,使得读写操作没有冲突,快照读为 MySQL 实现 MVCC 提供了一个非阻塞读功能 MVCC 的具体实现,依赖于数据库记录中的三个隐式字段、undo log 日志、readView
实现原理 - 三个隐式字段
DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID

image.png|500
实现原理 - undo log 日志
回滚日志 undo log,在 insert、update、delete 时产生,便于数据回滚的日志 insert 产生的 undo log 日志只在回滚时需要,在事务提交后,可被立即删除 update、delete 产生的 undo log 日志不仅在回滚时需要,在快照读时也需要,不会立即被删除

image.png|500
不同事务或相同事务对同一条记录进行修改,会导致该记录的 undo log 生成一条记录版本链表,链表的头部是最新的旧日记录,链表尾部是最早的旧记录
实现原理 - readView
每个事物在进行读取数据时生成的 readView 都不同

image.png|500
readView 版本链访问规则
- 先看当前事务 trx_id 是否等于 creator_trx_id,是则访问,不是则 2
- 检查当前事务 trx_id 是否 between min_trx_id and max_trx_id(最大 trx_id + 1)并且已经不再活跃(已提交),是则访问,不是则 3
- 找到最大的小于 min_trx_id 的版本(最早已提交版本)

image.png|500
Read Commited
事务 5 第一次快照读,trx_id = 2 的版本符合条件 2 的要求,trx_id < min_trx_id = 3,实现读事务 2 已提交
事务 5 第二次快照读,trx_id = 3 的版本符合条件 2 的要求,trx_id < min_trx_id = 4,实现读事务 3 已提交

image.png|500
Repeatable Read
RR 事务隔离级别仅在第一次执行快照读时生成 readView,后续复用该 readView

image.png|500