2.1 概述
- Innobase Oy 公司开发
- 第一个完整支持ACID事务的MySQL存储引擎(BDB是第一个支持,黄了),行锁设计,支持MVCC,提供类似Oracle风格的一致性非锁定读,支持外键,被设计用来最有效地利用内存和CPU
- Heikki Tuuri是创始人
- 适合OLTP(联机事务处理 On-Line Transaction Processing,对比联机分析处理 On-Line Analytical Processing)项目
2.2 InnoDB体系架构
多个内存块组成的内存池负责如下:
- 维护所有进程线程需要访问的多个内部数据结构
- 缓存磁盘上的数据,方便快速地读取,并且在对磁盘文件的数据进行修改之前在这里缓存
- 重做日志(redo log)缓冲
- ......
后台线程主要负责:
- 刷新内存池中的数据
- 保证缓存池中的内存缓冲的是最近的数据
- 将已修改的数据文件刷新到磁盘文件
- 保证在数据库发生异常时InnoDB能恢复到正常运行状态
2.2.1
Oracle是多进程架构,核心后台进程有CKPT、DBWn、LGWR、ARCn、PMON、SMON等
而InnoDB不是,它是在master thread线程上实现了所有的功能。
默认情况下,InnoDB后台线程有7个,4个IO线程,一个master thread,一个锁监控线程,一个错误监控线程。
4个IO thread为 insert buffer thread,log thread,read thread,write thread,InnoDB Plugin版本后不再用innodb_file_io_threads参数控制read thread 和write thread数量,而是使用 innodb_read_io_thread和innodb_write_io_thread
使用此命令查看后台线程
show engine innodb status\G
2.2.2 内存
InnoDB由缓冲池(buffer pool)、重做日志缓冲池(redo log pool)、以及额外的内存池(additional memory pool),分别由innodb_buffer_pool_size、innodb_log_buffer_size 和 innodb_additional_mem_pool_size 的大小决定。
show variables like 'innodb_buffer_pool_size';
- 缓冲池是占最大内存的部分,用来存放各种数据的缓存。InnoDB将数据库文件按页(16K,对应一帧)读取到缓冲池,然后按最近最少使用算法管理缓存数据。数据库文件修改后则先改缓存,定期刷新(flush)到文件
- 日志缓冲将重做日志信息存入缓存区,一般每秒刷新到日志文件一次,所以其大小需要满足一秒内的事务量
- 额外内存池也很重要,缓冲池中的帧缓冲还有对应的缓冲控制对象(记录了诸如LRU、锁、等待等方面的信息)都存储在额外内存池中,所以需要跟随缓冲池大小而变动
2.3 master thread
2.3.1 master thread 源码分析
master thread 线程级别最高,其内部由主循环(loop),后台循环(background loop),刷新循环(flush loop),暂停循环(suspend loop),master thread根据数据库运行状态在这几个循环中切换,loop主要有两大部分,每秒操作,每10秒操作:
void master_thread(){
loop;
for(int i = 0;i < 10;i++){
do thing once per second
sleep 1 second if necessary
}
do things once per 10 second
goto loop;
}
每秒一次操作包括:
- 日志缓冲刷新到磁盘,即使这个事务还没有提交(总是),所以再大的事物commit的时间也是很快的
- 合并插入缓存(可能),当前一秒IO次数少于5次时发生
- 至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能),通过判断当前缓冲池中的脏页比例(buf_get_modified_ratio_pct)是否超过配置文件中的innodb_max_ditry_pages_pct,默认为90,如果超过则执行。
- 如果当前没有活动,切换到background loop
每10秒一次操作包括:
- 刷新100个脏页到磁盘(可能)
- 合并至多5个插入缓冲(总是)
- 将日志缓冲刷新到磁盘(总是)
- 删除无用的Undo页(总是)
- 刷新100个或者10个脏页到磁盘(总是)
- 产生一个检查点(总是)
然后是background loop,若当前没有用户活动,则进入此处
- 删除无用的Undo页(总是)
- 合并20个插入缓冲(总是)
- 跳回到主循环(总是)
- 不断刷新100个页,直到符合条件(可能,跳转到flush loop中完成)
若是flush loop中也没有事儿,则转入 suspend loop,挂起master thread,等待事件
2.3.2 master thread 的潜在问题
- 随着硬件性能的增强,缓冲池到磁盘硬性规定刷新100个脏页可能浪费硬件资源,故引入innodb_io_capacity,表示磁盘吞吐量,可根据磁盘性能设置,刷新脏页时会按比例刷新
- innodb_max_dirty_pages_pct默认值90不妥,太大了,经测试,75更妥,并引入innodb_adaptive_flushing(自适应的刷新,决定每1秒刷新的脏页数),这个参数的引入,通过buf_flush_get_desired_flush_rate 这个函数来判断需要刷新的脏页数,其原理是通过判断产生重做日志的速度来判断最合适的刷新脏页的数量。
2.4 关键特性
2.4.1插入缓冲(插入性能)
主键是行唯一的标识符,程序中插入记录顺序是按主键递增的,因此插入聚集索引一般是顺序的,不需要磁盘的随机读取。而非聚集索引的插入是离散的,B+树特性而至。
插入缓冲有两个条件
- 索引是辅助索引
- 索引不是唯一的
但是当程序执行大量的插入和更新操作,此时数据库又宕机,将会有大量的插入缓冲没有合并到实际的非聚集索引中,花费大量时间恢复。
辅助索引不能使唯一的,因为插入到插入缓冲时,我们并不去查找索引页的详情,吐过去查看了则又出现离散读,插入缓冲则失去意义。
插入缓存占用太多缓冲池内存时,调整IBUF_POOL_SIZE_PER_MAX_SIZE 即可
2.4.2 两次写 (可靠性)
脏页刷新时,先memcpy拷贝到内存中的doublewrite buffer中,然后通过doublewrite buffer分两次,每次1MB写入共享表空间的物理磁盘(doublewrite页连续,开销较小),然后将doublewrite buffer中的页写入表空间文件(此时为离散的,开销大)。如果写入磁盘过程中崩溃了,则可以从doublewrite页拷贝页的副本到表空间文件,再用重做日志进行恢复。skip_innodb_doublewrite可以禁止两次写功能
2.4.3 自适应哈希索引
- 自适应哈希索引通过引用缓冲池的B+树构造而来,因此建立的速度很快。
- InnoDB存储引擎会自动根据访问的频率和模式来为某些页建立哈希索引。
- 自适应哈希索引只能用来搜索等值条件的查询。
- 可以通过 innodb_adaptive_hash_index开禁用和启动
2.5 启动、关闭与恢复
参数innodb_fast_shutdown 取值
- 0 当mysql关闭时,InnoDB需要完成所有的full purge 和 merge insert buffer
- 1 为默认值,表示不完成full purge 和 merge insert buffer,但是脏页数据还是会刷新到磁盘
- 2 不完成full purge 和 merge insert buffer,脏页数据也不会刷新到磁盘,但是下次启动时,数据库会执行恢复操作。
参数 innodb_force_recovery 取值
- 0 默认值,恢复时执行所有的回复操作
- 1 忽略检查到的corrupt页
- 2 阻止主线程运行,入主线程需要执行full purge操作,会crash
- 3 不执行事务回滚
- 4 不执行插入缓冲的合并操作
- 5 不查看撤销日志,将未提交事务视为已提交
- 6 不执行前滚的操作
需要注意的是,当设置参数大于0后,可以对表进行select、create、drop操作,但是insert、update、和delete这类操作是不允许的
2.6 InnoDB Plugin = 新版本的InnoDB存储引擎
mysql 5.1这个版本,采用了插件式的架构,这样,如有存储引擎的bug,无需再等待mysql的新版本了。
InnoDB新功能
- 快速索引重建
- 更好的多核性能
- 新的页结构
- 页压缩功能
- 更好的BLOB处理能力
欢迎大家关注我的公众号