1)Insert Buffer
Insert Buffer可以说是InnoDB存储引擎中比较关键的特性了。但是它并不是如名字般是缓冲池的组成部分,InnoDB缓冲池中确实存在Insert Buffer中的信息,但是Insert Buffer和数据页一样是物理页的一个组成部分。
InnoDB的插入:
如下创建的表test1:
mysql> create table test1(a int auto_increment,b varchar(30), primary key(a));
Query OK, 0 rows affected (0.13 sec)
其中a是自增列,如果对a插入null值,因为自身自增长的属性,其值会自动增长,同时页中的行记录按照主键的值进行顺序存放。在一般情况不需要随机读取另一个页的记录。因此对于这种情况的插入速度是十分快的。**并不是所有主键插入都是顺序的,若主键的数据类型是UUID这种,那么插入和辅助索引一样,是随机的。**即使主键自增,但是插入的是特定的值,而不是NULL值还是可能导致插入并非连续的情况。
mysql> create table test2(a int auto_increment,b varchar(30), primary key(a),key(b));
Query OK, 0 rows affected (0.12 sec)
这种情况差生了一个非聚集且不唯一的索引,在进行插入操作的时候,数据页的存放还是按照主键a的顺序进行存放的,但是对于非聚集索引叶子节点的插入不再是顺序的,这时就需要 离散的访问非聚集索引页,由于随机读取的存在而导致了插入性能的下降。但这并不是由于b字段上的索引错误而是因为B+树的特性决定了非聚集索引插入的离散性。**注意,在一些情况下,辅助索引的插入依然是顺序的或者说是比较顺序的。**比如用户购买表中的时间字段,通常用户的购买时间是一个辅助索引,用来根据时间条件进行查询,但是在插入时确实根据时间的递增而进行插入的,可以说插入也是‘比较’顺序的。
InnoDB的Insert Buffer:
InnoDB存储引擎开创性的设计Insert Buffer,对于非聚集索引的插入和更新操作,不是每一次都直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池,如果在就直接插入了,如果不在则会先放入一个Insert Buffer对象中,看似数据库这个非聚集的索引已经插入到叶子节点,实际上并没有,只是被存放在了另外一个位置。然后在以一定频率和情况进行Insert Buffer和辅助索引的叶子节点的merge(合并)操作,这样实现了将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能。
Insert Buffer的使用条件
索引是辅助索引(Secondary index)
索引不是唯一的(unique)
当满足以上两个条件的时候,Innodb存储引擎会使用Insert Buffer来提高插入操作的性能。不过需要考虑这样的情况:当应用程序进行大量的插入操作的时候,这些都涉及到了不唯一的非聚集索引都是用到了Insert Buffer。如果这个时候数据库发生了宕机,一定会有大量的Insert Buffer并没有合并到实际的非聚集索引中,这时恢复就需要很长时间。辅助索引不能是唯一的,因为在插入缓冲时数据库并不去查找索引页来判断插入的记录的唯一性,如果去查找一定会有离散读取的情况发生,从而导致Insert Buffer失去意义。
查询插入缓冲的相关信息:
## mysql> show engine innodb status\G
## INSERT BUFFER AND ADAPTIVE HASH INDEX
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
seg size显示当前的Insert Buffer大小为2*16kb;free list len 代表空闲列表的长度,size代表已经合并记录页的数量。
doublewrite带给Innodb存储引擎的是数据页的可靠性。
假设一个场景:
当一个数据库发生宕机的时候,可能Innodb存储引擎正在写入到某个页到表中,而这个页只写了一部分,比如一个16kb的页只写了前4kb,之后就发生宕机了,这种情况被称为部分写失效(partial page write)。在Innodb存储引擎未使用doublewrite技术前,出现过因为部分写失效导致的数据丢失的情况。
可以使用redo进行恢复,这的确是一个办法但是必须认识到,redo日志中记录的是对页的物理操作,比如偏移量800写「aaa」。如果这个页本身已经被损坏那么在对它进行重做是没有意义的。也就是说,**在应用重做日志前,用户还需要一个页的副本,当写入失效发生的时候,先通过页的副本来还原这个页,在进行重做,这就是doublewrite**。InnoDB中的doublewrite体系结构如下:
![](https://secure-static.wolai.com/static/oXXNYtMGtDhzC7YT27sEy2/image.png)
doublewrite由两部分组成,一部分是内存中的doublebuffer,大小为2MB,另一部分是物理磁盘上的共享表空间中的连续的128个页,即两个区(extent),大小同样为2MB。在对缓冲池的脏页进行刷新的时候并不直接写磁盘,而是回通过memcpy函数将脏页复制到内存中的doublewrite buffer,之后doublewrite buffer再分两次,每次1MB的顺序地写到共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘,避免缓冲写带来的问题。在这个过程中因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不大。在完成doublewrite后,再将doublewrite buffer中的页写入到各个表空间中,此时写入是离散的。
可以使用以下命令来查看doublewrite的运行情况:
mysql> show global status like 'innodb_dblwr%'\G;
*************************** 1. row ***************************
Variable_name: Innodb_dblwr_pages_written
Value: 374
*************************** 2. row ***************************
Variable_name: Innodb_dblwr_writes
Value: 9
2 rows in set (0.00 sec)
可以看到doublewrite一共写了374个页但是实际写入次数只有9次(这里存在64:1的关系,如果系统在高峰期时Innodb_dblwr_pages_written:Innodb_dblwr_writes远小于64:1,那么可以说明系统写入压力并不高。)
当系统在将页写入到磁盘的过程中发生了崩溃,在恢复过程中,Innodb存储引擎可以从共享表空间中的doublewrite中找到该页的一个副本,将其复制到任何一个表空间文件,再应用重做日志。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fe7QbJdg-1603691309849)(https://secure-static.wolai.com/static/s8eXtNv8Gktf2ZRgDY2xGc/image.png)]
如上所示就是数据库由doublewrite进行恢复的情况。
参数skip_innodb_double_write可以禁用二次写功能。但是可能会发生之前说的写失效问题。但是如果是多个从服务器(slave),需要比较快的性能,这个参数或许可以起到作用。但如果需要高可靠性还是需要保证doublewrite开启。
(有些文件系统本身就提供了部分写失效的防范机制比如ZFS文件系统,这种情况就不要启用doublewrite了。)
AHI,哈希是一种很快的查询方法,一般仅需要一次就可以定位数据。常见的B+树的查找次数,取决于B+树的高度,在生产环境中B+树的高度一般是34层,所以需要34次查询。
自适应哈希索引的自适应性体现在:Innodb存储引擎会监控对表上各索引页的查询,如果观察到建立hash索引可以带来速度的提升,则建立hash索引,这种索引就称为hash索引(AHI)。
自适应哈希索引特点:根据B+树页的构造而来,因此建立速度很快,而且不需要对整张表建立hash索引。Innodb存储引擎会自动根据访问频率和模式来自动地为某些热点页建立哈希索引。
自适应哈希索引的建立要求:1)对这个页的访问模式是一样的。例如对于对于(a,b)这样的联合索引页,访问情况可以如下:【1】where a = xxx 【2】where a=xxx and b=xxx。访问模式一样是指查询的条件一样,如果上面两种查询条件交替出现,那么Innodb存储引擎并不会对这个页构造AHI。
2)使用这种模式访问了100次。
3)页通过该模式访问了N次,其中 N=页中记录*1/16
官方文档说明:
启用AHI后,读取和写入的速度可以提高2倍,辅助索引的连接操作性能可以提高5倍,所以AHI是一种非常好的优化模式,设计的思想就是数据库自优化的(self-tuning)。
可以通过下面的方式查看AHI的使用情况,包括AHI的大小,使用情况,每秒使用AHI搜索的情况。注意:哈希索引只能用来搜索等值查询,其他查找类型,如范围查询等是不能使用哈希索引的。查看方法包括(show engine innodb status和innodb_adaptive_hash_index参数)。默认该参数开启。
mysql> show engine innodb status \G
*************************** 1. row ***************************
Type: InnoDB
Name:
# Status:
# 2020-10-21 15:17:18 0x7f0a6d86d700 INNODB MONITOR OUTPUT
Per second averages calculated from the last 10 seconds
## ...
## INSERT BUFFER AND ADAPTIVE HASH INDEX
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 796967, node heap has 0 buffer(s)
Hash table size 796967, node heap has 1 buffer(s)
Hash table size 796967, node heap has 3 buffer(s)
Hash table size 796967, node heap has 0 buffer(s)
Hash table size 796967, node heap has 1 buffer(s)
Hash table size 796967, node heap has 0 buffer(s)
Hash table size 796967, node heap has 0 buffer(s)
Hash table size 796967, node heap has 0 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
为了提高磁盘的操作性能,当前的数据库系统都采用异步IO(Async IO即AIO)的方式来处理磁盘操作。
与AIO对应的是SIO(sync IO),即每进行一次IO操作需要等待此次操作结束后才能继续接下来的操作。但是如果客户发出的是一条索引扫描的查询,那么这个SQL可能需要扫描多个索引页,也就是进行多次IO操作。在每扫描一个页并等待其完成后再进行下一次扫描,这是没有必要的。
AIO就是,用户在发送出一个IO请求后立即再发出另一个IO请求,当全部IO请求发出后,等待所有IO操作的完成,这就是AIO。
除此之外,AIO还有另一个优点,可以进行IO merge操作,也就是将多个IO合并为一个IO,这样可以提高IOPS的性能。例如用户需要访问页的(space,page_no)为:(8,6)(8,7)(8,8)每个页的大小是16kb,那么同步IO需要进行三次IO操作。而AIO会判断到这三个页是连续的,这时AIO就会发送一个IO请求,从(8,6)开始读取48KB的页。
控制AIO参数:innodb_use_native_AIO注意这个参数依赖于低层环境,这个参数用来控制是否启用Native AIO,在linux环境下默认为开启的。
在Innodb存储引擎中,read ahead方式的读取都是通过AIO完成的,脏页的刷新也就是磁盘写入操作也都是由AIO完成的。
刷新邻接页(Flush Neighbor Page),工作原理:当刷新一个脏页的时候,Innodb存储引擎会检测该页所在区(extent)的所有页。如果是脏页,那么一起刷新。而实现的方式就是上面的AIO,所以这个特性在传统的机械磁盘下有着明显的优势。
相关参数:innodb_flush_neighbors,视硬盘性能而定,性能较差的传统磁盘建议开启,高性能磁盘可以关闭(设置为0)。
信息加载中,请等待
微信客服(速回)
微信客服(慢回)