innodb存储引擎关键点
[TOC]
缓存池
- 索引页
-
数据页
-
undo页
-
插入缓存
-
自适应哈希索引
-
innodb引擎的锁信息
-
数据字典信息
mysql> use information_schema; Database changed
mysql> select pool_id,pool_size, free_buffers,database_pages from innodb_buffer_pool_stats \G;
*************************** 1. row ***************************
pool_id: 0
pool_size: 8192
free_buffers: 1024
database_pages: 7160
1 row in set (0.00 sec)
缓存池管理
- LRU(默认)
- 频繁使用的在LRU列表前端
- 优化:新访问的页不是放在列表前端,而是放在Midpoint。避免扫描全表的sql,将热点数据挤压刷出
- midpoint之前称为新表(new sublist),表示热点数据
- midpoint之前称为旧表(old sublist)
- unzip_LRU
- 管理非16KB的页
- LRU列表中包含unzip_LRU列表中的页
在LRU列表中的页被修改后,成改页为脏页(dirty page)。
数据块通过checkpoint机制将脏页刷新回磁盘
Flush列表中的页为脏页列表。脏页同时存在于LRU列表和Flush列表
重做日志缓存(redo log buffer)
- 先将redo信息放入redo log buffer
- 然后按照一定频率刷新到redo log file(一般1秒一刷)
- flush redo log
- master thread 一秒刷redo log buffer to redo log
- 每个事务提交时
- 当redo log buffer 剩余空间少于1/2时
额外的内存池
- 一些数据结构本身的内存优先从(额外的内存池)中分配
- 当(额外的内存池)内存不足,会从缓存池中分配
checkpoint技术
面对的难题
- 缓存池不可能缓存数据库所有数据
- redo log不可能无限大
用于解决
- 缩短数据库恢复时间
- 缓冲池不够用时,将脏页刷新到磁盘
- 重做日志不可用时,刷新脏页
通过LSN(log sequence Number)来标记版本的。
- LSN是8字节的数字
- 每个页有LSN
- redo log有LSN
- checkpoint 有LSN
有两种checkpoint
- Sharp checkpoint,数据库关闭时,将所有脏页刷新回磁盘
- Fuzzy checkpoint,数据库运行中使用,进行页的刷新,只刷新一部分脏页回到磁盘
- master Fuzzy checkpoint。异步定时从缓冲池刷脏页到磁盘
- FLUSH_LRU_LIST Checkpoint 为了保证LRU LIST有差不多100个空闲页可供使用,Page Cleaner线程中执行
- Async/Sync flush Checkpoint.
- dirty page too much Checkpoint
MASTER THREAD
- 具有最高的线程优先级
- 由多个loop循环组成
- 主循环(loop):每秒操作和每十秒操作
- 后台循环(backgroup loop)
- flush loop
- 暂停循环(suspend loop)
loop
每秒操作
- 刷redo log buffer到磁盘,即使这个事务还没有提交(总是)
解析了为什么再大的事务commit都很快
-
合并插入缓冲(可能)
只有io压力很低,前一秒的io总数<5,才会执行。
-
至多刷100个Innodb 缓冲池中的脏页到磁盘(可能)
当前缓存池中的脏页比例buffer_get_modified_ratio_pct大于配置值
-
当前没有用户,切换到backupgroup loop状态(可能)
每十秒操作
- 刷100个Innodb 缓冲池中的脏页到磁盘(可能)
- 合并至多5个插入缓冲(总是)
- 刷redo log buffer到磁盘(总是)
- 删除无用的undo页(总是)
- 刷100或者10个Innodb 缓冲池中的脏页到磁盘(总是)
backupgroup loop
- 删除无用的undo页(总是)
- 合并20个插入缓冲(总是)
- 跳回主循环(总是)
- 不断刷100个Innodb 缓冲池中的脏页到磁盘(总是)(可能跳转到flush loop中完成)
InnoDB1.0.x之后
1.0.x版本之前,Master Thread 有了一定的hard coding。随着硬件性能日新月异,这对编码方式不能很好的匹配不同性能的硬件。
- 使用配置代替hard coding
- 增加了一些参数自适应
InnoDB1.2.x之后
刷新脏页的操作从Master Thread分离到一个独立的Page Cleaner Thread。减轻Master Thread 的工作,同时进一步提高了系统的并发性。
Innode关键特征
- insert buffer
- double write
- adaptive hash index
- async IO
- Flush Neighbor Page
insert buffer
insert buffer 和数据页一样,也是物理页的一个组成部分,用于非唯一辅助索引的插入操作。
每一张表上除了有一个聚集索引(primary key),有可能还有多个非聚集的辅助索引。
在进行插入操作的时候,数据页还是按照primary key进行顺序存放的。但是对于非聚集索引叶子节点的插入不再是顺序的。这时需要离散的访问非聚集索引页,由于随机读取的存在,进而影响了插入性能。
mysql引入insert buffer,对非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若是在,则直接插入;若不在,则先放入到一个insert buffer对象中。
然后再以一定的频率和情况进行insert buffer和辅助叶子节点的merge操作。
辅助索引不能是唯一的,因为在插入缓冲时,数据库为了避免离散读取的情况发生,并不去查找索引页来判断插入的记录的唯一性。
Change Buffer
Innodb 1.0.x版本开始引入Change Buffer,可将其视为Insert BUFFER的升级。Innodb可以对DML操作–INSERT、DELETE、UPDATE都进行缓冲,它们分别是:
- Insert Buffer
- Delete Buffer
- Purge Buffer
Innodb 提供参数Innodb_change_bufferiing,用于开启各种Buffer的选项
对一条记录进行UPDATE操作可能分为连个过程:
- Delete buffer复制将记录标记为删除
-
Purge buffer负责将记录真正的删除
Insert Buffer的内部实现
-
使用b+树
mysql 4.1之前每张表有一颗insert buffer B+树。现在全局只有一颗Insert Buffer B+树
-
它保存在共享空间中(ibdata1),因此,试图通过独立表空间ibd文件恢复表中数据时,往往会导致CREATE TABLE失败。这是因为表的辅助索引中的数据可能还在Insert Buffer中。所以通过idb文件进行恢复后,还需要进行REPAIR TABLE操作来重建表上所有的辅助索引。
-
由叶子节点和非叶子节点组成
-
非叶子节点存放的是查询的search key(键值),结构如下
space |
marker |
offset |
表示待插入记录所以在表的表空间id |
用于兼容向后兼容 |
页所在的偏移量 |
- 当一个辅助索引要插入到页(space,offset)时,如果这个页不再缓冲池中,那么Innodb首先根据上述规则构造一个search key,接下来查询insert buffer,然后将记录插入到insert buffer B+树的叶子节点中。
space |
:marker |
offset |
metadata |
– |
– |
– |
– |
|
|
|
|
|
|
|
|
-
metadata中IBUF_REC_OFFSET_COUNT(2字节的整数)用来排序每个记录进入insert buffer的顺序
-
insert buffer bitmap:一个特殊的页,用来标记每个辅助索引页的可用空间,保证每次merge insert buffer页必须成功。
-
每个insert buffer bitmap用来追踪16384个辅助索引页,也就是256个区(Extent)
-
每个辅助索引页在insert buffer bitmap中存储结构
merge insert buffer
- 辅助索引页被读取到缓冲池中
- 检查insert buffer bitmap页,确认该辅助索引页是否存放在insert buffer B+树中
- 存在,则将B+树中该页的记录插入到该辅助索引页中。对该页多次的记录操作通过一次合并到原有的辅助索引页
- insert buffer bitmap页追踪到该辅助索引页已无可用空间时。(小于1/32页,会强制进行一次合并操作)
- Master Thread中每秒或者每十秒的合并操作。
两次写
避免数据库宕机时,出现部分写失效(partial page write)。例如某个页16KB,只写了4KB就发生了宕机。
通过redo log进行数据恢复,由于redo日志记录的是对页的物理操作(如偏移量800,写‘aaa’记录),需要基于页是好的。如果这个页本身已经发生了损坏,无法通过redo 日志进行重放。
doublewrrite:当写入失效发生时,先通过页的副本还原该页,再进行重做。
doublewrrite由两部分组成:
- 内存中的doublewrrite buffer,2MB
- 物理磁盘上共享表空间中连续的128个页,即2个区(extent),2MB
在对缓冲池进行flush时,并不是通过写磁盘,而是通过memcpy函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次写1MB顺序写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘避免缓冲写带来的问题(顺序写)。doublewrite buffer中的页写入到各个表空间文件中。(离散写)
自适应哈希索引(AHI)
自适应哈希索引:Innodb存储引擎会监控对表上各索引的查询,如果观察到建立哈希索引可以带来速度提升,则建立哈希索引。
AHI是通过缓冲池的B+树页构造,不需要对整张表构建哈希索引。
条件:
- 访问模式一样,即查询的条件一样。
- 以该模式访问了100次
- 页通过该模式访问了N次,其中(N=页中记录*1/16)。
异步IO
Innodb支持AIO。
- read ahead 方式的读取(借助aio,将所有io发送完毕后,然后等待所有IO操作的完成)
- 脏页的刷新(可以将多个连续io进行IO Merge)
刷新邻近页
当刷新一个脏页时,Innodb会检测改页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。然后通过AIO将多个io写入操作合并成一个IO操作。
缺点:
- 可能会将不怎么脏的页进行了写入,而该页之后很快又会变成脏页。
- 固态硬盘有着较高的IOPS,不需要这个特性。