MySQL(3)-日志
3. InnoDB日志
3.1 InnoDB架构
分为
- 内存区域架构
- buffer pool
- log buffer
- 磁盘区域架构
- redo log
- undo log
2.1.1 内存区域架构
1)Buffer Pool
-
定义
InnoDB对会将磁盘中经常访问的数据所在的页存入Buffer Pool中以加快访问速度,这种操作称为预读,后续对磁盘上某条数据做修改时,也是先读取到buffer pool中,再修改buffer pool中的数据
-
组成
由多个Page组成,其中存储了磁盘上的多行数据,方便了大容量的高效读操作,此外,将Page组织成链表结构,便于采用LRU进行内存淘汰
2)Change Buffer
-
定义
对于非唯一索引,如果在修改其对应的数据时,行记录不存在于Buffer Pool,那么就将修改操作记录到Change Buffer,等待后面真正读取到该记录到Buffer Pool中时,再将结果进行merge,并修改磁盘中的数据
-
为何必须非唯一
对于唯一索引,InnoDB需要对记录的唯一性做校验,也就必须从磁盘中读取到数据,而change buffer的存在意义是尽量避免不必要的随机磁盘io,而对于唯一性校验来说,磁盘io是不可避免的,且由于唯一索引b+树的特点,在是自增的情况下,插入操作是一次顺序io,效率是很高的,也就不必有change buffer
3)自适应哈希索引
-
定义
为了让MySQL性能更接近于基于内存的数据库,对于经常访问的数据,会根据数据的特点,以表的某几列建立hash索引,存储结构类似于hashmap,采用拉链法解决hash冲突,使得查询复杂度降低到O(1)
-
特性
- 优
- 适应等值比较
- 适应单条数据查询
- 劣
- 不适应范围查询
- 不可用范围或like比较
- 不适用连表查询
- 优
4)Log Buffer
是磁盘上log文件的缓冲区,修改会先记录到此buffer,之后异步的同步到磁盘上的log文件
3.1.2 磁盘区域架构
除了表、索引、表空间之外还有
1)Doublewrite Buffer
buffer pool中对页面的修改信息不会直接同步到对应的表中,而是会以大的连续块的形式调用fsync()
写入到双写缓存中,这样在os、存储引擎或其他异常发生时,可以从双写缓存中找到备份
2)redo log
3)undo log
3.2 bin log&redo log&undo log
3.2.1 binlog
1)组成
-
binlog cache
作为binlog的缓存,会先写入cache中
-
binlog-xxx
存在于磁盘上的binlog文件,是append only式创建
2)存储内容
可以配置成三种类型
-
statement
记录为逻辑日志,存储提交的事务的DML语句和事务号
-
row
记录为物理日志,记录了实际的修改,会使得日志比较大
-
mixed
前两者的结合
3)作用
多用于主从同步和冷备
4)层级
位于MySQL server层
3.2.2 redo log
1)组成
-
redo log buffer
作为redo log的缓存,会先写入cache中
-
ib_logfile-xxx
存在于磁盘上的redo log文件,是循环写入的,对于已经提交的事务,会清空
2)存储内容
是物理日志和逻辑日志的结合,物理体现在记录了具体某一页上发生了修改,逻辑体现在页内的实际修改是以记录DML语句完成的
3)作用
用于宕机恢复,保证一致性
4)层级
位于存储引擎层
3.2.3 undo log
1)组成
-
undo log
-
update log
对于未提交的事务内发生的update操作,会存储相反的update
-
insert log
对于未提交的事务内发生的delete操作,会存储对应的insert
-
delete log
对于未提交的事务内发生的insert操作,会存储对应的delete
-
上述log会以事务号的顺序编排成一个链表以便于确定要回滚到哪个事务
2)存储内容
是逻辑日志,存储相反的DML语句
3)作用
用于回滚,保证原子性
4)层级
位于存储引擎层
3.3 事务内修改流程
假设事务内存在一条insert语句,那么实际执行流程如下
- 导入buffer并修改
- 记录undo log
- 记录redo log buffer并写盘
- 2PC提交
详细流程如下
3.3.1 导入buffer并修改
检查buffer pool中是否存在要更新的数据所在的页,如果不存在,需要将页面读入buffer pool,之后修改对应的数据
3.3.2 记录undo log
将delete语句记录到磁盘中的undo log,组织成链表
3.3.3 记录redo log buffer并写盘
将修改记录到buffer,之后根据写盘策略,将buffer中的数据写入到redo log,同步的策略有下面三种,通过设定innodb_flush_log_at_trx_commit
完成
-
0
每次提交都写入redo log buffer,之后每秒执行
fsync()
同步到redo log -
1
每次提交都直接写入到redo log中
-
2
每次提交写入os cache,之后根据
innodb_flush_log_at_timeout
配置,决定多久后fsync()
3.3.4 2PC提交
1)流程
由于InnoDB的redo log出现晚于binlog,且两者都用于crash safe,那么就需要保证binlog和redolog中数据的一致性,这里采用类似于分布式事务中的想法,采用两阶段提交的方式来保证一致性,流程如下(此时默认redo log写盘已经执行)
-
进入Prepare阶段,设置redo log为prepare
-
写入binlog cache
-
进入Commit阶段,设置redo log为commit
-
根据binlog的写盘策略,将binlog cahce写入binlog,策略有下面三种
-
0
每次提交写入到os cache
-
1
每次提交都直接写入bin log
-
N
每次都写入os cache,累计N个事务再
fsync()
-
2)异常分析
-
写redo log宕机
这时可以根据已经落盘的undo log进行回滚
-
写binlog cache宕机
这时一致性未达成,根据undo log做回滚
-
提交后宕机
检查redo log中存储的最新事务号是否存在于binlog,如果不存在,将不存在的回滚
3.4 预写日志
预写日志(Write Ahead Log)即在修改磁盘内的数据页中的信息前,将修改信息先写入磁盘中的log文件,如redo log和bin log
这么做有以下优势
-
顺序io
由于redo log和binlog落盘时是顺序写入的,而如果直接修改磁盘中数据页中的数据,是随机io,效率非常低
-
并发量大
读写者互不阻塞
-
fsync调用次数少
相较于直接写入磁盘,WAL的fsync调用次数很少,无需每个事务都写盘
3.5 sync、fsync和fdatasync
3.5.0 延迟写
linux中为了减少磁盘io,在写入磁盘时会经历如下步骤
- 写入os cache
- 写入output queue
- 写入磁盘
只有当os cache满时,才会复制到output queue;只有output queue队首的数据会被写入到磁盘
3.5.1 sync
将数据同步到os cache,并不会等待到写入磁盘后返回,这需要update守护进程周期性调用sync将os cache输出到output queue保证写盘成功
3.5.2 fsync
对于某个文件的fd,调用fsync会在写盘成功后返回,写入的数据包括inode中的文件属性以及文件的数据部分
3.5.3 fdatasync
同样对于某个文件的fd,调用fdatasync会在写盘成功后返回,写入的数据只有文件的数据部分,不包括inode
3.6 double write和redo log
3.6.1 为何需要Doublewrite
buffer pool中的数据要写入到磁盘时,是以页为单位,如果写入过程出现宕机,那么就算有redo log也无法恢复,由于redo log每个页内记录的是逻辑日志,而逻辑日志需要保证表中的数据是完备且未改动的,这样where条件才不会失效,因而redo log并不能保证页面级别的crash safe
3.6.2 Doublewrite实现
1)组成
分为两部分
- 内存中的double write buffer
- 物理磁盘上共享表空间中连续的128个页,即2个区(extent),大小同样为2MB
2)机制
流程如下
- 每次脏页会先复制到double write buffer
- 分两次,每次1MB将页面信息书顺序写入到磁盘上的共享表空间
- 将buffer中的数据调用fsync离散写入磁盘
这样由于在磁盘的共享表空间中记录了页面的详细修改信息,就可以在同步页面到磁盘上时保证crash safe
# 参考
MySQL :: MySQL 5.7 Reference Manual :: 14.4 InnoDB Architecture
为什么数据不会丢,InnoDB的Double Write,你必须知道 – 掘金 (juejin.cn)
MySQL–buffer pool、redo log、undo log、binlog_黄智霖的博客-CSDN博客
Write-Ahead Logging (sqlite.org)
redo log的被动刷盘机制 – 云+社区 – 腾讯云 (tencent.com)
Linux IO同步函数:sync、fsync、fdatasync | Byte_Liu’s Blog (byteliu.com)