Mysql系列(七)—Msql之InnoDB关键特性

mysql Innodb存储引擎的关键特性包括:

  • 插入缓冲
  • 两次写
  • 自适应哈希索引
  • 异步IO
  • 刷新邻接页

这些特性为InnoDB存储引擎带来了好的性能和更高的可靠性。

插入缓冲

Insert Buffer

Insert Buffer和数据页一样,也是物理页的一个组成部分。

在Innodb存储引擎中,主键是行唯一的标识符。通常记录是按照主键递增的顺序进行插入的。因此,插入聚集索引一般是

顺序的,不需要磁盘的随机读取。这类情况的插入操作,速度是非常快的。

但是并不是所有的主键插入都是顺序的,若主键是uuid这样的,那么插入将会是随机的,不连续的。同时,每个表上不可能只有一个聚集索引,还会存在多个非聚集索引。插入的时候还是按照主键的顺序进行插入的,那么对于非聚集索引叶子节点的插入就不是有序的了,这时就需要离散的访问非聚集索引页。由于随机的读取而导致了插入的性能下降。

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

Insert Buffer的使用需要同时满足以下两个条件

  • 索引是辅助索引
  • 索引不是唯一的

索引为什么不能是唯一的?因为插入缓冲时,数据库并不去查找索引页来判断记录的唯一性。如果要做唯一,就肯定会去查找,如果去查找那么就会有离散读的情况发生,从而导致Insert Buffer失去了意义。

在写密集的情况下,插入缓冲会占用过多的缓冲池内存,默认最大可以占用到1/2的缓冲池大小。通过修改IBUF_POOL_SIZE_PER_MAX_SIZE来修改。例如修改为3,则是之占用1/3的大小。

Change Buffer

InnoDB从1.0.x版本开始引入了change Buffer,可将其视为Insert Buffer的升级版。从这个版本开始,Innodb存储引擎可以对DML操作(INSERT、DELETE、UPDATE)都进行缓冲,分别为Insert Buffer、Delete Buffer、Purge buffer。

change buffer适用的对象依然是非唯一的辅助索引。对一条记录的update操作可能分为两个过程:

  • 将记录标记为删除;
  • 真正将记录删除。

因此Delete Buffer对应Update操作的第一个过程,即将记录标记为删除。Purge Buffer对应了Update操作的第二个过程,即将记录真正删除。Innodb提供了innodb_change_buffering来开启各种Buffer,该参数值分为:inserts、deletes、purge、changes、all、none。changes表示启用inserts和deletes,alll表示启用所有,none表示都不启用,默认为all。

从Innodb1.2.x开始,可以通过innodb_change_buffer_max_size来负责内存的分配。默认为25,表示最多使用1/4的缓冲池内存。该参数最大有效值为50.

Insert Buffer的内部实现

Insert Buffer的数据结构是一颗b+树。在mysql4.1之前,每张表由一颗b+树,之后全局只有一个insert buffer b+树。负责了所有表的insert buffer工作。这棵树存放在共享表空间中,默认在ibdata1中。因此通过独立表空间ibd文件恢复表数据时,往往会导致check table失败。这是因为表的辅助索引中的数据还有可能在insert buffer中。所以通过ibd文件进行恢复之后,还需要进行repair table操作来重建表上的所有辅助索引。

                        

Merge Insert Buffer

Insert Buffer中的记录什么时候合并到真正的辅助索引 呢?

  • 辅助索引页被读取到缓冲池中时
  • Insert Buffer Bitmap页追踪到该辅助索引页已无可用空间时
  • Master Thread

第一种情况为当前辅助索引页被缓存到缓冲池中时。例如:执行select语句时,需要检查Insert Buffer Bitmap页,然后确认该辅助索引是否有记录存放于Insert Buffer B+树种。若有,则进行合并。

第二种情况:Insert Buffer Bitmap页用来追踪每个辅助索引页的可用空间,并至少由1/32的空间。若小于该空间,则会进行一次合并操作。

第三种情况:Master thread 每隔10秒会进行一次Merger Insert Buffer的操作。不同之处在于每次merge的页的数量不一样,页的数量是根据srv_innodb_io_capactiy的百分比来决定的。对Innodb来说,它是随机的选择Insert Buffer B+树的一个页,然后查看该页种的space及之后需要数量的页,然后进行合并。这种算法在复杂的情况下应有更好的公平性。若进行merge时,要进行的merge的表也已经被删除,此时可用直接丢弃已经被Insert Buffer以及Change Buffer的数据记录。

两次写

Insert Buffer带给InnoDB存储性能上的提升,那么doublewrite带给的是数据页的可靠性。

当数据库发生宕机时,如果innodb存储引擎正在写入某个页的列表中,而只写了一部分,这种情况被称为部分写失效。有人可能会提出疑问?不能通过重做日志恢复么。重做日志中记录的是对页的物理操作,如果该页本身已经发生了损坏,再对其进行重做是没有意义的。doublewrite就解决了这个问题。

doublewrite由两部分组成,一部分是内存中的doublewrite buffer,大小为2M;另一部分为物理磁盘上共享表空间中连需的128个页,即两个区,大小为2MB。在对缓冲池中的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页 先复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次1M顺序的写入共享表空间的物理磁盘上,然后马上调用fsync函数进行同步磁盘。在此过程中,因为doublewrite 页是连续的,因此这个过程是顺序的,开销不是很大。

                        

上述问题,如果发生了崩溃,innodb 存储引擎可以从共享表空间中的doublewrite中找到该页的一个副本,将其复制到表空间文件,然后再应用重做日志。

注:有些文件系统本身提供了部分写失效的防范机制,如ZFS文件系统。在这种情况下就没有必要启用doublewrite了,可通过参数skip_innodb_doublewrite禁用doublewrite功能。

自适应哈希索引

哈希是一种非常快的查找方法,一般情况下时间复杂度为O(1),即一般仅需要一次查找就能定位数据。而B+树的查找次数取决于其高度,一般为3-4层。

InnoDB存储引擎会监控表上各索引页的查询,如果观察到建立哈希索引可用带来速度的提升,则建立hash索引,称作自适应哈希索引简称(AHI)。InnoDB是根据访问的频率和模式来自动地为某些热点数据建立哈希索引。

AHI有一个要求,即对这个页的连续访问模式必须是一样的,例如对于(a,b)联合索引

  • where a=xxx
  • where a=xxx and b=xxx

访问模式一样是指查询条件一样。若交替进行上述两种查询,那么不会构建AHI。还有其他要求如下:

  • 以该模式访问了100次
  • 页通过该模式访问了N次,其中N=页中记录*1/16

注:哈希索引只能用来搜索等值的查询,不适于范围查找。AHI可以通过innodb_adaptive_hash_insex来进行设置启用或者禁用。默认开启。

异步IO

为了提高磁盘操作的性能,当前的数据库系统都采用了异步IO(AIO)来处理磁盘操作。相对应的是Sync IO,用户每发出一个操作,需要等待此次操作结束后才能进行接下来的操作。用户可以在发出一个IO请求后发出另一个请求,当全部IOq请求发送完毕后,等待所有的IO操作的完成,这就叫做AIO。

AIO的另一个优势可以进行IO Merge操作,也就是将多个IO合并为1个IO。例如需要访问的页的(space,page_no)为:(8,6)

、(8,7)、(8,8)每个页的大小为16kb,那么同步需要进行3次IO操作。而AIO会判断该页是连需的,因此底层会从(8,6)开始读取48kb的页。

在InnoDB1.1.x之前,AIO的实现通过InnoDB存储引擎中的代码来模拟。而从1.1.x之后的版本,提供了内核级别的AIO支持,称为Native AIO。需要libaio库的支持,并依赖于操作系统提供的支持。

在Innodb存储引擎中,read ahead方式的读取、脏页的刷新都是AIO完成的。可以通过Innodb_use_native_aio来控制是否启用Native AIO。默认为开。

刷新邻接页

当刷新一个脏页时,存储引擎会检测该区所有的页是否为脏页,如果是则会一起刷新。

该操作会带来以下问题:

  • 是不是可能将不怎么脏的页进行了写入,而该页之后很快又变脏
  • 固态硬盘有着较高的IOPS,是否还需要这个特性?

可以通过innodb_flush_neighbors来控制是否启用。默认为开。固态硬盘建议关闭此特性。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值