MySQL InnoDB 存储引擎.
一、InnoDB 体系架构
InnoDB 存储引擎有多个内存块,可以认为这些内存块组成了一个大的内存池,负责如下工作:
- 维护所有进程/线程需要访问的多个内部数据结构。
- 缓存磁盘上的数据,方便快速的读取,同时对磁盘文件的数据修改之前在这里进行缓存。
- 重做日志(redo log)缓冲。
后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。同时将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的情况下 InnoDB 能恢复到正常运行状态。
通过 SHOW ENGINE INNODB STATUS 可以观察到 INNODB 存储引擎的运行情况。
SHOW ENGINE INNODB STATUS
二、内存池
缓冲池简单来说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。缓冲池的大小直接影响着数据库的整体性能,可以通过配置参数 innodb_buffer_pool_size 来设置。
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'
并且 InnoDB 允许有多个缓存池实例,每个 PAGE 根据哈希值平均分配到不同缓冲池实例中,这样做的好处是减少数据库内部的资源竞争,增加数据库的并发处理能力,可以通过配置参数 innodb_buffer_pool_instances 来设置。
SHOW VARIABLES LIKE 'innodb_buffer_pool_instances'
通常来说,数据库中的缓存池是通过 LRU(Lastest Recent Used,最近最少使用)算法来进行管理的,缓存池的默认单位是 "页",一页默认 16 KB。
从 InnoDB 1.2 版本开始,可以通过 INNODB_BUFFER_POOL_STATS 来观察缓存池的运行状态。
SELECT
POOL_ID,
HIT_RATE '缓存池的命中率',
PAGES_MADE_YOUNG AS '缓存池 old 部分加入到 new 部分的次数',
PAGES_NOT_MADE_YOUNG '缓存池 new 部分加入到 old 部分的次数'
FROM information_schema.INNODB_BUFFER_POOL_STATS
重做(REDO)日志缓存是用来存放重做日志信息的,一般不需要设置得过大,因为一般情况下每一秒钟都会将重做日志缓存刷新到日志文件,一般设置 8MB 就足以满足绝大部分得应用,可通过 INNODB_LOG_BUFFER_SIZE 参数控制;参数 innodb_flush_log_at_trx_commit 用来控制重做日志刷新到磁盘的策略,默认为 1,表示事务提交时必须调用一次 fsync 操作。
SHOW VARIABLES LIKE 'INNODB_LOG_BUFFER_SIZE'
当前事务数据库系统普遍都采用了 WriteAhead Log 策略,即当事务提交时,先写重做日志,再修改页。因此,重做日志的作用是对数据库系统中的数据进行恢复(当数据库系统异常宕机的时候)。
额外内存池是用来分配一些数据结构本身的内存,例如缓冲池中的帧缓存(frame buffer)、缓冲控制对象(innodb_buffer_pool)。
三、后台线程
Master Thread 是一个非常核心的后台线程,主要负责将缓存池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓存(INSERT BUFFER)、UNDO 页的回收等。
IO Thread 的工作主要是负责 IO 请求的回调处理(InnoDB 存储引擎中大量的使用了 AIO 来处理写 IO 请求)。
SHOW VARIABLES LIKE 'INNODB_%io_threads'
PurgeThread 是在 InnoDB 1.1.x 版本中引入的。用来回收已经使用并分配的 undo 页以减轻 Master Thread 的工作量 ,因为事务被提交后,其所使用的 undolog 可能不再需要。(由于 delete 和 update 操作可能并不直接删除原有的数据,而只是将 delete flag 设置为 1,这样设计是因为 InnoDB 存储引擎支持 MVCC,而 purge 用于最终完成 delete 和 update 操作)
SHOW VARIABLES LIKE 'INNODB_purge_threads';
# 用来设置每次 purge 操作需要清理的 undo page 数量
SHOW VARIABLES LIKE 'INNODB_purge_batch_size'
Page Cleaner Thread 是在 InnoDB 1.2.x 版本中引入的。其作用是将之前版本的脏页刷新操作放入到单独的线程中来完成以减轻 Master Thread 的工作量。
四、其他
InnoDB 存储引擎开创性地设计了 Insert Buffer(插入缓冲),对于非聚簇索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚簇索引页是否在缓冲池中,若在,则直接插入;若不在,则先放入到一个 Insert Buffer 对象中,然后再以一定的频率进行 Insert Buffer 和辅助索引页子节点的 merge(合并)操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚簇索引插入的性能。
doublewrite(两次写)由两部分组成,一部分是内存中的 doublewrite buffer,大小为 2MB,另一部分是物理磁盘上共享表空间(tablespace)中连续的 128个页,即2个区,大小同样是 2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过 memcpy 函数将脏页先复制到内存中的 doublewrite buffer,之后通过 doublewrite buffer 再分两次,每次 1MB 顺序地写入共享表空间(tablespace)的物理磁盘上,然后马上调用 fsync 函数,同步磁盘,避免缓冲写带来的问题。如果操作系统在将页写入磁盘的过程中发生了崩溃,在恢复过程中,InnoDB 存储引擎可以从共享表空间中的 doublewrite 中找到该页的一个副本,将其复制到表空间文件,再应用重做日志。
SHOW GLOBAL STATUS LIKE 'INNODB_dblwr%'
自适应哈希索引(Adaptive Hash Index,AHI)是指 InnoDB 存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。AHI 是通过缓冲池的 B+ 树页构造而来,因此建立的速度很快,而且不需要对整张表构建哈希索引。
在 InnoDB 存储引擎中,采用异步IO(Asynchronous IO,AIO)的方式来处理磁盘操作。
SHOW VARIABLES LIKE 'innodb_use_native_aio'
InnoDB 存储引擎还提供了 Flush Neighbor Page(刷新邻接页)的特性。其工作原理为:当刷新一个脏页时,InnoDB 存储引擎会检测该页所在区的所有页,如果是脏页,那么一起进行刷新,这样做的操作显而易见,通过 AIO 可以将多个 IO 写入操作合并为一个 IO 操作。(固态硬盘具有超高的 IOPS)
SHOW VARIABLES LIKE 'innodb_flush_neighbors'