InnoDB存储引擎架构简述

近期企业内部分享了这篇架构,粘出来分享给你们.
参考资料:https://dev.mysql.com/doc/ref...html

InnoDB存储引擎是平衡了高可靠和高性能的存储引擎,它也是Mysql8.0中默认的存储引擎.mysql

clipboard.png

从架构图中可见,InnoDB引擎可划分为两大部分:内存结构和硬盘结构.算法

内存部分(Memory)又包含Buffer pool, change buffer, Log Buffer 等,经过操做系统缓存与硬盘结构进行交互.sql

硬盘部分包含若干个表空间,如系统表空间,单文件表空间,通常表空间,临时表空间,undo表空间和redo log. 数据库

1.Buffer Pool(缓冲池)缓存

clipboard.png

缓冲池是主内存的一部分,它用于在咱们查询访问任何表和索引数据时进行缓存.据官方文档介绍的统计,高达80%的查询来会由缓存池命中.session

在介绍缓存池的官方文档中,咱们能够见到一个分明另外的lru算法.数据结构

首先,缓冲池能够理解为一个塞满page的链表,注意,其中单位是页(page, innodb引擎的存储单位自上到下为表空间 table spaces,片断 segemant, 分区 extend, 页 page, 行row,列 column),也就是说,缓存是以页为单位的,io操做将硬盘中的数据读入缓冲的单位也是页.架构

其次,缓冲队列其实有两个sublist,最新的5/8为new sublist,较老的3/8为old sublist.new sublist 的tail与old sublist 的head相邻,这个相邻点被官方文档称之为midpoint(中点).较新访问的数据放置于new sublist,反之放置于old sublist.并发

第三,当咱们从硬盘中读取新的页并入池时,它是放置于中点的.固然,这个"读"包含咱们用户的初始化操做(user-initiated, 包含sql查询),也包含引擎为优化而作的预读操做(read-ahead).

第四,有关移动操做,对于old sublist的页成员进行读取操做时,分两种状况,若为用户初始化操做形成,则会当即将其移动到new list头,若为预读操做形成,则可能不会移动.

第五,每一个页的位置至关于一个虚拟的"年龄",每当buffer pool有一个页被读取,都会形成其余页的年龄加1(实际效果即向后移位).同时,old sublist的成员也会因一些页的初次读入缓存而老化,经过这两个机制的共同做用,new sublist的元素不停地移入old sublist,old sublist中的数据可能会慢慢过时或获得新生.

2.change buffer(变动缓冲)

辅助索引和聚簇索引有什么区别?除了耳熟能详的前者须要二次io之外,变动缓冲也是二者在查询上的一个不一样点.

clipboard.png

change buffer的做用点在于,对于一些数据的二级索引进行更新操做(insert update delete都在此更新范围),而相应的辅助索引页还并未加载进缓冲池时,将相应的对辅助索引的更新缓存到change buffer,直到当后续对相应的页进行了读操做时,将这个结果合并进buffer pool.

这样实现的理由也很简单,咱们知道辅助索引(二级索引)不像聚簇索引同样惟一,它的插入操做相对为随机写.对二级索引的删除和更新操做也可能会影响到在索引树中根本不相邻的数据页,所以将它暂时缓冲起来,在后续使用到变动索引的读操做发生时统一合并入buffer pool,再由buffer pool返回读取的结果,能够减小大量的随机写操做.

同时,当mysql进行周期净化(purge)操做时,或进行慢关闭时,能够将这些change buffer批量地写入到硬盘,后者会写入到系统表空间的change buffer,从而减小了多个单次io的开销.

特殊状况,当二级索引包含降序索引列,或主键包含降序索引列时,change buffer将不可用.change buffer的批量落盘操做会形成mysql瞬间的io开销增大.

3.adaptive hash index (自适应哈希索引)

自适应哈希索引做用于缓冲池中的数据,当有效地平衡了工做负载和缓冲池内存时,咱们使用自适应哈希索引能够令mysql在不损坏任何事务性和可靠性的前提下逼近内存数据库的性能.

它是自适应的(意味着能够由innodb自行观测查询频率,并估算优化的结果和构建哈希索引的开销比),它的构建是使用索引key的左前缀实现的,主要针对频繁访问的数据.

有两种特殊地场景,高并发地访问自适应哈希索引(如并发地进行join)可能会是一个重要的竞态来源,当咱们大量使用通配符%或like操做时,自适应哈希索引将毫无益处,此时能够经过重启服务并加上--skip-innodb-adaptive-hash-index关闭之.

4 log buffer

log buffer是一块内存区域,用于保存将要刷盘的日志操做,它有两个落盘的机制,一是周期性地落盘,二是事务提交后落盘,所以当咱们使用大事务时,适当调大它的值,能够减小无谓的io.

5.system tablespace (系统表空间)

系统表空间是在硬盘中存放double write buffer和change buffer的表空间,同时,若一个表建立于系统表空间(而不是单文件表空间或通常表空间)时,它可存放这些表的表数据和索引数据.

6.File-Per-Table Tablespaces (单文件表空间)

据官方文档介绍,单文件表空间相对于传统的全部表数据存放于系统表空间的优点在于更加灵活,它使得每一个表能够有本身的表空间文件.

7.General Tablespace (通常表空间)

通常表空间是共享的,由create tablespace语法建立的表空间.它与系统表空间相似,存放可供多表使用的共享数据.它具有先天的使用优点,如在完整生命周期内将表空间的数据加载至内存,则可减小相似单文件表空间的频繁io开销.

8.undo tablespaces

undo log包含于undo段,undo段包含于回滚段,而undo表空间就是存放了可用于回滚事务的undo log.

9.temporary tablespaces(临时表空间)

临时表空间包含两个级别:会话级别的临时表空间和全局的临时表空间.

会话级临时表空间每次使用时会从一个临时表空间池中取出并和session进行attach,默认状况下,server启动时会建立一个拥有10个临时表空间的池供使用,当会话释放时,它持有的临时表空间会被清空并还池.

会话级临时表空间默认大小为5个page大小,而且以.ibt结尾.

全局级临时表空间用于存放对于用户建立的临时表的变动回滚段.在server关闭或放弃了全局级临时表空间的初始化操做时销毁,在server每次启动时重建.

10.double write buffer(双写缓冲)

双写缓冲存放于系统表空间,是innodb从内存模块的buffer pool取出的页刷新到磁盘的目的地,这也说明对于innodb的写操做实际上是二次写,但这并不影响宏观的性能,反而增大了吞吐量.double write buffer中已写入的数据将在后续刷入它真正的目的地,若在此过程当中出现宕机等事故,mysql server将能够在double write buffer中找到一个准确的copy.

数据页刷入double write buffer的过程使用了操做系统的fsync()函数,将较大的数据量统一处理,顺序写入.这一特性能够手动关闭,或者在支持原子写的Fusion-io设备上运行时,将自动禁用double write buffer.

11 redo log(重放日志)

重放日志也是一个用于恢复数据的存放于硬盘的数据结构,但它做用于那些未完成的事务.在一次crash保存的未完成的对某些数据文件的更改能够在后续服务启动后的初始化过程当中基于redo log自动重放.

重放日志在物理上体现为两个文件:ib_logfile0,ib_logfile1,mysql中对这两个文件的使用是"环形方式",这意味着能够交替重用.redo log中的日志标识是一个按LSN(日志顺序号)永远递增的数.

为了节省性能,重放日志也支持按组提交落盘(批量).日志自己支持压缩.

12 undo log(回滚日志)

undo log是单次读写事务的回滚日志,它包含了对于一个事务来讲必要的针对一个聚簇索引记录的最近修改的回滚信息,若是有一个事务在一致读场景下须要读到原版本数据,它能够从它事务的undo log中找到相应的数据.

mysql innodb引擎的隔离级别实现与undo log有重大关联,当隔离级别为读已提交时,事务对数据的读取永远是取最新提交,并不须要undo log,但当级别为可重复读时,每次读取须要利用undo log进行.

  1. Multi-Versioning(多版本并发控制,通常简称mvcc)

innodb是一个支持多版本并发的存储引擎,前面已经提过,innodb利用表空间的回滚段(包含了undo log)来保存了为支持事务的并发和回滚而必需的,被修改数据行的老版本信息,它使用该信息进行回滚以及一致性读等操做.

关于mvcc的内部实现,innodb为每一行数据多维护了隐式的三个field,分别是6字节长的DB_TRX_ID,7字节长的DB_ROLL_PTR,6字节的DB_ROW_ID.

DB_TRX_ID:它表示最近对一个行的数据的插入或更新的事务id(删除在此理解为一种更新操做,只是行中有一个位表示标记为删除).

DB_ROLL_PTR:即滚动指针,它指向undo段的一个undo log.当发生该行的更新时,undo log中包含了重建历史数据的所有信息.

DB_ROW_ID:行id,它是一个在新行插入时单调递增的值,当innodb自动生成聚簇索引时,索引将包含行id,不然行id不出如今任何索引中.

存放于undo段的undo log区分为insert和update两种(后者包含delete),前者能够在事务提交以后直接舍弃,后者因一致性读的缘由(这些读须要访问历史版本的数据),只能在全部快照均没有事务在使用时才能舍弃.

所以,须要及时地提交事务,尤为那些包含一致读的事务,不然undo log会愈来愈大,最终充满表空间.

涉及mvcc,被删除的数据行不会当即物理删除,直到有关的undo log被删除,这一操做称为purge,而且它会按照实际删除的顺序进行.

对于mvcc,innodb在聚簇索引和二级索引实现上稍有不一样,聚簇索引能覆盖的记录会就地更新,而指向早期版本记录的undo log指向的一些隐藏信息条目能够进行重建,但对于二级索引,它并不包含隐藏系统列,也不进行原地更新.

当二级索引被更新时,老的二级索引记录会被标记删除,新的二级索引会插入,标记删除的老二级索引记录会最终被清理,若是已经开启了一个针对它的读事务,且发生了写操做,则从undo log中查找相应的版本记录.

当二级索引被删除时,覆盖索引将不可用,即不从索引树中查找,而是从聚簇索引记录中查找.

当使用索引条件下推优化时,且只有部分where查询条件可以使用到索引,mysql server仍旧会将全条件下推到存储引擎,而存储引擎会即便相应的索引取值,若是没有取到值,则省去了后续的使用聚簇索引再次查找的过程.若是有记录(甚至多是被标记删除但能匹配的记录,还未被purge,多是由于mvcc),则无可避免后续的,还须要进行一次的利用聚簇索引查找的过程.

相关文章
相关标签/搜索