镰鼬LL

导航

 
本节内容同 MySQL笔记六-innodb存储引擎是一起的,由于内容比较多,排版不想弄,就单独放一节,但是大序号不变,都是六
 
 
insert buffer
insert buffer需要满足的条件 :insert的 索引是非唯一辅助索引。
 
什么是insert buffer?简单的说就是当insert一条数据,在更新辅助索引树时,如果需要插入的数据页不在内存里,则将数据放到insert buffer里,并返回insert成功(实际上没有),等到一定条件下,将多个insert命令一起执行,减少与磁盘的交互,提高insert性能。
 
为什么需要insert buffer?由于主键一般是自增的,当insert一条数据时是按照主键顺序存放,这是insert数据是很快的,因为只需要顺序存放即可。可是对于表中的辅助索引而言,insert的数据可就不是顺序的了,innodb需要一个个的比对索引数据的大小,再将索引数据存放在合适的位置(这里插入的不是数据,而是索引字段的数据和对应的主键值),如果每一条数据都插入都落盘,就存在大量的随机io,将严重的拖慢insert速度,更严重的是一张表上很可能不止一个辅助索引,更更严重的是,还会存在页分裂,这都会对insert效率产生负面的影响。所以,需要insert buffer
 
insert buffer 为何需要满足以上的条件 以上的条件有两个关键,1,非唯一索引;2,辅助索引。
关于第一的关键点,假设索引是唯一的,那么再插入索引数据时,为了保证唯一性,需要将整个索引树的数据都遍历一遍,而此操作就会伴随着大量的io;问:这些io都是顺序io啊,辅助索引表的存放是顺序的,顺序io不是很快么。答,1,顺序io确实很快,但是如果数据量很大,即使是顺序io也会耗时久;2,既然已经把索引数据都load 进内存了,哪还有使用insert buffer的必要么,直接insert不就行了。
关于第二个关键点,假设不是辅助索引,那就是主键,主键都是有唯一性要求,同第一点要求。
PS:主键是UUID类型呢,那不是更不行,又唯一,又无序
        
insert buffer是怎么是实现的?insert buffer的实现是通过一颗 B+ tree实现,该树存放在共享表空间,即ibdata1里。它的非叶子节点的结构是,
  space id 
marker
offset
其中space id是表的唯一表示,marker是兼容老版本的标志,offset是表示该页的偏移量,这三个字段结合在一起称之为 search key。这个search key可以用于标识 某个具体的页。而非叶子节点的结构是
  space id 
marker
offset
metadata
secondary index record
前面三个字段一样,metadata字段记录了每个记录进入insert buffer的顺序。secondary index record 字段记录了实际insert的记录。
那么insert buffer的流程就是这样,一条insert sql进来,首先判断是否满足使用insert buffer的条件,是则生成对应的 search key,放进tree里,然后再将这条记放到这个search key下的叶子节点中。
 
什么时候merge insert buffer呢?
1,当辅助索引页被读到内存里时,这是可以去insert buffer B+ tree里查找是否有属于此页的insert buffer 记录,如果有则merge,等同与搭顺风车。
2,当辅助索引页空间紧张时,当检测到某个辅助索引页可用空间小于1/32时,需要强制将其读进内存进行页分裂,此时又可以搭便车
3,在Master thread 中会主动机进行merge insert buffer 操作,不同的是,此次merge会一次随机merge多个页,具体由srv_innodb_io_capacity参数控制。
ps 还有个特殊页名为 insert buffer bitmap,每个此种页追踪16384个辅助索引页,它会记录每个辅助索引页的剩余空间和是否有记录在insert buffer里。所以merge insert buffer 的三种场景其实都需要 insert buffer bitmap页参与,它很重要。
 
insert buffer 的发展  目前insert buffer已经升级成change buffer,innodb可以对DML操作,比如insert ,update,delete都进行缓存,对应的就是insert buffer,update buffer ,delete buffer,实现和关键都一样,就不深入了。其中提一嘴delete buffer,还记得之前提到过的隐藏字段么,每一行都有个类似is_delete的隐藏字段,当update/delete 删除一条数据时,实际上修改is_delete字段来标识删除,实际时通过purge thread 完成实际的删除操作。
 
 
 
doublewrite
    什么是doublewrite  如果innodb正在写入某个页到表中时,发生宕机,此时页只写了一半,这种情况称之为 partial page write(部分写失效),是有可能导致数丢失的。crash recover时,由于页本身已经损坏,即使是rodo log页无法修复,因为redo log记录的是对页的物理操作,比如在page 1 offset 100的位置写 "a"
但此时页本身已经损坏,其他位置的数据已经丢失,所以仅有redo log无法修复。所以再使用redo log 恢复之前,需要先找到该页的副本,然后还原该页,最后再apply redo log恢复数据。这种操作就是 double write
    
    为什么需要doublewrite 上文说了
    
    怎么实现 doublewrite? doublewrite 由两部分组成,第一部分是 doublewrite buffer,大小是2MB,这是在内存里的。第二部分在共享表空间里,大小也是2MB,这是在磁盘里的。当需要flush dirty page时,不是直接刷进磁盘,而是通过 memcpy 函数将脏页 cpoy到 doublewrite buffer里,这是内存操作,很快;然后通过doublewrite buffer分两次,每次1MB的,顺序的将数据写入共享表空间,这是顺序写,也很快;最后立即调用fsync函数刷新磁盘,这是随机写,就慢了,注意fsync到磁盘的数据是 doublewrite buffer里的数据,不是原来脏页里的数据。如果发生宕机导致页损坏,就可以使用共享表空间里的数据来恢复数据页,然后用redo log 恢复数据。
 
 
 
AHI 自适应哈希索引
    AHI是由innodb自己为了加速某些查询条件建立的索引,自适应是因为此操作只能由innodb控制,人工无法干预,当然你有关闭此功能的权利,哈希索引就是哈希索引,是一种如果合适那就最快的索引。
    建立AHI的条件 1,以某种查询条件 连续 执行了 100次,比如where a=  1234,注意是连续,不是总共。2,页通过 某种查询条件连续访问了 N次,N=页中的记录/16,注意是连续,不是总共。
    性能上使用AHI 将提高2倍的读写性能。
 
 
 
异步IO
      异步io可以类比下merge insert buffer,异步IO也有个 merge io操作,就是将多个io合并成一个,比如io1 访问page 1 offset 100~200,io 2 访问page 1 offset 300~400,io3 访问 page 1 offset 200 ~300,那么就可以merge 这三个io操作合成 io 访问page 1 offset 100~400,此功能可手动关闭
 
 
刷新临接页
    如名字所示,就是在刷新某个脏页时,检查下同区(extent)的所有页有没有页需要刷新,如果有就一起刷新,好处是将多个io操作合并成一个。但是有一下两个问题,1,顺带刷新的页可能很快又脏了,2,现在普遍使用ssd,iops足够高。归根结底,启用此功能弊大于利,故可以考虑关闭此功能。如果使用sas,可以开启,不过应该很少了吧。
 
 
 
 
 
 
 
 
 
 
 
 
posted on 2021-07-11 21:32  镰鼬LL  阅读(52)  评论(0编辑  收藏  举报