天天看点

MySQL · 引擎特性 · MySQL logical read-ahead

这两种方式之所以带来了更高的吞吐量,都基于数据存储的连续性的假设,比如mysql使用自增字段作为pk的innodb索引表,或者是oracle使用默认的堆表,但当这样的假设条件不成立的时候,怎么办?

考虑下面的一个场景,如下图所示:

MySQL · 引擎特性 · MySQL logical read-ahead

这是一个b-tree结构,典型的innodb的索引聚簇表,这样的结构很容易构造,比如使用一个非连续的字段作为索引字段,随机对记录进行插入,这样leaf page链表上的page_no就会产生非连续性,如果进行一次全表扫描,比如 <code>checksum table t</code>,按照正常的升序扫描,leaf page扫描的page_no顺序是3, 4, 5230等等,这样其实是无法使用到innodb 的linear read-ahead,更没有办法合并io请求。

对于存在时间比较长,变更又比较多的大表,除非我们对于这个表进行重建,否则leaf page的离散性会随着时间的推移,越来越严重。但对于在线应用来说,重建又会产生比较大的运维风险,这里就介绍一种平衡的方法,logical read-ahead。

逻辑预读的概念是指,根据branch节点来预读leaf节点。

逻辑预读使用两个扫描路径:

1. 一个cursor定位到leaf page,然后根据leaf page之间的双链表,moves_up进行扫描数据;

2. 另一个cursor定位到branch节点,因为innodb b-tree结构的每一层都由双向链表进行连接,然后这个cursor就沿着branch节点进行扫描,保存扫描到的page_no,然后使用异步io,发起这些leaf page的预读取。

mysql 5.6版本上的实现方式:

1. 在<code>row_search_for_mysql</code>进行moves_up的过程中进行logical read-ahead;

2. branch节点扫描的cursor保存到trx结构中,生命周期到一个sql语句结束;

3. branch cursor扫描用户可配置的page count,临时保存到数组中,对page_no进行排序;

4. 使用libaio发起异步io读取,完成logical read-ahead。

logical read-ahead很好的提升了离散存储数据的吞吐能力,facebook在他们的mysql实例的逻辑备份过程中,对于大表的dump备份开启了此特性,备份速度有非常大的提升。

继续阅读