MySQL InnoDB 技术内幕

第一章 MySQL 体系架构和存储引擎

mysql是数据库也是数据库实例

mysql  是一个单进程多线程架构的数据库  daemon 守护进程

当启动实例时,MySQL数据库会去读取配置文件,根据配置文件的参数来启动数据库实例。

用以下命令可以查看当Mysql 数据库实例启动时,会在哪些位置查找配置文件。

mysql —help | grep my.cnf


mysql由一下几部分组成:

连接池组件

管理服务和工具组件

sql接口组件

查询分析器组件

优化器组件

缓冲(cache)组件

插件式存储引擎

物理文件


最大的特点就是其插件式的表存储引擎。存储引擎是基于表的,而不是数据库。

mysql独有的插件式体系架构,存储引擎的是mysql区别于其他数据库的一个重要的特性。存储引擎的好处是,每个存储引擎都有各自的特点,能够根据具体的应用建立不同存储引擎表。

MySql 存储引擎的改进http:/code.google.com.p/mysql-heap-dynamic-rows/

MySQL 数据库开源特性,存储引擎可以分为MySql官方存储引擎和第三方存储引擎。Inno存储引擎,是mysql 数据库OLTP(online transaction Processing在线事务处理)应用中使用最广泛的存储引擎。

1.3.1  InnoDB存储引擎

InnoDB存储引擎支持事务,其设计目标主要面向在线事务OLTP的应用。其特点是行级锁设计、支持外键,并支持类似于Oracle的非锁定读,从5.5.8开始InnoDB存储引擎是默认的存储引擎。

InnoDB通过使用多版本并发控制MVCC来获得高并发性,并且实现了SQL标准的四种隔离级别,默认的为REPEATABLE级别,同时使用一种被称为next-key locking的策略来避免幻读(phantom)现象的产生。除此之外,InnoDB存储引擎还提供了插入缓冲(Insert buffer)、二次写(double write)、自适应哈希索引(adaptive hash index)、预读(read ahead)等高性能和高可用的功能。

对于表中的数据存储,InnoDB存储引擎采用聚集(cluster)的方式,因此每张表的存储都是按主键的顺序进行存放。如果没有显式地在表定义时指定主键,InnoDB存储引擎会为每一行生成一个6个字节的ROWID ,并以此作为主键。

1.3.2 MyISAM存储引擎

MyISAM存储引擎不支持事务、表锁设计,支持全文索引,主要面向一些OLAP数据库应用。

1.3.3 NDB 存储引擎是一个集群存储引擎,类似于oracle的RAC集群,不过Oracle,Oracle RAC share everything 架构不同是,器结构是share nothing的集群架构,因此能够提供更高的可用性。NDB的特点是数据全部放在内存中,因此主键查询(primary key lookups)的速度极快,并且通过添加NDB数据存储节点(data Node)可以线性地提高数据库性能,是高可用、高性能的集群系统。

关于NDB存储引擎有一个问题值得注意,那就是NDB存储引擎的链接操作JOIN时在MySQL数据库层完成的,而不是在存储引擎层完成的,这意味着复杂的链接操作需要巨大的网络开销,因此查询速度很慢,如果解决了这个问题,NDB存储引擎的市场应该是非常大的。

1.3.4 Memory存储引擎

Memory存储引擎(之前称之为HEAP存储引擎)将表中的数据存放在内存中,如果数据库重启或者发生奔溃,表中的数据将消失。它非常适合用于存储临时数据的临时表,以及数据仓库中的维度表。Memory存储引擎默认使用hash索引,而不是我们熟悉的B+树索引。

Memory存储引擎速度快,但只支持表锁,并发性能较差,并且不支持TEXT和BLOB列类型,最重要的是,村粗变长字段varchar时是按照定常字段方式进行的,因此会浪费内存。

1.3.5 Archive 存储引擎

Archve 存储引擎只支持INSERT 和SELECT操作,从MySQL开始支持索引。

1.3.6 Federated 存储引擎

1.3.7 Maria 存储引擎

Maria存储引擎是新开发的引擎,设计目标主要是用来取代原有的MyISAM存储殷勤,从而成为MySQL的默认存储引擎。Maria存储引擎的开发者是MySQL的创始人之一。Maria存储引擎的特点是:支持缓存数据和索引文件,应用了行锁设计,提供了MVCC功能,支持事务和非事务安全的选项,以及更好的BLOB字符类型的处理性能。

总结: MySQL 的InnoDB 存储引擎的效率在OLTP中效率更好,对于ETL MyISAM存储引擎更具有优势。

当数据量大于1000万时MySQL的性能会急剧下降吗?不!,MySQL是数据库,不是文件,随着数据行的增加,性能当然会有所下降,但是这些下降不是线性的,如果用户选择了正确的存储引擎,以及正确的配置,再多的数据量MySQL也能承受。InnoDB上存储超过1TB的数据,还有一些其他网站使用InnoDB存储引擎,处理插入/更新的操作平均800次/s

衡量标准:

存储容量的限制、事务的支持、锁的粒度、MVCC支持、支持的索引、备份和复制等

各大存储引擎

第2章 InnoDB存储引擎

InnoDB是事务安全的MySQL存储引擎,设计上采用了类似于Oracle数据库的架构。通常来说,InnoDB存储引擎是OLTP应用中核心代表的首选存储引擎。同时,也正式因为InnoDB的存在,才使得MySQL数据库变得有魅力。

2.1 InnoDB存储引擎概述

最早由Innobase Oy公司开发,其特点行锁设计、支持MVCC、支持外键、提供一致性非锁定读,同时被设计用来最有效地利用以及内存和CPU。

2.3 InnoDB体系架构

Inno存储引擎有多快内存块,可以认为这些内存组成了一个大的内存池,负责如下工作:

1、维护所有进程/线程需要访问的多个内部数据结构

2、缓存磁盘上的数据,方便快速地读取,同时在对磁盘文件的数据修改之前在这里缓存

3、重做日志redo缓冲





后台线程的主要负责刷新内存池中的数据,保证缓冲池的内存缓存的是最近的数据,此外将已经修改的数据文件刷新到磁盘文件,同时保证在数据库发生的异常情况下,Inno能恢复到正常的运行状态。

2.3.1 后台线程

InnoDB存储引擎是多线程的模型,因此其后台有多个不同的后台线程,负责处理不同的任务。

1、Master Thread

Master Thread 是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲(INSERT BUFFER)、UNDO页的回收等。

2、IO Thread

在InnoDB存储引擎中大量使用了AIO(Async IO)来处理写IO请求,这样可以极大提高数据库的性能。而IO Thread 的工作主要负责这些IO请求的回调(call back)处理。

3、Purge(净化) Thread

事务被提交后,其所使用的undolog可能不在需要,因此需要PurgeThread来回收并分配的undo页面。Purge操作可以独立到单独的线程中,以此来减轻Master Thread的工作,从而提高CPU的使用率以及提升存储引擎的性能。用户可以再MySQL 数据库的配置文件中添加如下命令来启动独立的Purge  Thread

innodb_Purge_threads=1

2.3.2内存

1、缓冲池

InnoDB 存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可将视为基于磁盘的数据库系统。

缓冲池简单说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响,首先将从磁盘读到的页存放在缓冲池中,这个过程称为将页“FIX”在缓冲池中。下一次在读相同的也页时,首先判断该页是否在缓冲池中,若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。否则读取磁盘上的页。

对于数据库中也的修改操作,则首先修改缓冲池中的页,然后再以一定的频率刷新到磁盘上。这里需要注意的是,页从缓冲池刷新回磁盘的操作斌不是每次页发生更新时触发,而是通过一种称为CheckPoint的机制刷新回磁盘。同样这也是为了提高数据库的整体性能。

具体来看,缓冲池中缓存的数据页类型有:索引页,数据页、undo页、插入缓冲(insert buffer)、自适应哈希索引(adaptive hash index)、InnoDB存储的锁信息(lock info)、数据字典信息(data dictionary)等,InnoDB存储引擎中内存的结构情况。





允许多个缓冲池实例,每个页根据哈希值平均分配到不同的缓冲池实例中,这种做的好处是减少数据库内部的资源竞争,增加数据库的并发处理能力。可以通过参加innodb_buffer_pool_instances来进行配置,该值默认为1

2、LRU List、Free List和Flush List

通常来说,数据库中的缓冲池是通过LRU (Latest Recent Used),最近最少使用算法来进行管理的。即最频繁使用的页在LRU列表的前端,而最少使用的页在LRU列表的尾端。当缓冲池不能存放新读取到页时,将首先释放LRU列表中的尾端的页。

InnoDB存储引擎中,缓冲池中页的大小默认为16KB,同样使用LRU算法对缓冲池进行管理,稍有不同的是InnoDB存储引擎对传统的LRU算法做了一些优化。在InnoDB的存储引擎中,LRU列表中还加入了midpoint位置。新读取到的页,虽然是最新访问的页面,但并不是直接放入到LRU列表的首部,而是放入到LRU列表的midpoint位置。这个算法在InnoDB存储引擎下称为midpoint insertion strategy,在默认配置下,该文职在LRU列表长度得5/8处。midpoin位置可由参数innodb_old_blocks_pct控制,在InnoDB存储引擎中,把midpoint之后的列表称为old列表,之前的列表称为new列表。可以简单的理解为new列表中的页都是最为活跃的热点数据。

为什么不直接采用朴素的LRU算法,直接将读取的页放入到LRU列表的首部?这是因为若直接将读取到的页放入到LRU的首部,那么某些SQL操作可能会使缓冲池中的页被刷新出,从而影响缓冲池的效率。常见的这类操作为索引或数据的扫描操作。这类操作需要访问列表中的许多页,设置是全部的页,而这些页面通常来说又仅在这次查询操作中需要,并不是活跃的热点数据。如果页被放入LRU列表的首部,那么非常可能将所需要的热点数据从LRU列表中移除,而在下一次需要读取该页时,InnoDB存储引擎需要再次访问磁盘。

为了解决这个问题,InnoDB存储引擎引入了另一个参数来进一步管理LRU列表,这个参数是innodb_old_blocks_time,用于表示页读取到mid位置后需要等待多久才会被加入到LRU列表的热端。因此当需要执行上述所说的SQL操作时,可以通过下面的方法尽可能使LRU列表中热点数据不被刷出。

LRU列表用来管理已经读取的页,但当数据库刚启动时,LRU列表是空的,即没有任何页,这是页都存放在free 列表中,当需要从缓冲池中分页时,首先从Free列表中查找是否有可用的空闲页面,若有则将该页从free列表中删除,放入到LRU列表中,否则根据LRU算法,淘汰LRU列表末尾的页面,将该内部空间分配给新的页面。当页从LRU列表的old部分加入到new部分时候,称此时操作为page made young,而因为inno_old_blocks_time的设置导致页从old部分移动new部分操作称为page not made young。可用通过命令SHOW ENGINE INNODB STATUS来观察LRU列表及Free列表的使用情况和运行状态。

在LRU列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘上的页数据产生的不一致。这时数据库会通过CHECKPOINT机制将脏页刷新回磁盘,而Flush列表中,LRU列表用来管理缓冲池中的页的可用性,Fluash列表用来管理将页刷新回磁盘,二者互补影响。

3、重做日志缓冲

InnoDB存储引擎的内存区域除了有缓冲池外,还有重做日志缓冲(redo log buffer)。InnoDB存储引擎首先将重做日志信息放入到这个缓冲区,然后按一定的频率将其刷新到重做日志文件。重做日志缓冲一般不需要社会得很大,因为一般情况下每秒钟会将重做日志缓冲刷新到新的日志文件,因此用户只需要保证每秒产生的事务在这个缓冲大小之内即可,该值可由配置参数innodb_log_buffer_size控制,默认为8MB




在通常情况下,8MB的重做日志缓冲池足以满足绝大部分的应用,因为重做日志在系列三种情况下会将重做日志缓冲中的内容刷新到外挂磁盘的重做日志文件中。

1、Master Thread每一秒将重做日志缓冲刷新到重做日志文件;

2、每个事务提交时会将重做日志缓冲刷新到重做日志文件;

3、当重做日志缓冲池剩余空间小于1/2,重做日志日志缓冲刷新到重做日志文件。

4、额外的内存池

在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行分配,

2.4 checkpoint技术

缓冲池的设计目的为了协调CPU速度和磁盘速度的鸿沟。因此页的操作首先都是在缓冲池中完成。如果一条DML语句,如果update或者Delete改变了页中国的记录,那么此时页是脏的,即缓冲池中的页的版本要比磁盘的新。数据库需要将新版本的页从缓冲池刷新到磁盘。

倘若每次一个页发生变化,就将新页面的版本刷新到磁盘,那么这个开销是非常大的。若热点数据中在某几个页中,那么数据库的性能将变得非常差。同时如果在从缓冲池将页的新版本刷新到磁盘时发生了宕机,那么数据就不能恢复了。为了避免发生数据丢失的问题,当前事务数据库系统普遍都采用了Write Ahead Log策略,即当事务提交时,先重做日志,再修改页面。当由于发生宕机导致数据丢失时,通过重做日志来完成数据的恢复,这也是事务ACID中D(Durability持久性)的要求。

思考下面的场景,如果重做日志可以无限地增大,同时缓冲池也足够大,能够缓冲所有数据库的数据,那么是不需要将缓冲池中的页的新版本刷新回磁盘。因为当发生宕机的时候,完全可以通过重写日志来恢复整个数据库中的数据到宕机发生的时刻。但是这需要两个前提条件:

1、缓冲池可以缓存数据库中所有的数据;

2、重做日志可以无限大

对于第一个前提条件,有经验的用户都知道,当数据库开始创建时,表中没有任何数据。缓冲池的确可以缓存所有的数据库文件

。。。。。

Checkpoint 技术的目的是解决一下几个问题:

1、缩短数据库恢复的时间

2、缓冲池不够用,将脏页刷新到磁盘

3、重做日志不可用时,刷新脏页面;

当数据库发生宕机时,数据库不需要重做所有的日志,因为CheckPoint之前的页都已经刷新回磁盘。故数据库值需要对Checkpoint后的重做日志进行恢复,这样就大大缩短了恢复时间。

此外,当缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,若此页面为脏页,那么需要强制执行Checkpoint,将脏页也就是也的新版本刷回磁盘。

重做日志出现不可用的情况是因为当前事务数据库系统对重做日志的设计都是循环使用的,并不是让其无限增大,这从成成本及管理上都是比较困难的,重做日志可以被重用的部分是指这些重做日志已经不再需要,即当数据库发生宕机时,数据库恢复操作不需要这部分的操作日志,因此这部分就可以被覆盖重用。若此时重做日志还需要使用,那么必须强制产生checkpoint,将缓冲池中的页至少刷新到当前重做日志的位置。

对于InnoDB存储引擎而言言,其是通过LSN(log Sequence Number)来标记版本的。而LSN是8字节的数字,其单位是字节。每个页有LSN,重做日志中也有LSN,checkpoint也有LSN,

在InnoDB存储引擎中,Checkpoint发生的时间,条件以脏页的选择都非常复杂。而Checkpoint所做的事情无法外乎是将缓冲池中的昂页刷回到磁盘。不同之处在于每次刷新多少页到磁盘,每次从哪里取脏页,以及什么时间触发Checkpoint,在InnoDB存储引擎内部,有两种checkpoint,分别为:

1、Sharp Checkpoint

2、Fuzzy Checkpoint

Sharp Checkpoint发生在数据库关闭时将所有脏页都刷新回磁盘,这是默认的工作方式

参数 innodb_fast_shutdown=1

但是若数据库在运行时也使用Sharp Checkpoint,那么数据库的可用性就会受到很大的影响,所以在InnoDB存储引擎内部使用Fuzzy CheckPoint进行页面刷新,即只刷新一部分脏页,而不是刷新所有脏页回磁盘。

下面几种情况的Fuzzy CheckPoint

1、Master Thread Checkpoint

2、FLUSH_LRU_LIST_CheckPoint

3、Async/Sync Flush Checkpoint

4、Dirty page too much Checkpoint

Master Thread CheckPoint 以每秒或每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘。这个过程是异步的,即此时Inno搜索引擎开源进行其他的操作,用户查询线程不会阻塞。

FLUSH_LRU_LIST CheckPoint是因为InnoDB存储引擎需要保证LRU列表中需要有差不多100多个空闲页可供使用。需要检查LRU列表中是都有足够的可用的空间操作发生在用户查询线程中,显然会阻塞用户的查询操作。倘若没有100个可用空闲页面,那么InnoDB存储引擎会将LRU列表尾端的页移除。如果这些有脏页,那么需要进行checkpoint,而这些页是来自LRU列表的因此成为FLUSH_LRU_LIST checkpoint。

在mysql 5.6 也就是InnoDB1.2.x版本开始,这个检查被一个单独的Page Cleanner线程进行,并且用户可以通过innodb_lru_scan_depth控制LRU列表中可用页面的数量,该值默认为1024

Async/Sync flush checkpoint 指的是重做日志文件不可用的情况,这是需要强制将一些页刷新回磁盘,而此时脏页是从脏页列表中选取的。若将已经写入重做日志的LSN标记为redo_lsn,将已经刷新回磁盘最新也的LSN记为checkpoint_lsn

checkpoint_age = radio_lsn - checkpoint_lsn

再定义以下的变量:

async_water_mark = 75% * total_redo_log_file_size

sync_what_mark = 90% * total_log_file_size

2.5 Master Thread 工作方式

InnoDB存储引擎的主要工作都是在一个独立的后台线程Master Thread中完成,

2.5.1 InnoDB 1.0.x版本之前的Master Thread

Master Thread 具有最高的线程的优先级别。其内部由多个循环loop组成:主循环loop、后台循环backgroup loop、刷新循环flush loop、暂停循环suspend  loop。Master Thread 会根据数据库运行的状态在loop,backgroud loop 、flush loop 和suspend loop中进行切换。

可以看到,loop循环通过thread sleep来实现,这意味着所谓的每秒一次或者10秒一次的操作时不精确的,在负载很大的情况下可能会有延迟(delay),只能说大概在这个频率下。当然InnoDB源代码中还通过了其他方法来尽量保证这个频率。

每秒一次的操作包括:

1、日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)

2、合并插入缓冲(可能)

3、至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能)

4、如果当前没有用户活动,则切换到background loop(可能)

即使某个事务还没有提交,InnoDB存储引擎任然每秒会将重做日志缓冲中的内容刷新到重做日志文件。这一点是必须要知道的,因为这可以很好地解释为什么再大的事务提交commit的时间也是很短的。

合并插入缓冲(Insert Buffer)并不是每秒都会发生的。InnoDB存储引擎会判断当前一秒内发生的IO次数是否小于5,如果小于5次,InnoDB认为当前的IO压力很小,可以执行合并插入缓冲的操作。

同样,刷新100个脏页也不是每秒会发生的,InnoDB存储引擎通过判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了配置文件中innodb_max_dirty_pages_pct 这个参数(默认为90%),如果超过了这个阈值,InnoDB存储引擎认为需要做磁盘同步的操作,将100脏页写入磁盘中。

10秒的操作

1、刷新100个脏页到磁盘(可能的情况下)

2、合并至多5个插入缓冲(总是)

3、将日志缓冲刷新到磁盘(总是)

4、删除无用的undo页(总是)

5、刷新100个或者10个脏页到磁盘(总是)。

在以上的过程中,InnoDB存储引擎会先判断过去10秒内磁盘的IO操作是否是小于200次,,如果是,InnoDB存储引擎认为目前有足够的磁盘IO操作能力,因此将100个脏页刷新到磁盘。接着,InnoDB存储引擎会合并插入缓冲。不同于每秒一次操作时可能发生的合并插入缓冲的操作,这次的合并插入缓冲操作总会在这个阶段进行。之后,InnoDB存储引擎会进行一次将日志缓冲刷新到磁盘的操作。这和每秒一次时发生的操作是一样的。

接着InnoDB存储引擎会执行full purge操作,即删除无用的Undo页面。对表进行update、delete这类操作,原先的行被标记为删除,但是因为一致性读(consistance read)的关系,需要保留这些行版本的信息。但是在full purge的过程中,InnoDB存储引擎会判断当前事务系统中已被删除的行是都可以删除,比如有时候可能还有查询操作需要读取之前的版本undo的信息,如果可以删除,InnoDB会立即将其删除。从源代码中可以发现,InnoDB存储引擎在执行full purge操作时,每次最多尝试回收20个undo页。

然后InnoDB存储引擎会判断缓冲池页面的比例buf_get_modified_ratio_pct,如果有超过50%的脏页面,则刷新100个脏页到磁盘,如果脏页的比例小于70%,则只需要刷新10%的脏页面到磁盘。

若当前没有用户活动(数据库空闲)或者数据库关闭(shutdown),就会切换到这个循环。background loop会执行以下操作:

1、删除无用的undo页(总是)

2、合并20个插入缓存(总是)

3、跳回到主循环(总是)

4、不断刷新100个页面知道服务条件(可能,跳转到flush loop中完成)。

若flush loop中也什么事情可以做,InnoDB存储引擎会切换到suspend_loop,将Master Thread挂起,等待事情的发生。若用户启用enable了InnoDB存储引擎,却没有使用任何InnoDB存储引擎的表,那么Master Thread 总是处于挂起的状态。

2.5.2 InnoDB1.2.x版本之前的Master Thread

InnoDB存储引擎对于IO其实是有限制的,在缓冲池想磁盘刷新时,其实都做了一定的硬编码(hard coding)。在磁盘技术飞速发展的今天,当固态硬盘SSD出现时,这种规定在很大程度上限制了InnoDB存储引擎对磁盘IO的性能,尤其是写入性能。

InnoDB存储引擎最大只会刷新100个脏页到磁盘,合并20个插入缓冲。如果再写入密集的应用程序中,每秒可能产生大于100个脏页面,如果是产生了大于20个插入缓冲的情况,Master Thread 似乎会“忙不过来”,或者说它总是做的很慢。即使磁盘能在1秒内处理多于100个脏页的写入和20个插入缓存的合并,但是由于hard coding。Master Thread 也只会选择刷新100个脏页和并20个插入缓冲。同时,当发生宕机需要恢复时,由于很多数据还没有刷新回磁盘,会导致恢复的时间可能需要很久,尤其是对于insert buffer来说。经过谷歌团队的修正提供了innodb_io_capacity的百分比来进行控制,规则如下:

1、在合并插入缓冲时,合并插入缓冲的数量为innodb_io_capacity值的5%

2、在从缓冲区刷新脏页时,刷新脏页的数量为Innodb_io_capacity

如果用户使用了SSD类的磁盘,或者将尽快磁盘做了RAID,当存储设备拥有更高的IO速度时,玩可以将innodb_io_capacity的值调得再高点,知道符合磁盘IO的吞吐量为止。

另一个问题是,参数innodb_max_dirty_pages_pct默认值的问题,在InnoDB 1.0.x版本之前,该值的默认为90,意味着脏页占缓冲池的90%,但是该值“太大”了,因为InnoDB存储引擎在每秒刷新缓冲池和flush loop时会判断这个值,如果该值大于innodb_max_dirty_pages_pct,才刷新100个脏页,如果有很大的内存,或者数据库服务器的压力很大,这时刷新脏页的速度反而会降低。同样在数据库的恢复阶段可能需要更多的时间。

在很多论坛上都有对这个问题的讨论,有人甚至将这个值调到 20或10,然后测试发现性能所有提高,但是将Innodb_max-dirty_pages_pct调到20或10会增加磁盘的压力,系统的负担还是会有所增加的。google在这个问题上进行了测试,证明20并不是一个最优值。从1.0.x版本开始,inno_max_dity_pages_pct默认值变为75和google测试的80%比较接近,这样既可以加快刷新脏页频率,又能保证磁盘IO的负载。

InnoDB 1.0.x版本带来的另一个参数是innodb_adaptive_flushing(自适应地刷新)

该值影响每秒刷新脏页的数量,不刷新脏页;大于innodb_max_dirty_pages_pct时,刷新100个脏页。随着inno_adaptive_flushing参数的引入,InnoDB存储引擎会通过一个名为buf_flush_get_desired_flush_rate的函数来判断需要刷新脏页最合适的数量。粗略地翻阅源代码后发现buf_flush_get_desired_flush_rate通过判断产生重做日志undo log的速度来决定适合的刷新脏页的数量。因此当脏页的比例小于innodb_max_dirty_pages_pct时,也会刷新一定量的脏页。

还有一个改变:之前每次进行full purge操作时,最多回收20个undo页面,从InnoDB 1.0.x版本开始引入参数innodb_purge_batch_size这个参数可以控制每次full purge回收的undo页的数量。该参数的默认值为20,并可以动态地对其进行修改。

2.5.3 InnoDB 1.2.x版本的Master Thread

srv_master_do_idle_tasks() 之前版本中每10秒的操作

srv_master_do_active_tasks()之前版本每秒的操作

从Master Thread线程分离到一个单独的Page Cleaner Thread,从而减轻了Master Thread的工作,同时进一步挺高了行的并发性。

2.6 InnoDB关键特性

1、插入缓冲 (Insert buffer)

2、两次写(double write)

3、自适应哈希索引(Adaptive Hash Index)

4、异步IO(Async IO)

5、刷新邻接页(Flush Neighbor Page)

一般情况下按照主键有序插入,如自增长,这样的插入方式速度快,

如遇到UUID或者辅助索引,secondary index 并非有序,需要离散地访问非聚集索引页,由于随机读写的存在而导致插入操作性能下降,而B+树的特性决定了非聚集索引插入的离散性。

插入缓冲(insert buffer)

InnoDB存储引擎开创性地设计了Insert Buffer,对于非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集的引页是都在缓冲池中,若在,则直接插入,若不在,则先放入到一个Insert Buffer对象中。然后再以一定的频率和情况进行Insert buffer和辅助索引页子节点的merge(合并)操作,这是通常能将多个插入合并到同一个操作中(因为在一个索引页面中),这就大大提高了非聚集索引插入的性能。

insert buffer 的使用需要满足以下两个条件:

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

2、索引不是唯一索引(unique)的

3、insert buffer的内部实现

以前版本每个表都有一颗Insert buffer B+树

现在的版本内部只要一个B+树,负责对所有的表的辅助索引进行Insert buffer。而这棵B+树存放在共享表的空间中,默认也是在ibdata1。因此试图通过独立表空间ibd文件恢复表中的数据时,往往会导致CHECK TABLE失败。这是因为表的辅助索引中的数据可能换在Insert Buffer中,也是共享空间中,所以通过ibd文件进行回复后,还需要进行REPAIR TABLE操作来重建表上的所有辅助索引。

Insert Buffer 是一个B+树,因此由也是节点和非叶子节点组成。非叶子节点存放的是查询的search key(键值)构造如下



search key一共占用9个字节,其中space表示带插入记录所在的表的表空间id,space占用4个字节。marker占用1个字节,它是用来兼容老版本的insert buffer,offset表示页所在的偏移量,占4个字节

当一个辅助索引要插入到页(space,offset)时,如果这个页不在缓冲池中,那么InnoDB存储引擎首先根据上述规则苟泽一个search key,接下来查询insert buffer 这课B+树,然后再将这条记录插入到Insert Buffer B+树的叶子节点中

2.6.2两次写 doublewrite




2.6.3 自适应哈希索引

哈希(hash)是一种非常快的查找方式,在一般的情况下这种查找的时间复杂度为O(1),即一般仅需要一次查找就能定位的数据。而B+树的查找次数,取决于B+树的高度,在生产环境中,B+树的高度一般为3-4层,古只需要3-4查询。

InnoDB存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。AHI是通过缓冲池的B+树页构造而来,因此建立的速度很快,而且不需要对整张表构建哈希索引。InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点建立哈希索引。

2.6.4异步IO

2.6.5刷新邻接页

Flush Neighbor Page(刷新紧邻页)的特性,其工作原理为:当刷新一个脏页时,Inno存储引擎会检测该页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。

推荐阅读更多精彩内容