共计 4899 个字符,预计需要花费 13 分钟才能阅读完成。
后台线程
Master Thread
核心的后台线程,主要负责将缓冲池的数据异步刷新到磁盘,保证数据的一致性,包括(脏页的刷新)、合并插入缓冲、(UNDO 页的回收)等
IO Thread
4 个 write、4 个 read、1 个 insert buffer、1 个 log
参数 innodb_read_io_threads 和 innodb_write_io_threads 可以进行设置。
Purge Thread
回收已经使用并分配的 UNDO 页。(从 Innodb 1.1 版本开始独立线程)
从 Innodb 1.2 版本开始,Innodb 支持多个 Purge Thread,进一步加快 UNDO 页的回收。同时由于 Purge Thread 需要离散的读取 UNDO 页,这样能更进一步利用磁盘的随机读取性能。
Page Cleaner Thread
在 Innodb 1.2 版本中引入,将之前版本中脏页的刷新操作都放入到单独的线程中完成。减轻 Master Thread 的工作以及用户查询线程的堵塞,进一步提高 InnoDB 存储引擎的性能。
内存
缓冲池
缓冲池的配置通过参数 innodb_buffer_pool_size 来设置。
show variables like 'innodb_buffer_pool_size';
缓冲池中缓存的数据页类型有:索引页、数据页、undo 页、插入缓冲(insert buffer)、自适应哈希索引、Innodb 存储的锁信息(lock info)、数据字典信息(data dictionary)等。
从 InnoDB 1.0.x 版本开始,允许有多个缓冲池示例。每个页根据哈希值平均分配到不同的缓冲池中。好处是减少数据库内部的资源竞争,增加数据库的并发处理能力。通过参数 innodb_buffer_pool_instances 来进行配置,默认为 1。
show variables like 'innodb_buffer_pool_instances';
LRU List、Free List 和 Flush List
数据库中的缓冲池通常是通过 LRU(Latest Recent Used,最近最少使用)算法进行管理的。即最频繁进行的页在 LRU 列表的前端,而最少使用的页在 LRU 列表的尾端。当缓冲池不能存放新读到的页时,将首先释放 LRU 列表中的尾端的页。
InnoDB 的缓冲池对传统的 LRU 算法做了一些优化。在 LRU 列表中还加入了 midpoint 位置。新读取到的页,虽然是最新访问的页,但并不是直接放入到 LRU 列表的首部,而是放入 LRU 列表的 midpoint 位置。这个算法在 InnoDB 存储引擎下称为 midpoint insertion strategy。默认情况下位置在 LRU 列表长度的 5 / 8 处。通过参数 innodb_old_blocks_pct 控制。
为什么不使用传统的 LRU 算法,如果直接将读取到的页放入到 LRU 的首部,那么某些 SQL 操作可能会是缓冲池中的页全部被刷出,从而影响缓冲池的效率。常见的这类操作为索引或数据的扫描操作。这类操作需要访问表中的许多页,甚至是全部的页,而这些页仅仅是这次操作需要的,并不是热点数据。
同时,InnoDB 使用参数 innodb_old_blocks_time 管理页读取到 midpoint 位置后等待多久才会被加入到 LRU 的热端(midpoint 之前的称为 new 列表热端,之后的列表称为 old 列表)。可以通过下面的设置尽可能使 LRU 列表中热点数据不被刷出。
set global innodb_old_blocks_time = 1000;
如果预估自己的热点数据不止 63%,那么通过下面的设置来减少热点页的刷出,但是还是会被刷出。
set global innodb_old_blocks_time = 20;
当数据库刚启动时,LRU 列表是空的,即没有任何的页。这是页都存放在 Free 列表中,当需要从缓冲池中分页时,首先从 Free 列表中查找是否可用的空闲页,若有则从 Free 列表中删除,放入到 LRU 列表中。否则,根据 LRU 算法,淘汰 LRU 列表末尾的页,分配给新的页。
当页从 LRU 列表的 old 部分加入到 new 部分时,称此时发生的操作为 page made young,而因为 innodb_old_blocks_time 的设置而导致没有从 old 部分变为 new 部分的操作称为 page not made young。
通过命令查看 LRU 列表及 Free 列表的使用状况。
show engine innodb status\G;
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137428992
Dictionary memory allocated 417853
Buffer pool size 8191
Free buffers 7724
Database pages 467
Old database pages 0
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 433, created 34, written 36
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: 467, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
在 LRU 列表中的页被修改后,称该页为脏页,即缓冲池中的页和磁盘上的页的数据产生了不一致。这是数据库会通过 CHECKPOINT 机制将脏页刷新回磁盘,而 Flush 列表中的页即为脏页列表。注意:脏页即存在于 LRU 列表中,也存在于 Flush 列表中。LRU 列表管理缓冲池中页的可用性,Flush 列表用来管理页刷新回磁盘,二者不影响。
重做日志缓冲
InnoDB 存储引擎首先将重做日志信息放入重做日志缓冲区,然后按照一定的频率将其刷入到重做日志文件。由参数 innodb_log_buffer_size 控制,默认为 8M。
重做日志在下面三种情况下会把重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中。
- Master Thread 每一秒将重做日志缓冲刷新到重做日志文件。
- 每个事物提交时会将重做日志缓冲刷新到重做日志文件。
- 当重做日志缓冲池剩余空间小于 1 / 2 时,重做日志缓冲刷新到重做日志文件。
Checkpoint 技术
如果使用 Update 或 Delete 操作,改变了页中的记录,那么此时页是脏的,即缓冲池中的页的版本比磁盘的新。数据库需要将新版本的数据从缓冲池刷新到磁盘。
但是不能每一次变化,都将新页的数据刷新到磁盘。一方面开销太大,再就是如果在刷新过程中发生了宕机,那么数据不能恢复,为了避免发生数据丢失的问题,数据库普遍采用 Write Ahead Log 策略,即当事务做日志提交时,先写重做日志,再修改页。如果发生宕机,通过重做日志文件来完成数据的恢复。
如果缓冲池缓存数据库中所有的数据,而且重做日志能够无线的增大,那么不需要将缓冲池中的新版本刷新回磁盘。因为发生宕机的时候通过重做日志文件就可以恢复整个数据库系统中的数据到宕机发生的时刻。但是这需要两个条件:1. 缓冲池可以缓存数据库所有的数据,2. 重做日志能够无限增大。这几乎是不可能的(原因就不说了 …),而且就算满足条件,宕机数据库恢复的时间会非常久。(说到底,就是得用 Checkpoint 技术,实现分批定时将数据刷新到磁盘)
Checkpoint(检查点)技术目的是解决如下问题:
- 缩短数据库的恢复时间;当发生宕机时,数据库不需要重做所有的日志,因为 Checkpoint 之前的页都已经刷新到磁盘。
- 缓冲池不够用时,将脏页刷新到磁盘;当缓冲池不够用时,根据 LRU 算法会溢出最近最少使用的页,若此页为脏页,那么需要强制执行 Checkpoint,将脏页也就是新版本刷回磁盘。
- 重做日志不可用时,刷新脏页。所谓的不可用,是指重做日志的设计是循环使用的,并不是让其无限增大的,重做日志可以被重用的部分是指这些重做日志已经不再需要(应该是已经被刷回到磁盘),发生宕机时,恢复操作不需要这部分重做日志,因此这部分可以被覆盖重用,也即是上面说的重做日志不可用,但是如果想要使用不可用的重做日志,就需要强制使用 Checkpoint,将缓冲池中的页至少刷新到当前重做日志的位置( 这里和下面的分析有一些冲突 )。
对于 InnoDB 存储引擎,通过 LSN 来标记版本的。而 LSN 是 8 个字节的数字,每个月有 LSN,重做日志有 LSN,Checkpoint 也有 LSN。
通过命令查看 LSN。
show engine innodb status\G;
LOG
---
Log sequence number 33646077360
Log flushed up to 33646077360
Last checkpoint at 33646077360
0 pending log writes, 0 pending chkp writes
49687445 log i/o's done, 1.25 log i/o's/second
在 InnoDB 存储引擎有两种 Checkpoint,分别是:
- Sharp Checkpoint
- Fuzzy Checkpoint
Sharp Checkpoint 发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作方式,即参数 innodb_fast_shutdown = 1。
在内部使用 Fuzzy Checkpoint 进行页的刷新,即只刷新部分脏页,而不是刷新所有的脏页回磁盘。
可以分为如下一些情况:
- Master Thread Checkpoint
- FLUSH_LRU_LIST_Checkpoint
- Async/Sync Flush Checkpoint
- Dirty Page too much Checkpoint
对于 Master Thread Checkpoint,差不错以每秒或者每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘。这个过程是异步的,即此时 InnoDB 可以进行其他操作。
FLUSH_LRU_LIST_Checkpoint 是因为 InnoDB 需要保证 LRU 列表中需要有差不多 100 个空闲页可使用。如果没有 100 个空闲页,那么 InnoDB 会将 LRU 列表尾端的页移除。如果这些页中有脏页,那么需要进行 Checkpoint,而这些页来自 LRU 列表。从 MySql 5.6 版本开始,这个检查工作被放在一个单独的 Page Cleaner 线程中进行,并且用户可以通过参数 innodb_lru_scan_depth 控制 LRU 列表中可用页的数量,默认为 1024。
Async/Sync Flush Checkpoint 指的是重做日志不可用的情况,这是需要强制将一些页刷新回磁盘( 这里和上面的分析有一些冲突,希望可以交流学习 ),而此时是从脏页列表中选取的。这包含一系列的触发的判断条件,目的是为了保证重做日志的循环使用的可用性。在 InnoDB 1.2.x 版本之前,Async Flush Checkpoint 会阻塞发现问题的用户查询线程,而 Sync Flush Checkpoint 会阻塞所有的用户查询线程,并且等待脏页刷新完成。从 InnoDB 1.2.x 版本以后,也就是 MySQL 5.6 版本,这部分被放到单独的 Page Cleaner Thread 中,不会阻塞用户查询线程。
最后一种 Checkpoint 的情况是 Dirty Page too much,即脏页数据太多,导致强制执行 Checkpoint。其目的是保证缓冲池中有足够可用的页,由参数 innodb_max_drity_pages_pct 控制,目前默认值 75,即 75%。
本文永久更新链接地址 :http://www.linuxidc.com/Linux/2017-01/139359.htm