MySQL技术内幕 InnoDB存储引擎 第2版 封面
MySQL技术内幕 InnoDB存储引擎 第2版 封面

2.3 InnoDB 体系结构

InnoDB 存储引擎体系结构

2.3.1 后台线程

1. Master Thread

核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲(Insert Buffer)、UNDO页的回收等。

2.IO Thread

大量使用了 AIO(Async IO)来处理 IO 请求,这样可以极大提高数据库的性能。而 IO Thread 的工作主要是负责这些 IO 请求的回调(call back)处理。一共有四种:write、read、insert buffer和log

执行 SHOW ENGINE INNODB STATUS 命令获取 InnoDB 的详细信息。

--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)

可见,当前有一个 insert buffer 线程,一个 log 线程,4 个读线程和 4 个写线程。其中 read 和 write 的线程可数可以使用 innodb_read_io_threads 和 innodb_write_io_threads 来设置。

3. Purge Thread

比如当事物提交后,其所使用的 undo log 可能不再需要,因此需要 Purge Thread 来回收已经使用并分配的 undo 页

这里简单提一下 undo 页和 redo log。通过这样的方式进行记忆,UC 和 RAD。故 Undo 页 是用来保证 C(一致性),Redo log 是用来保证A(原子性)和D(持久性)。undo 页 会存放事物执行过程中的日志,用来保证事物的一致性。

4.Page Cleaner Thread

用于将脏页的数据刷新到磁盘

2.3.2 内存

1.缓冲池

缓冲池是一块内存区域,通过快速地读取缓冲池的内容来弥补读取磁盘慢的问题。

InnoDB 缓冲池数据内容

缓冲池缓冲的数据页类型有:索引页、数据页、插入缓冲、 自适应哈希索引(adaptive hash index) 、undo 页、重做日志缓冲(redo log buffer)、额外内存池等。

  • 通过 show variables like ‘innodb_buffer_pool_size’ 来查看缓冲池大小。
  • 通过 show variables like ‘innodb_buffer_pool_instances’ 来查看缓冲池个数。

2. LRU List、Free List 和 Flush List

----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137363456
Dictionary memory allocated 634482
Buffer pool size   8192
Free buffers       6151
Database pages     2025
Old database pages 727
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 2, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 834, created 1191, written 556091
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 2025, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]

缓冲池信息

LRU(Latest Recent Used),即最少使用策略来管理缓冲池的数据页。他是一个 双向链表与哈希的数据结构,简单来说链表头部保留的是较新(热)的数据页,尾部保留的是较旧(冷)的数据页。当有新的数据页插入时,将数据页插入到头部。若数据页数量超过容量上限时,即将尾部数据页丢掉。

在 InnoDB 中,LRU 算法被做了一些优化。比如,在有新数据页需要插入时,虽然是最新访问的页,但是并不是直接放入到 LRU 列表的首部,而是放到 LRU 列表的 midpoint 位置 —— midpoint insertion strategy

之所以这样做,是为了避免在诸如索引或者数据的扫描操作时,将 LRU 中原本“真正”的热数据页刷掉。

同时,InnoDB 存储引擎引入了另一个参数来进一步管理 LRU 列表。这个参数是 innodb_old_blocks_time,用于表示页读取到 mid 位置后需要等待多久才能被加入到 LRU列表的热端。

LRU 列表用来管理已经读取到的页,但当数据库刚启动时,LRU 列表是空的,即没有任何的页。这时页都存放在 Free 列表中。但需要从缓冲池中分配页时,首先从 Free 列表中查找是否有可用的空闲页,若有则将该页从 Free 列表中删除,放入到 LRU 列表中。否则,根据 LRU 算法,淘汰 LRU 列表末端的页,将该页空间分配给新的页。

Free buffers 表示当前 Free 列表中页的数量,Database pages 表示 LRU 列表中页的数量。可能的情况是 Free buffers 与 Database pages 之和不等于 Buffer pool size。正如上面的缓冲池信息那样,因为缓冲池中的页还可能被分配给自适应哈希、插入缓冲等页,而这部分页不需要 LRU 算法进行维护。

在 LRU 列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘的上的页的数据产生了不一致。这时数据库会通过 CheckPoint 机制将脏页刷新到磁盘,而 Flush 列表中的页即为账页列表。注意,账页会既存在于 LRU 列表中,页存在于 Flush 列表中。LRU 列表用来管理缓冲池中页的可用性,Flush 列表用来管理将页刷新会磁盘,两者互不影响。

3. 重做日志

InnoDB 存储引擎首先将重做日志信息放入到这个缓冲区,然后按照一定的频率刷新到磁盘上的重做日志文件。刷新时机如下:

  • Master Thread 每一秒将重做日志缓冲刷新到重做日志文件
  • 每个事物提交时
  • 当重做日志缓冲池剩余空间小于 1 / 2 时

参考

欢迎分享,引用。若转载,请联系我,谢谢配合。
本文地址:https://qoogle.top/chapter-two-of-mysql-technology-insider-innodb-storage-engine-2nd-edition/
最后修改日期:2020年4月24日

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。