天天看点

InnoDB体系结构及工作原理

概念

InnoDB主要包括了内存池、后台线程以及存储文件。INNODB的三大特性:插入缓存,两次写,自适应hash

内存池又是由多个内存块组成的,主要包括Buffer Pool、redo log缓冲等,解决cpu速度和磁盘速度的严重不匹配问题

后台线程则包括了Master Thread、IO Thread以及Purge Thread等,后台进程保证缓存池和磁盘数据的一致性(读取、刷新),并保证数据异常宕机时能恢复到正常状态。

由InnoDB存储引擎实现的表的存储结构文件一般包括表结构文件(.frm)、共享表空间文件(ibdata1)、独占表空间文件(ibd)以及日志文件(redo文件等)等。

InnoDB体系结构及工作原理

内存池

如果客户端从数据库中读取数据是直接从磁盘读取的话,无疑会带来一定的性能瓶颈,缓冲池的作用就是提高整个数据库的读写性能。

客户端读取数据时,如果数据存在于缓冲池中,客户端就会直接读取缓冲池中的数据,否则再去磁盘中读取;对于数据库中的修改数据,首先是修改在缓冲池中的数据,然后再通过Master Thread线程刷新到磁盘上。

理论上来说,缓冲池的内存越大越好。

缓冲池的数据页类型有:数据页,索引页,undo页,插入缓冲,自适应哈希索引,innodb存储的锁信息,字典信息。

InnoDB允许多个缓冲池实例,从而减少数据库内部资源的竞争,增强数据库的并发处理能力。

InnoDB存储引擎会先将重做日志信息放入到缓冲区中,然后再刷新到重做日志文件中。

insert buffer场景

1、解决了什么问题

对于非聚集索引来说,叶子节点的插入不再有序,这时就需要离散访问非聚集索引页,插入性能变低。

2、机制

如果该索引页在缓冲池中,直接插入;否则,先将其放入插入缓冲区中,再以一定的频率和索引页合并,这时,就可以将同一个索引页中的多个插入合并到一个IO操作中,大大提高写性能。master thread主循环其中的一项工作就是每秒钟合并插入缓冲,insert buffer和辅助索引合并(merge)操作的时间 1S 10S合并,如果用户需要查询辅助索引的数据,那么会优先进行合并,然后在返回给用户数据

3、条件

插入缓冲的启用需要满足下两个条件:

1)索引是辅助索引(secondary index)

2)索引不适合唯一的

如果辅助索引是唯一的,就不能使用该技术,原因很简单,因为如果这样做,整个索引数据被切分为2部分,无法保证唯一性。

4、坏处

1)可能导致数据库宕机后实例恢复时间变长。如果应用程序执行大量的插入和更新操作,且涉及非唯一的聚集索引,一旦出现宕机,这时就有大量内存中的插入缓冲区数据没有合并至索引页中,导致实例恢复时间会很长。

2)在写密集的情况下,插入缓冲会占用过多的缓冲池内存,默认情况下最大可以占用1/2,这在实际应用中会带来一定的问题。

double write buffer

介绍double write之前我们有必要了解partial page write(部分页失效)问题。

关于IO的最小单位:

  1、数据库IO的最小单位是16K(MySQL默认,oracle是8K)

  2、文件系统IO的最小单位是4K(也有1K的)

  3、磁盘IO的最小单位是512字节

因此,存在IO写入导致page损坏的风险

InnoDB的Page Size一般是16KB,其数据校验也是针对这16KB来计算的,将数据写入到磁盘是以Page为单位进行操作的。我们知道,由于文件系统对一次大数据页(例如InnoDB的16KB)大多数情况下不是原子操作,这意味着如果服务器宕机了,可能只做了部分写入。16K的数据,写入4K时,发生了系统断电/os crash ,只有一部分写是成功的,这种情况下就是partial page write问题。

有经验的DBA可能会想到,如果发生写失效,MySQL可以根据redo log进行恢复。这是一个办法,但是必须清楚地认识到,redo log中记录的是对页的物理修改,如偏移量800,写’aaaa’记录。如果这个页本身已经发生了损坏,再对其进行重做是没有意义的。MySQL在恢复的过程中检查page的checksum,checksum就是检查page的最后事务号,发生partial page write问题时,page已经损坏,找不到该page中的事务号。在InnoDB看来,这样的数据页是无法通过checksum验证的,就无法恢复。即使我们强制让其通过验证,也无法从崩溃中恢复,因为当前InnoDB存在的一些日志类型,有些是逻辑操作,并不能做到幂等。

为了解决这个问题,InnoDB实现了double write buffer,简单来说,就是在写数据页之前,先把这个数据页写到一块独立的物理文件位置(ibdata),然后再写到数据页。这样在宕机重启时,如果出现数据页损坏,那么在应用redo log之前,需要通过该页的副本来还原该页,然后再进行redo log重做,这就是double write。double write技术带给innodb存储引擎的是数据页的可靠性,下面对doublewrite技术进行解析,让大家充分理解double write是如何做到保障数据页的可靠性。

工作流程:

InnoDB体系结构及工作原理

doublewrite由两部分组成,一部分为内存中的doublewrite buffer,其大小为2MB,另一部分是磁盘上共享表空间(ibdata x)中连续的128个页,即2个区(extent),大小也是2M。其中120个用于批量写脏,另外8个用于Single Page Flush。做区分的原因是批量刷脏是后台线程做的,不影响前台线程。而Single page flush是用户线程发起的,需要尽快的刷脏并替换出一个空闲页出来。

  1、当一系列机制触发数据缓冲池中的脏页刷新时,并不直接写入磁盘数据文件中,而是先拷贝至内存中的doublewrite buffer中;

  2、接着从两次写缓冲区分两次写入磁盘共享表空间中(连续存储,顺序写,性能很高),每次写1MB;

  3、待第二步完成后,再将doublewrite buffer中的脏页数据写入实际的各个表空间文件(离散写);(脏页数据固化后,即进行标记对应doublewrite数据可覆盖)

副作用:

1、double write带来的写负载

  1、double write是一个buffer, 但其实它是开在物理文件上的一个buffer, 其实也就是file, 所以它会导致系统有更多的fsync操作, 而硬盘的fsync性能是很慢的, 所以它会降低mysql的整体性能。

  2、但是,doublewrite buffer写入磁盘共享表空间这个过程是连续存储,是顺序写,性能非常高,(约占写的%10),牺牲一点写性能来保证数据页的完整还是很有必要的。

2、监控double write工作负载

show global status like '%dblwr%';

Innodb_dblwr_pages_written | 178712
Innodb_dblwr_writes        | 3069      

关注点:Innodb_dblwr_pages_written / Innodb_dblwr_writes

  开启doublewrite后,每次脏页刷新必须要先写doublewrite,而doublewrite存在于磁盘上的是两个连续的区,每个区由连续的页组成,一般情况下一个区最多有64个页,所以一次IO写入应该可以最多写64个页。

  而根据以上系统Innodb_dblwr_pages_written与Innodb_dblwr_writes的比例来看,大概在58左右,还没到64(如果约等于64,那么说明系统的写压力非常大,有大量的脏页要往磁盘上写),所以从这个角度也可以看出,系统写入压力并不高。

3、关闭double write适合的场景

  1、海量DML

  2、不惧怕数据损坏和丢失

  3、系统写负载成为主要负载

show variables like '%double%';

innodb_doublewrite | ON      

作为InnoDB的一个关键特性,doublewrite功能默认是开启的,但是在上述特殊的一些场景也可以视情况关闭,来提高数据库写性能。静态参数,配置文件修改,重启数据库。

4、为什么没有把double write里面的数据写到data page里面呢?

  1、double write里面的数据是连续的,如果直接写到data page里面,而data page的页又是离散的,写入会很慢。

  2、double write里面的数据没有办法被及时的覆盖掉,导致double write的压力很大;短时间内可能会出现double write溢出的情况。

adaptive hash index

哈希索引是一种非常快的等值查找方法(注意:必须是等值,哈希索引对非等值查找方法无能为力),它查找的时间复杂度为常量,InnoDB采用自适用哈希索引技术,它会实时监控表上索引的使用情况,如果认为建立哈希索引可以提高查询效率,则自动在内存中的“自适应哈希索引缓冲区”建立哈希索引。

之所以该技术称为“自适应”是因为完全由InnoDB自己决定,不需要DBA人为干预。它是通过缓冲池中的B+树构造而来,且不需要对整个表建立哈希索引,因此它的数据非常快。

InnoDB官方文档显示,启用自适应哈希索引后,读和写性能可以提高2倍,对于辅助索引的连接操作,性能可以提高5倍,因此默认情况下为开启,通过 set global innodb_adaptive_hash_index=off/on 关闭和打开该功能。。

show variables like '%ap%hash_index';

innodb_adaptive_hash_index | ON      

特点

  1、无序,没有树高

  2、降低对二级索引树的频繁访问资源 索引树高<=4,访问索引:访问树、根节点、叶子节点

  3、自适应

缺陷

  1、hash自适应索引会占用innodb buffer pool;

  2、自适应hash索引只适合搜索等值的查询,如select * from table where index_col='xxx',而对于其他查找类型,如范围查找,是不能使用的;

  3、极端情况下,自适应hash索引才有比较大的意义,可以降低逻辑读。

限制

  1、只能用于等值比较,例如=, <=>,in

  2、无法用于排序

  3、有冲突可能

  4、MySQL自动管理,人为无法干预。

状态监控

show engine innodb status\G

 Hash table size 34673, node heap has 0 buffer(s)0.00 hash searches/s, 0.00 non-hash searches/s      

  1、34673:字节为单位,占用内存空间总量

  2、通过hash searches、non-hash searches计算自适应hash索引带来的收益以及付出,确定是否开启自适应hash索引

后台线程

Master Thread 主要负责将缓冲池中的数据异步刷新到磁盘中,并协调调度其它后台进程,除此之外还包括插入缓存、undo页的回收等

IO Thread 引擎中大量使用了异步IO来处理写IO请求,IO Thread的主要工作是处理这些IO请求的回调

Purge Thread主要用于回收事务已经提交了的undo log,InnoDB支持多个PurgeThread,这样可以加快undo页的回收,默认设置4个。

show variables like 'innodb_purge_threads' \G

Pager Cleaner Thread是新引入的一个用于协助Master Thread刷新脏页到磁盘的线程,它可以减轻Master Thread的工作压力,减少阻塞

master thread根据服务器的压力分为了每一秒及每十秒的操作。

每一秒的操作包括:刷新重做日志、根据过去一秒的磁盘吞吐量来判断是否需要merge insert buffer、根据脏页在缓冲池中占比是否超过最大脏页占比及是否开启自适应刷新来刷新脏页。

每十秒的操作包括:根据过去10秒的磁盘吞吐量来刷新脏页,刷新重做日志,回收undo 页,再根据脏页占比是否超过70%刷新定量脏页。

存储文件

在MySQL中建立一张表都会生成一个.frm文件,该文件是用来保存每个表的元数据信息的,主要包含表结构定义。

在InnoDB中,存储数据都是按表空间进行存放的,默认为共享表空间,存储的文件即为共享表空间文件(ibdata1)。若设置了参数innodb_file_per_table为1,则会将存储的数据、索引等信息单独存储在一个独占表空间,因此也会产生一个独占表空间文件(ibd)。