2.1 InnoDB存储引擎概述
是OLTP应用核心表的首选存储引擎
是第一个支持ACID事务的MySQL存储引擎
特点:行锁设计、支持MVCC、支持外键、提供一致性非锁定读、最有效的利用及使用内存和CPU
高性能、高可用、高可扩展
2.2 InnoDB存储引擎的版本
2.3 InnoDB体系架构
-
InnoDB存储引擎有多个内存块,组成了内存池
-
维护所有进程/线程需要访问的多个内部数据结构
-
缓存磁盘上的数据,方便快速读取,同时在对磁盘文件的数据修改之前在这里缓存
-
重做日志(redo log)缓冲
-
-
后台线程
-
负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据
-
将已修改得到数据文件刷新到磁盘文件
-
保证在数据库发生异常的情况下InnoDB能恢复到正常运行状态
-
2.3.1 后台线程
不同的后台线程,负责处理不同的任务
-
Master Thread
- 将缓冲池中的数据异步刷新到磁盘,保证数据的一致性(脏页的刷新、合并插入缓冲、UNDO页的回收)
-
IO Thread,使用命令SHOW ENGINE INNODB STATUS观察IO Thread
-
InnoDB使用大量AIO(Async IO)处理IO请求,负责IO请求的回调
-
1.0版本,使用innodb_file_to_threads参数增大IO thread
-
write
-
read
-
insert buffer
-
log IO thread
-
-
1.0.x开始。使用innodb_read_io_threads和innodb_write_to_threads参数进行设置
-
read thread增大为4个
-
wrute thread增大为4个
-
-
-
Purge Thread
-
回收已经使用并分配的undo页,减轻原Master Thread的工作
-
设置四个Purge Thread,加快undo页的回收,进一步利用磁盘的随机读取性能
-
-
Page Cleaner Thread
- 将之前版本中脏页的刷新操作都放入到单独的线程中完成,减轻原Master Thread的工作以及对于用户查询线程的阻塞
2.3.2 内存
-
缓冲池(协调CPU速度与磁盘速度的鸿沟)
-
通过参数innodb_buffer_pool_size设置大小
-
通过参数innodb_buffer_pool_instance设置缓冲池实例数量
-
一个很大的内存区域,存放各种类型的页
-
-
LRU List、Free List和Flush List(对内存区域进行管理)
-
InnoDB将LRU List优化,使用midpoint insertion strategy
-
通过参数innodb_old_blocks_pct设置midpoint位置,默认37%,LRU列表尾端37%处(LRU列表的5/8处)
-
通过参数innodb_old_blocks_time设置页读到mid位置多久后加入LRU列表的热端
-
unzip_LRU
-
-
数据库刚启动时,LRU列表为空,页都存放在Free List中,当需要从缓冲池中分页时,首先从Free List中查找是否有可用的空闲页,若有则将该页从Free中删除放入LRU
-
LRU中的页被修改后,称该页未脏页,数据库通过CHECKPOINT机制将脏页刷新回磁盘,脏页既存在于LRU中,也存在于Flush List中。LRU用来管理缓冲池中页的可能性,Flush用来管理将页刷新回磁盘
-
-
重做日志缓冲(按一定频率刷新至重做日志文件)
-
通过参数innodb_log_buffer_size设置,默认8MB
-
额外的内存池
-
2.4 CheckPoint技术
Write Ahead Log策略(先写重做日志,再修改页),避免发生数据丢失
-
作用
-
缩短数据库的恢复时间
-
缓冲池不够用时,将脏页刷新到磁盘
- LRU算法溢出最近最少使用的页,若为脏页,则强行执行Checkpoint
-
重做日志不可用时,刷新脏页
- 当前事务数据库系统对重做日志的设计是循环使用,当数据库发生宕机,数据库恢复操作不需要的重做日志可以被覆盖利用,需要使用的重做日志,强行执行Checkpoint,将缓冲页刷新至当前重做日志的位置
-
LSN (Log Sequence Number) :8字节的数字-InnoDB使用LSN标记版本
CheckPoint发生的时间、条件和脏页的选择很复杂
-
分类
-
Sharp CheckPoint:数据库关闭时将所有脏页刷新回磁盘(参数innodb_fast_shutdown=1)
-
Fuzzy CheckPoint:只刷新一部分脏页
-
Master Thead CheckPoint:异步以每秒或者每十秒的速度从缓冲页中刷新一定比例的脏页回磁盘
-
FLUSH_LRU_LIST CheckPoint:需保证大概有100个空闲页可用
-
1.1.x版本之前,需检查是否有可用空间,若没有,则需将列表末尾的页移除,若移除的页中有脏页,则需进行CheckPoint
-
1.2.x版本之后,检查被放入单独的Page Cleaner线程中,使用参数innodb_lru_scan_depth控制可用页的数量,默认1024
-
-
Async/Sync Flush CheckPoint:保证重做日志的循环使用的可用性
-
Dirty Page too musc CheckPoint:保证缓冲池中有足够可用的页,使用参数innodb_max_dirty_page_pct,默认90%
-
-
2.5 Matser Thread工作方式
2.5.1 InnoDB1.0.x版本之前的Master Thread
Master Thread具有最高的线程优先级别,由多个loop组成
-
主循环(loop)
-
每1秒的操作
-
日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)
-
合并插入缓冲(可能)-- IO次数小于5时
-
至多刷新100个InnoDB的缓冲池的脏页到磁盘(可能)–缓冲页比列超过参数innodb_max_dirty_pages_pct,默认90%
-
如果当前没有用户活动,则切换到backgroup loop(可能)
-
-
每10秒的操作
-
刷新100个脏页到磁盘(可能)-- IO次数小于200时
-
合并至多5个插入缓冲(总是)
-
将日志缓冲刷新到磁盘(总是)
-
删除无用的Undo页(总是)–full purge操作,最多20个
-
刷新100个或者10个脏页到磁盘(总是)–缓冲页比列超过70,刷新100,小于70 ,刷新10
-
-
后台循环(backgroup loop)
-
删除无用的Undo页(总是)
-
合并20个插入缓冲(总是)
-
跳回到主循环(总是)
-
不断的刷新100个页直到符合条件(可能)
-
-
刷新循环(flush loop)
-
暂停循环(suspend loop)
-
伪代码
2.5.2 InnoDB1.2.x版本之前的Master Thread
硬编码限制了InnoDB对磁盘IO的性能
-
引入参数innodb_io_capacity,表示IO的吞吐量,默认200
-
对于刷新到磁盘页的数量,按照参数的百分比进行控制
-
在合并插入缓冲时,合并插入缓冲的数量为参数值的5%
-
在从缓冲区刷新脏页时,刷新脏页的数量为参数值
-
-
-
参数innodb_max_dirty_page_pct,默认90%–>75%
-
引入参数innodb_adaptive_flushing(自适应刷新),影响每秒刷新脏页的数量
- 使用函数buf_flush_get_desired_flush_rate判断脏页的刷新数量:通过产生重做日志的速度来决定
-
引入参数innodb_purge_batch_size:控制回收的Undo页的数量,默认为20
伪代码
2.5.3 InnoDB1.2.x版本的Master Thread
2.6 InnoDB关键特性
插入缓冲(Insert Buffer)、两次写(Double Write)、自适应哈希索引(Adaptive Hash Index)、异步IO(Async IO)、刷新邻接页(Flush Neighbor Page)
2.6.1 插入缓冲(Insert Buffer)–性能提升
1.Insert Buffer(插入)
Insert Buffer和数据页一样,也是物理页的一个组成成分
对于非聚集索引的插入操作,先判断插入的非聚集索引页是否存在缓冲池中,若在则直接插入,否则先放入一个Insert Buffer对象,再以一定的频率和情况进行Insert Buffer和辅助索引叶子结点的merge操作–通常能将多个插入合并到一个操作中(因为在一个索引页中)–大大提高了对非聚集索引插入的性能
使用Insert Buffer的前提条件:
-
索引是辅助索引
-
索引不是唯一的:插入缓冲时,数据库并不会去查找索引页来判断插入的记录的唯一性,如果查找,会有离散读取的情况,使Insert Buffer失去意义
使用命令SHOW ENGINE INNODB STATUS查看插入缓冲的信息
对于写密集的情况,Insert Buffer会占用过多的缓冲,使用参数IBUF_POOL_PER_MAX_SIZE对Insert Buffer的大小进行控制,=3,允许Insert Buffer使用1/3,=2,允许Insert Buffer使用1/2
2.Change Buffer(插入、删除或更新)
Innodb1.0.x版本引入,Insert Buffer的升级,适用对象:非唯一的辅助索引
InsertBuffer、Delete Buffer、Purge Buffer
UPDATE分两步:
-
1.Delete Buffer将标记记为删除
-
2.Purge Buffer将记录真正删除
使用参数innodb_chang_buffering开启各种Buufer:inserts、deletes、purges、changes(inserts+deletes)、all、none
Innodb1.2.x新版本引入参数innodb_change_buffer_max_size,控制Change Buffer最大使用内存数量,默认25,最多使用1/4的缓冲池内存空间,最大有效值50
3.Insert Buffer内部实现
B+树
MYSQL4.1之前,每张表一棵Insert Buffer B+树,之后全局只有一棵Insert Buffer B+树,负责对所有的表的辅助索引进行Insert Buffer,放在共享表空间中,默认为idbdata1
试图通过独立表空间ibd文件恢复表中数据,会导致CHECK TABLE失败,因为表的辅助索引中的数据可能还在Insert Buffer中,所以还需要进行REPAIR TABLE来重建表上的所有辅助索引
辅助索引要插入页时,若该页不再缓冲池内,那么InnoDB存储引擎
-
首先根据上述规则构造一个search key
-
然后查询Insert Buffer B+树
-
最后将这条记录插入到Insert Buffer B+树的叶子节点中
-
使用特殊的页Insert Buffer Bitmap来标记每个辅助索引页的可用空间,为了保证每次Merge Insert Buffer页必须成功
-
每个Insert Buffer Bitmap页用来追踪16384个辅助索引页,即256个区
- 每个辅助索引页在Insert Buffer Bitmap页中占用四位(bit)
- 每个辅助索引页在Insert Buffer Bitmap页中占用四位(bit)
-
每个Insert Buffer Bitmap页都在16384个页的第二个页中
-
-
4.Merge Insert Buffer
发生的情况
-
辅助索引页被读取到缓冲池时
- 若在执行正常的SELECT查询操作,需要检查Insert Buffer Bitmap页,然后确认该辅助索引页是否有记录存放于Insert Buffer B+树中,若有,则将Insert Buffer B+树中该页的记录插入到该辅助索引页中
-
Insert Buffer Bitmap页追踪到该辅助索引页已无可用空间时
- 追踪每个辅助索引页的可用空间,并至少有1/32页的空间,若插入辅助索引记录时检测到插入记录后可用空间小于1/32,则强制进行一个合并操作(强行读取辅助索引页)
-
Master Thread
- 根据参数srv_innodb_io_capacity的百分比决定真正要合并多少个辅助索引页(随机选取Insert Buffer B+树中的一个页,读取该页中的space及之后所需数量的页)
2.6.2 两次写(Double Write)–可靠性
-
避免因写入页时宕机导致的数据丢失问题
- 页在写入磁盘时操作系统发生了崩溃,在恢复过程中,InnoDB存储引擎从共享表空间中的doublewrite页中找到该页的副本,将其复制到表空间文件,再应用重做日志
- 页在写入磁盘时操作系统发生了崩溃,在恢复过程中,InnoDB存储引擎从共享表空间中的doublewrite页中找到该页的副本,将其复制到表空间文件,再应用重做日志
使用命令SHOW GLOBAL STATUS LIKE 'innodb_dblwrj%'观察Double Write的运行情况
使用参数skip_innodb_doublewrite可禁止使用doublewrite:若是从服务器,为了较快的性能可以关闭,但是主服务器需要确保打开
2.6.3 自适应哈希索引(Adaptive Hash Index)-读取和写入速度提高2倍,辅助索引的连续操作性能提高5倍
InnoDB引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引(Adaptive Hash Index)
AHI通过缓冲池的B+树页构造而来,建立速度很快,且不需要对整张表构建哈希索引
-
AHI的要求
-
对这个页的连续访问模式必须一样
-
该模式访问了100次
-
页通过该模式访问了N次,N=页中记录*1/16
-
只用来搜索等值的查询,使用参数innodb_adaptive_hash_index启用或禁用,默认开启
2.6.4 异步IO(Async IO)-恢复速度提高75%
用户在发出一个IO请求后立即发出另一个IO请求,全部IO请求发送完毕后,等待所有IO操作的完成
InnoDB 1.1.x开始之前,AIO的实现通过InnoDB存储引擎中的代码来模拟实现
InnoDB 1.1.x开始,提供了内核级别AIO的支持,native AIO(需要系统支持,windows系统和linux系统支持,Mac OSX不支持);在编译或运行该版本MySQl时,需要libaio库的支持
使用参数innodb_use_native_aio来控制是否启用native AIO,linux系统默认为ON
-
InnoDB存储引擎中,磁盘的写入操作全部由AIO完成
-
read ahead方式的读取
-
脏页的刷新
-
2.6.5 刷新邻接页(Flush Neighbor Page)
使用参数innodb_flush_neighbors控制是否启用,传统机械硬盘建议启用,固态硬盘有超高IOPS性能的磁盘建议关闭
2.7 启动、关闭与恢复
-
参数innodb_fast_shutdown
-
参数innodb_force_recovery