当进行文件读写操作时,如果是异步读操作,发现stop_new_ops或者被设置了但is_being_truncated未被设置,会返回报错;但依然允许同步读或异步写操作(fil_io)。
当进行文件flush操作时,如果发现fil_space_t::stop_new_ops或者fil_space_t::is_being_truncated被设置了,则忽略该文件的flush操作 (fil_flush_file_spaces)。
文件预读文件预读是一项在SSD普及前普通磁盘上比较常见的技术,通过预读的方式进行连续IO而非带价高昂的随机IO。InnoDB有两种预读方式:随机预读及线性预读;Facebook另外还实现了一种逻辑预读的方式。
随机预读
入口函数:buf_read_ahead_random
以64个Page为单位(这也是一个Extent的大小),当前读入的page no所在的64个pagno 区域[ (page_no/64)*64, (page_no/64) *64 + 64],如果最近被访问的Page数超过BUF_READ_AHEAD_RANDOM_THRESHOLD(通常值为13),则将其他Page也读进内存。这里采取异步读。
随机预读受参数innodb_random_read_ahead控制
线性预读
入口函数:buf_read_ahead_linear
所谓线性预读,就是在读入一个新的page时,和随机预读类似的64个连续page范围内,默认从低到高Page no,如果最近连续被访问的page数超过innodb_read_ahead_threshold,则将该Extent之后的其他page也读取进来。
逻辑预读
由于表可能存在碎片空间,因此很可能对于诸如全表扫描这样的场景,连续读取的page并不是物理连续的,线性预读不能解决这样的问题,另外一次读取一个Extent对于需要全表扫描的负载并不足够。因此Facebook引入了逻辑预读。
其大致思路为,扫描聚集索引,搜集叶子节点号,然后根据叶子节点的page no (可以从非叶子节点获取)顺序异步读入一定量的page。
由于Innodb Aio一次只支持提交一个page读请求,虽然Kernel层本身会做读请求合并,但那显然效率不够高。他们对此做了修改,使INNODB可以支持一次提交(io_submit)多个aio请求。
入口函数:row_search_for_mysql --> row_read_ahead_logical
具体参阅这篇博文
或者webscalesql上的几个commit:
git show 2d61329446a08f85c89a4119317ae85baacf2bbb // 合并多个AIO请求,对所有的预读逻辑(上述三种)采用这种方式 git show 9f52bfd2222403f841fe5fcbedd1333f78a70a4b // 逻辑预读的主要代码逻辑 git show 64b68e07430b50f6bff5ed67374b336623db24b6 // 防止事务在多个表上读取操作时预读带来的影响 日志填充写入由于现代磁盘通常的block size都是大于512字节的,例如一般是4096字节,为了避免 “read-on-write” 问题,在5.7版本里添加了一个参数innodb_log_write_ahead_size,你可以通过配置该参数,在写入redo log时,将写入区域配置到block size对齐的字节数。
在代码里的实现,就是在写入redo log 文件之前,为尾部字节填充0(参考函数log_write_up_to)。
Tips:所谓READ-ON-WRITE问题,就是当修改的字节不足一个block时,需要将整个block读进内存,修改对应的位置,然后再写进去;如果我们以block为单位来写入的话,直接完整覆盖写入即可。
buffer pool 内存管理InnoDB buffer pool从5.6到5.7版本发生了很大的变化。首先是分配方式上不同,其次实现了更好的刷脏效率。对buffer pool上的各个链表的管理也更加高效。
buffer pool初始化在5.7之前的版本中,一个buffer pool instance拥有一个chunk,每个chunk的大小为buffer pool size / instance个数。
而到了5.7版本中,每个instance可能划分成多个chunk,每个chunk的大小是可定义的,默认为127MB。因此一个buffer pool instance可能包含多个chunk内存块。这么做的目的是为了实现在线调整buffer pool大小(WL#6117),buffer pool增加或减少必须以chunk为基本单位进行。