1.四个线程(InnoDB版本为1.0.x以上)。
查看innodb版本:show VARIABLES like 'innodb_version';
Master Thread:负责合并插入缓冲,
IO Thread :InnoDB大量使用AIO处理写请求,其负责处理IO请求的回调处理,有四个IO线程,read,write,insert buffer,
log Thread:
parge Thread:事务提交后,undolog不再需要,回收undo页
Page Cleaner Thread:脏页刷新
2.内存
数据库中页的修改操作,首先会在内存池中的页,以一定的频率刷新到磁盘,一般采用checkpoint机制。
查看缓冲池大小 :show VARIABLES like 'innodb_buffer_pool_size';
缓冲池页类型有:索引页,数据页,undo页,插入缓冲,自适应哈希索引,锁信息,数据字典。
缓冲池实例配置查看:show VARIABLES like 'innodb_buffer_pool_instances';
查看缓冲池状态:SELECT * from information_schema.INNODB_BUFFER_POOL_STATS
3.缓冲池管理
Innodb采用优化后的LRU算法管理缓冲页(16k)。在列表中加入一个midpoint位置,新读到的页放到这个位置,而不是最前页。为了防止活动页被刷出,影响缓冲池效率
midpoint位置查看:show VARIABLES like 'innodb_old_blocks_%'。innodb_old_blocks_pct值为百分比。innodb_old_blocks_time为过多久加入到LRU列表热端,0为不等待。
当页从LRU的old部分加入到new部分,称此操作为page made young;
当页从LRU的new部分加入到old部分,称此操作为page not made young;
show engine innodb status可以查看LRU使用状态。
有个重要的参数buffer pool hit rate表示缓冲池的命中率,通常该值不应该小于95%。若小于95%,该检查下是否有全表扫描操作导致LRU列表被污染。
查看LRU每个页具体信息:SELECT * from information_schema.INNODB_BUFFER_PAGE_LRU
4.重做日志缓冲
InnoDB内存区域除了有缓冲池外,还有重做日志缓冲,
查看重做日志缓冲大小:show VARIABLES like 'innodb_log_buffer_size'
重做日志缓冲刷新到磁盘的时机:
1.Master Thread每一秒将重做日志缓冲刷新到重做日志文件。
2.每个事物提交时将重做日志缓冲刷新到重做日志文件。
3.当重做日志缓冲池空间小于1/2,重做日志缓冲刷新到重做日志文件。
5.额外的内存池
数据结构本身的内存分配,如LRU,锁信息
6.checkpoint技术目的
checkpoint技术就是为了刷新脏页到磁盘。
1.减少数据库恢复时间;2.缓冲池不够用时刷新到磁盘;3.重做日志不可用时,刷新脏页;
解释下第三点目的:因为对重做日志的设计和管理是循环使用的,不是不限增大,日志能被重用的部分空间也就是这个空间的日志不再被需要,这部分空间可以被覆盖重用,若重做日志还需要使用,那么强制产生checkpoint,将缓冲池的页至少刷新到当前重做日志的位置。
有两种checkpoint:1.Sharp Checkpoint.2.Fuzzy Checkpoint.
Sharp Checkpoint发生在数据库关闭时发生,将所有脏页刷新到磁盘,相应参数innodb_fast_shutdown=1.
Fuzzy Checkpoint发生在运行中,只刷新部分脏页。发生的情况有以下几种:
1.Master Thread CheckPoint
主线程每秒或每十秒的速度执行,用户查询线程不会阻塞
2.Async/sync Flush Checkpoint
重做日志不可用时,强制刷新一些脏页到磁盘。
3.FLUSH_LRU_LIST checkpoint
若LRU列表中没有100个空闲页,则从列表尾部清空,若有脏页,则执行checkpoint
4.Dirty too much checkpoint
脏页太多,强制执行checkpoint,由参数max_dirty_pages_pct控制
7.LSN(日志序列号)作用
InnoDB用LSN来标记版本号,LSN占8字节,每个页有LSN,重做日志也有,checkpoint也有。
show engine innodb status可查看Log sequence Number,Log flushed up to ,Last checkpoint at。
8.InnoDB的关键特性
插入缓冲,两次写,自适应哈希索引,异步IO,刷新邻接页
8.1 插入缓冲
Insert Buffer和数据页一样,是物理页的一个组成部分,不是缓冲池的一部分,只是缓冲池有insert buffer信息。
注意:并不是所有的主键插入都是有顺序的,如UUID这样的类别,插入是随机的。
尽管主键是有顺序的,但对非聚集索引叶子节点的插入不一定是顺序的,那么就会有离散的访问非聚集索引页,由于随机读取的存在,会导致插入性能下降,因为B+树的特性决定了非聚集索引插入的离散性。
但对于时间字段的非聚集索引,相对来说是比较有顺序的。
InnoDB的Insert Buffer用来解决非聚集索引插入和更新的性能问题。插入数据时先判断缓冲池中是否存在要插入的非聚集索引页中,没有则放到Insert buffer对象,InnoDB以一定的频率进行Insert buffer和非聚集索引节点的合并操作,这样能将多个插入操作一次性合并到一个操作,因为操作都在一个索引页中,从而大大提高非聚集索引插入性能。
Insert buffer使用的两个条件:1.索引是非聚集索引;2.索引不是唯一的。
解释下第二点:InnoDB如果要维护索引的唯一性,那么插入的时候要查找索引页来判断插入的唯一性,如果去查找肯定会有离散读取的存在,那Insert buffer没有意义了。
查看插入缓冲对性能的提高状态:show engine innodb status
seg size 显示当前insert buffer的大小;free list len显示空闲列表的长度;size显示已经合并记录页的数量;inserts表示插入的记录数;merged recs表示已经合并的记录数;merges表示合并操作的次数,也就是实际读取页的次数;
merged recs : merges的比例(平均每次合并操作的记录数)越大,性能越好。
在写密集情况下,插入缓冲会占用过多缓冲池内存,这样对其他操作会有影响。
IBUF_POOL_SIZE_PER_MAX_SIZE可以对插入缓冲占用的最大内存进行控制。
8.2change buffer
是对insert buffer的升级,可对,insert,delete,update都进行缓冲,分别是insertbuffer ,deletebuffer,parge buffer。
update操作分为两个阶段:1.记录标记为已删除(对应delete buffer);2.真正删除(对应parge buffer).
innodb_change_buffering用来开启各种buffer的选项;
innodb_change_buffer_max_size来控制change buffer最大使用的内存数量。
show engine innodb status可以查看到merged operations和discarded operation.
8.3Insert buffer内部实现
Insert buffer的数据结构上是一颗B+树,全局只有一颗Insert buffer B+树,负责对所有表的辅助索引进行Insert buffer,这颗B+树存放在共享表空间,默认也就是ibdata1.因此试图通过独立表空间ibd文件恢复表中数据,往往会导致check table失败,这是因为表的辅助索引中的数据可能还在insert buffer中,也就是共享表空间中,所以通过ibd文件进行恢复后,还需要repair table 操作来重建表上所有的辅助索引。
Insert buffer B+树非叶子节点和叶子节点构成。非叶子节点:
space id (每个表有一个唯一的id),4字节 | marker用来兼容老版本的insert buffer | offset表示页所在的偏移量 |
IBUF_REC_OFFSET_COUNT(2字节):用来排序进入insert buffer的顺序。
IBUF_REC_OFFSET_TYPE:(1字节)
IBUF_REC_OFFSET_FLAGS(1字节)。
在启用了insert buffer之后,辅助索引页中的记录可能被插入到insert buffer B+树中,为了保证每次merge insert buffer页必须成功,需要一个特殊页记录辅助索引页的 可用空间,这个页的类型是insert buffer bitmap.
insert buffer bitmap用来追踪16384个辅助索引页,每个辅助索引页在insert buffer bitmap占用4bit,
4字节的存储信息:
IBUF_BITMAP_FREE(2):剩余空间
IBUF_BITMAP_BUFFERED(1):表示该辅助索引页是否有记录被缓存在insert buffer B+树中。
IBUF_BITMAP_IBUF(1):1表示该页为insert buffer B+树的索引页。
8.4merge insert buffer
若插入记录的辅助索引页不在缓存池,则记录插入到insert buffer B+树中,但何时合并到真正的辅助索引页中。
merge insert buffer发生主要有以下3种情况:
1.辅助索引页被读取到缓存池的时候,
2.insert buffer bitmap追踪到这个辅助索引页没有可用空间;
3.Master Thread每秒或每十秒执行merge insert buffer。
第一种情况:当select 查询时,会查询insert buffer bitmap,判断该索引页是否在inser buffer中有记录,如有就合并。
第二种情况:insert时,当检测到IBUF_BITMAP_FREE记录的可用空间小于1/32页,强制执行合并,即强制读取辅助索引页,将insert buffer B+树中该页的记录及待插入的记录插入到辅助索引页。
第三种情况:在Master Thread合并的是多个页,根据srv_innodb_io_capacity的百分比来确定真正要合并辅助索引页的数量。
那又如何确定哪些页需要合并操作?
在insert buffer B+树中,辅助索引页根据(space,offset)都已排序好,随机选择insert buffer B+树的一个页,读取该页的space及之后需要数量的页。若在merge时,要进行merge的表已经被删除,则可以直接丢弃已经被insert/change buffer的数据记录。
8.5 两次写
insert buffer 带给innodb性能提升,两次写是提高数据页的可靠性。
两次写解决的问题:当数据库宕机时,可能innodb存储引擎正在写入某个页到表中,而这个页只写入了一部分,即发生部分写失效,而重做日志是对物理页的修改记录,如果磁盘上这个页已损坏,用重做日志恢复也没任何意义。
所以在应用重做日志之前需要备份待写入页的副本到共享表空间,当写入失效时,在恢复过程中,先通过共享表空间中的副本复制到表空间文件,再通过重做日志进行重做,这就是doublewrite.相关的doublewrite架构图在网上有。
show global status like '%innodb_dblwr%';
Innodb_dblwr_pages_written表示写入的页数量,innodb_dblwr_writes表示写入操作次数。
skip_innodb_doublewrite参数可以禁止使用doublewrite功能,主服务器为了数据的可靠性,必须启用,从服务器可选。有些文件系统本身已经提供了部分写失效的防范机制,如ZFS文件系统,这样情况下就不用 启用doublewrite了。
8.6 自适应哈希索引(adaptive hash index)
哈希是O(1)时间复杂度的数据存储和查询方式,而B+树需要3到4次的查询。InnoDB会监控每个表上各索引页的查询,若发现建立哈希索引能提供性能则建立索引,称之为自适应哈希索引。
建立条件:1.对这个页的连续访问模式必须一样的,2.以该模式访问了100次,3.该模式下访问了N次,N=页中记录*1/16;4.等值搜索,不能是范围查询。
其设计思想是数据库自优化的,不需要DBA参与。
show engine innodb status可查看 hash searches ,non-hash searches.
innodb_adaptive_hash_index参数用来启用和禁用AHI.默认开启
8.7异步IO
AIO如何提升IOPS性能?AIO可以进行IO merge操作,如果操作的页是连续的,则将多个IO操作合并为一个。
innodb_use_native_aio用来控制是否启用Native AIO,默认是ON,
8.8刷新邻接页
在刷新脏页时,会检测脏页所在的区的所有页,如果是脏页,则一起刷新,通过AIO可以将多个IO写入操作合并为一个IO。
innodb_flush_neighbors可控制是否启用该特性,对于传统机械硬盘建议启用,固态硬盘建议关闭此特性。
9.启动,关闭,恢复
在关闭时,innodb_fast_shutdown影响innodb的行为。
可取值有0,1,2.默认是1.
0:表示在数据库关闭时,innodb需要完成所有的full parge 和merge insert buffer。将所有的脏页刷新到磁盘。如果数据库升级,那么需要设置为0;
1:不需要完成full parge 和merge insert buffer。但会将所有的脏页刷新到磁盘;
2:不需要完成full parge 和merge insert buffer,也不会将所有的脏页刷新到磁盘。但是会将日志写入到日志文件,这样不会有任何事务的丢失,下次启动时,会进行恢复;
innodb_force_recovery影响innodb的恢复行为。可取值为0,1(忽略corrupt页,),2,3(不回滚),4(不合并插入缓冲),5(不查看undo Log),6(不前滚操作).默认是0,表示恢复所有。
在设置了大于0的值后,可进行select ,create,drop.不能进行insert,update,delete这类DML操作。
如果在启动之前设置了3,那么启动时不进行回滚,启动会很快。