从MySQL 5.5 版本开始默认使用InnoDB 作为引擎,它擅长处理事务,具有自动崩溃恢复的特性,在日常开发中使用非常广泛。
一、InnoDB 内存结构
内存结构主要包括Buffer Pool、Change Buffer、Adaptive Hash Index 和 Log Buffer 四大组件。
- Buffer Pool:缓冲池,简称BP。BP 以Page 页为单位,默认为16k,BP 的底层采用链表数据结管理Page。在InnoDB 访问表记录和索引时会在Page 页缓存,以后使用可以减少磁盘IO 操作,提升效率。
Page 管理机制
Page 根据状态可以分为三种类型:
free page: 空闲page, 未被使用
clean page: 被使用page, 数据没有被修改过
dirty page: 脏页, 被使用page, 数据被修改过, 页中数据
和磁盘数据产生了不一致
针对上述三种page 类型, InnoDB 通过三种链表结构来维护和管理
free list: 表示空闲缓冲区,管理free page
//
flush list: 表示需要刷新到磁盘的缓冲区,管理dirty
page, 内部page 按修改时间排序。脏页即存flush 链表,
也在LRU 链表,但是两种互不影响,LRU 链表负责管理page
的可用性和释放,而flush 链表负责管理脏页和刷盘操作。
//
lru list: 表示正在使用的缓冲区,管理clean page 和
dirty page, 缓冲区以midpoint 为基点,前面链表成为new
列表区,存放经常访问的数据,占63%; 后面的链表成为old
列表区,存放使用较少数据,占37%。
- 改进型LRU 算法维护
普通LRU: 末尾淘汰法,新数据从链表头部加入,释放空间时从末尾
淘汰。
//
改进LRU:链表分为new 和old 两个部分,加入元素时并不是从表头
加入,而是从中间midpoint 位置插入,如果数据很快被访问,那么
page 就会向new 列表头部移动,如果数据没有被访问,会逐步向old
尾部移动,等待淘汰。
//
每当有新的page 数据读取到buffer pool 时,InnoDB 引擎就会判
断是否有空闲页,空闲页是否足够,如果有就将free page 从free
list列表删除,放入到LRU 列表中。没有空闲页,就会根据LRU 算法
淘汰LRU 链表默认的页,将内存空间释放分配给新的页。
- Buffer Pool 配置参数
// 查看page 页大小
show variables like '%innodb_page_size%';
// 查看lru list 中old 列表参数
show variables like '%innodb_old%'
// 查看buffer pool 参数
show variables like '%innodb_buffer%'
建议: 将innodb_buffer_pool_size 设置为总内存的60%-80%,innodb_buffer_pool_instances 可以设置为多个,这样可以避免缓存争夺。
-
配置Buffer Pool 的大小
当服务器正运行时,用户可以离线(启动时)或在线配置InnoDB缓冲池大小。这部分描述的行为适用这两种方法。当增加或减少innodb_buffer_pool_size时,该操作按照数据块(chunks)执行。数据块的大小通过innodb_buffer_pool_chunk_size配置选项进行定义,该选项默认为128M.
缓冲池大小必须总是等于innodb_buffer_pool_chunk_size* innodb_buffer_pool_instances或为其倍数。如果将innodb_buffer_pool_size配置为不等于innodb_buffer_pool_chunk_size *
innodb_buffer_pool_instances或不为其倍数,则缓冲池大小自动调整为等于innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances或不小于指定缓冲池大小的该乘积的倍数。
-
Change Buffer: 写缓存区,简称CB。在进行DML 操作的时候,如果没有相应的Page 数据,并不会立即将磁盘页加载到缓冲区,而是在CB 记录缓冲变更,等未来数据被读取时,再将数据合并恢复到BP 中。
ChangeBuffer 占BufferPool 空间,默认占25%,最大允许占50%,可以更根据读写业务量来进行调整。
参数innodb_change_buffer_max_size。
当更新一条记录时,该记录在Buffer Pool 存在,直接在Buffer Pool 修改,一次内存操作,如果该记录在BufferPool 不存在(没有命中),会直接在Change Buffer 进行一次内存操作,不用再去磁盘查询数据,避免一次磁盘I/O。当下次查询记录时,会先进行磁盘读取,然后再从Change Buffer 中读取信息合并,最终载入Buffer Pool 中。
写缓冲区,仅适用于非唯一普通索引页,为什么?
如果在索引设置唯一性,在进行修改时,InnoDB 必须要做唯一性校验,因此必须查询磁盘,做一次I/O 操作。会直接将记录查询到Buffer Pool 中,然后在缓冲池修改,不会在Chaneg Buffer 操作。 -
Adapative Hash Index: 自适应哈希所有,用于优化对BP 数据的查询。InnoDB 存储引擎会监控对表索引的查找,如果观察到建立哈希索引可以带来速度的提升,则建立哈希索引,所以称之为自适应。InnoDB 存储引擎会根据访问的频率和模式来为某些页建立哈希索引。
-
Log Buffer: 日志缓冲区,用来保存要写入磁盘上log 文件(Redo log/ undo)数据,日志缓冲区的内容定期刷新到磁盘log 文件中。日志缓冲区默认16M, 缓冲区满时也会自动将其刷新到磁盘,当遇到BLOG 或多行更新的大事务操作时,增加日志缓冲区可以节省磁盘I/O。
Log Buffer 主要是用于记录InnoDB 引擎日志,在DML 操作时会产生Redo 和 Undo 日志。
Log Buffer 空间满了,会自动写入磁盘。innodb_flush_log_at_trx_commit 参数控制日志刷新行为,默认为1
0:每隔1秒写日志文件和刷盘操作(写日志文件Log Buffer --> OS Cache, 刷盘 OS cache --> 磁盘文件),最多丢失1秒的数据
1:事务提交,立即写日志文件和刷盘,数据不丢失,但是会频繁IO 操作
2:事务提交,立即写日志文件,每隔1秒进行刷盘操作