innoDB体系架构

141 阅读7分钟

前言

本系列文章是对 MySQL技术内幕 InnoDB存储引擎 一书观后总结

InnoDB存储引擎是什么?

innoDB存储引擎是基于磁盘存储的mysql存储引擎之一, 也是mysql默认存储引擎

innoDB支持事务, 支持行锁和表锁, 支持外键

innodb将数据存储在逻辑上的表空间中, 而数据管理单位是页, 也就是说mysql使用页的方式对数据进行curd,. 类似于cache line

同时mysql使用mvcc(多版本并发控制)的方式来提高并发的性能, 并且实现了4个隔离级别, 默认为可重复读级别

使用临键锁的策略来避免幻读问题

此外, innoDB还提供了插入缓冲(insert buffer), 二次写(double write), 自适应哈希索引(adpative hash index), 预读(read ahead)等高性能和高可用的功能

innodb还会按照主键顺序进行存放, 如果没有主键还会生成一个隐藏主键

InnoDB体系架构

image.png

前端的后台线程在文件中读取数据缓存到内存池中, 然后线程的操作将直接在内存池中进行, 然后再同步到磁盘中

这是InnoDB存储引擎的整体工作流程

后台线程

后台线程的介绍?

后台线程分为

  • Master Thread: 核心后台线程, 主要负责将内存池中的数据刷新到文件中
  • IO Thread: 负责mysql的IO操作, 主要负责IO请求的回调, IO成功之后, 需要执行回调函数时就会使用IO Thread执行回调函数
  • Purge Thread: 回收事务提交后没有用的undo log, 而且是一个线程池
  • Page cleaner Thread: 刷新脏页, 这里的脏页并不是脏读, 反而是已经被修改, 但未被同步到磁盘中的有用数据

内存

缓冲池

为什么需要缓冲池?

由于磁盘和cpu速度存在鸿沟, 所以为了提高数据库的整理性能, 引入了缓冲池

好处

将磁盘中读取页, 然后存放在缓冲池中, 下次先判断缓冲池是否命中, 如果命中, 则直接在缓冲池中操作, 否则还是读取页到内存中

修改页, 再按照频率同步到磁盘中

碰到检查点就同步一次

Q: 那么缓冲池中不可能只存储数据吧? 还存在哪些类型呢?

A: 还有索引页, 数据页, undo页, 插入缓冲, 自适应哈希索引, 锁信息, 数据字典信息等

Q: 对了, 既然前面说了, 缓冲池中都是按照页来管理的?

A: 是的, 按照页来管理, 而且每个页上头都会有一个页码, 这个页码使用哈希值计算出来的

LRU List, Free List和Flush List

Q: 前面我们说过, innoDB管理使用的是页, 这是数据结构, 但是没有算法也不好管理吧?

A: 是的, 所以需要其他别的策略, 比如LRU, 缓冲池中哪些页空闲, 哪些页需要刷新等

Q: 当我们的缓冲池慢了, 那么按照什么方法删除掉一部分页?

A: 使用最流行的LRU就可以, 按照页被使用的频率排序, 高频的不淘汰, 低频淘汰, 而且LRU并不仅仅只针对数据页, 而且mysql对LRU进行了优化, 设计了一个midpoint, 将新访问的页放到midpoint, 而所谓的midpoint指向的是中间位置, 而不是放在首部

Q: 这样做的好处是什么呢?

A: 有时候查询出来的数据可能并不是热点数据, 如果直接放在首部, 那么热点数据可能会被过早淘汰

重做日志缓冲

是什么?

mysql将修改存放到redo log buffer中, 然后再以一定的频率将buffer刷新到redo log中

Q: 那什么时候会触发刷新redo log?
A: 有三种情况

  • Master Thread每秒刷新一次
  • 事务提交时刷新
  • redo log buffer占用量达到1/2

重做日志的作用(redo log)

为了防止数据库实体宕机导致在缓存中的内容丢失, 所以采用了write ahead log策略, 即当事务提交时, 先写入日志, 然后在修改页. 如果数据库真的发生了宕机, 那么可以通过重做日志恢复数据, 这就是ACID中的D持久化的要求

重做日志是循环使用的, 所以如果出现一个循环后, 旧的数据将被代替, 所以可能都只丢失数据

checkpoint 技术

为什么需要checkpoint技术?

页发生改变, 变成脏页, 但是最终都需要刷新到磁盘中

但是如果每次发生改变都需要刷新, 将是低效的

Q: 那么什么情况下会触发checkpoint呢?
A: 这几个情况将会触发checkpoint

  • 缩短数据库的恢复时间
  • 缓冲池不够用时, 将脏页刷新到磁盘
  • 重做日志不可用时, 刷新脏页
  • LRU准备淘汰页时, 发现是脏页也会触发checkpoint

所以checkpoint的作用无非是将内存中的值存放到磁盘中, 不同之处在于每次刷新的页数量不同

checkpoint的类型

  • sharp checkpoint
  • fuzzy checkpoint

sharp checkpoint发生在数据库关闭时将所有脏页都刷新到磁盘中

fuzzy checkpoint将一部分脏页刷新, 每次都刷新一点

Q: 你没讲什么时候刷新fuzzy checkpoint?
A: 一般出现在这几种情况:

  • master thread checkpoint: 每秒或者每十秒的速度从缓冲池的脏页列表中刷新一定比例的页到磁盘高中, 是异步的
  • FLUSH_LRU_LIST checkpoint: 判断空闲页是否有100个, 如果没有则触发checkpoint, 将LRU列表尾端的页移除, 从5.6起, 差个检查被放在了一个单独的Page Cleaner线程中进行, LRU列表中可用页的数量为1024
  • Async./Sync Flush checkpoint: 重做日志不可用时, 强制将脏页刷新到磁盘中
  • Dirty Page too much checkpoint: 脏页数量过多, 触发checkpoint

InnoDB的功能

插入缓冲

聚集索引一般是按照主键顺序插入到, 但是辅助索引不是, 他会带来离散性导致速度变慢

类似于磁盘随机读写和redo log的顺序读写, 顺序读写效率快

如果辅助索引DML(curd)的次数过多, 可能导致效率变慢

此时让你设计你会怎么设计?

mysql给出的答案是引入 insert buffer(后续好像是change buffer)

将针对辅助索引的修改, 直接变为针对buffer缓存的修改, 然后按照一定的频率进行merge和同步

所以insert buffer只支持

  • 非聚集索引
  • 非唯一索引

因为在聚集索引下没有任何优势

并且 insert buffer还可以合并DML操作

两次写

double write

什么情况下发生double write?

从缓存中刷新到磁盘前, 先写入数据到double buffer中, 做一份备份

为什么?

防止页出现问题导致数据失效, 比如磁盘坏道等导致的数据页失效, 数据丢失

自适应哈希索引

自动根据情况生成哈希索引树,

效率最高

但是不能够使用返回查询等

异步IO

说白了就是AIO, 同步启动多个IO操作, 用户只需要等待结果就行了, IO交给 IO Thread去做

IO合并: 如果判断可行, 那么就会在一个IO操作中完成多个IO需要完成的工作

刷新邻接页

在刷新脏页到磁盘时, 会检查该页所在区的所有页, 刷新到磁盘中