首页 > 数据库 > 数据库开发技术 > MyTopling 中的 iterator 缓存
MyTopling 是基于 ToplingDB 的 MySQL,分叉自 MyRocks,ToplingDB 则分叉自 RocksDB,兼容 RocksDB 接口,从而 MyTopling 可以复用 MyRocks 的大部分成果。
ToplingDB 早已开源,MyTopling 也会于近期开源。
sysbench是一个模块化的、跨平台、多线程基准测试工具,主要用于评估测试各种不同系统参数下的数据库负载情况。关于这个项目的详细介绍请看:
https://
github.com/akopytov/sys
bench
。
它主要包括以下几种方式的测试:
sysbench 的数据库 OLTP 测试支持 MySQL、PostgreSQL、Oracle,目前主要用于 Linux 操作系统,开源社区已经将 sysbench 移植到了Windows,并支持 SQL Server 的基准测试。
所以,我们用 sysbench 对 MyTopling 进行基准测试,并与主流的 MySQL 变体(原版 MySQL,MyRocks,PolarDB)进行对比。
火焰图(Flame Graph)是由 Linux 性能优化大师 Brendan Gregg 发明的,和所有其他的 profiling 方法不同的是,火焰图以一个全局的视野来看待时间分布,它从底部往顶部,列出所有可能导致性能瓶颈的调用栈。
火焰图 svg 文件可以通过浏览器打开, 它对于调用图的优点是:可以通过点击每个方块来分析它上面的内容。
火焰图的调用顺序从下到上,每个方块代表一个函数,它上面一层表示这个函数会调用哪些函数,方块的大小代表了占用 CPU 使用的长短。火焰图的配色并没有特殊的意义,默认的红、黄配色是为了更像火焰而已。
火焰的每一层都会标注函数名,鼠标悬浮时会显示完整的函数名、抽样抽中的次数、占据总抽样次数的百分比。下面是一个例子。
在某一层点击,火焰图会水平放大,该层会占据所有宽度,显示详细信息。
按下 Ctrl + F 会显示一个搜索框,用户可以输入关键词或正则表达式,所有符合条件的函数名会高亮显示。
这个火焰图是运行 sysbench select_random_range 时 MyTopling 服务器进程的。图中我加了一个绿色和蓝色的方框,绿色是
handler::read_range_first,蓝色是
myrocks...setup_scan_iterator。

可以看到,read_range_first 耗时主要包含 iterator 的 setup 和 seek,seek 是必不可少的,但是 setup 占那么多,太过于浪费了!
在 MyTopling 使用的 ToplingDB 中,SST 数据存储是 ToplingZipTable,ToplingZipTable 使用了可检索内存压缩技术,select_random_range 测试命中的 ToplingZipTable 索引是 Topling NestLoudsTrie,这是一种 嵌套的Succinct 数据结构,众所周知,Succinct 因为实现难度高,内存访问又比较随机,实际工程中很少使用,但是,ToplingZipTable 中使用的 Succinct,经过了我多年的持续优化,性能已经逼近硬件极限,所以,在这个火焰图中,原以为会是性能热点的 NestLoudsTrie,其 CPU 占比却 极低!大量的 CPU 浪费在了不必要的计算上……
通过火焰图进行性能分析,重点是发现 意料之外的时间消耗,然后找到相应的代码,看到底干了什么事。
在这个案例中,我们通过看火焰图知道 setup_scan_iterator 中会创建 iterator,再看代码就发现,这些 iterator 是在
ha_rocksdb::index_init 中创建的,然后在
ha_rocksdb::index_end 中销毁。
所以我们自然而然就会想:只要在第一次创建好之后,把这些 iterator 保存起来,后面直接使用,不再创建,就可以了啊!
创建 iterator 时指定了一个快照(snapshot),每次创建 iterator 时,都是在不同的 snapshot 上创建的,单单保存 iterator 是没用的!MyRocks 的作者们不是傻子,问题真这么简单,他们早就解决了!
所以,我们得继续分析:snapshot 本质上只是一个 SeqNum,在 snapshot 上创建 iterator 时,snapshot 的作用就只是给 iterator 提供一个 SeqNum。那么,我们只要改变 iterator 中保存的 SeqNum 不就可以了?
继续看代码,很快发现,RocksDB 确实提供了一个接口:
Iterator::Refresh ,从字面上看,好像有点符合我们的期望,但是该函数没有参数,所以它必然不能满足我们改变 Iterator 中 SeqNum 的需求。所以,我很快给 上游 RocksDB 提了一个
Feature Request: Iterator::Refresh() with a snapshot。
既然要复用 Iterator,那我们就看看都有哪些东西需要复用,在上面那个火焰图中,我们聚焦到 Seek:

然后发现,蓝色方框中的部分,这是创建单个 SST 的 Iterator,对 DBIter 的 Seek,会落到具体的 SST 上,每个 Level(严格讲是每个 Sorted Run,L0 包含多个 Sorted Run),这样,对于多次 Seek,如果落到不同的 SST 上,就需要销毁旧 SST 的 Iterator,创建新 SST 的 Iterator,在大部分情况下,这部分都不算大热点,但是如果复用 DBIter 之后,这一块必然会成为新热点。
这个小热点可以轻松消除,并且解决思路很清晰:增加新选项
ReadOptions::cache_sst_file_iter,据此决定是否缓存 SST 文件的 Iterator。此类修改,改动不大,风险低,也容易理解,容易测试,所以我改完也就
Pull Request 给上游 RocksDB 了。
RocksDB 官方只是把这个问题标记为 待解决,所以我就先做其它事情(MyTopling 一个导致 MySQL Query Plan 出错的 Bug)。
这期间还实现了一个重大优化: MultiGet IO 并发在 ToplingDB 中的协程实现,以及在 MyTopling 中的落地应用,实现过程很顺利,从确定决策开始动手,到完成在 MyTopling 中的整合及测试,一共花了三天时间。
手边紧要的事情都做完了, Feature Request: Iterator::Refresh() with a snapshot 还没有动静,我们等不起,尝试一下自己解决吧:
Iterator::Refresh 的具体实现是
ArenaWrappedDBIter::Refresh,
代码面前,
了无秘密,在该函数中,如果发现
SuperVersionNumber 发生了改变,就原地析构旧的 DBIter 并原地构造新的 DBIter,否则的话,把 Iterator 中保存的 SeqNum(相当于一个 snapshot)更新到最新。
所以,我的修改就很简单直接了:给 Refresh 增加一个 snapshot 参数,如果 snapshot 为 null,那就是旧的行为,否则的话,把 SeqNum 更新到 snapshot 的 SeqNum。再加上其它配套修改,总共也没有动多少代码。
这个修改我也 Pull Request 给上游 RocksDB 了,因为这个修改可以连带解决另一个问题: MyTopling 解决的 MyRocks Bug: create index 引发的内存问题。这个问题,我也向 RocksDB 和 MyRocks 提交了相应的 issue 和 pull request( RocksDB Issue #10536, MyRocks Issue #1211, MyRocks PR #1212)。
create index 引发的内存问题,该问题我们在 MyTopling 中的解决方案是 Workaround( MyRocks PR #1212):间歇性地(目前是每前进/后退 10万次)销毁旧 Iterator,创建新 Iterator。
因为 Iterator 引用了一个 Version 对象,而该 Version 对象会将 MemTable 和 SST pin 住,并且因为 RocksDB 实现的原因,后续新的 MemTable 对象也会被 pin 住,从而在一个长命百岁的 Iterator 活着时,持续向 DB 写入数据会导致 OOM 内存爆炸。
我们之前的 workaround 解决方案就是缩短 Iterator 对象的生命周期,从而间接地解决问题。我们现在增加了 Refresh(snapshot) 之后,只需要间歇性地 Refresh(snapshot) 就可以了。理论上这个间歇性的Refresh(snapshot) 可以放在 Iterator 本身的实现之内,不过这有点越俎代庖,就维持原样吧。
我们增加了新的 MySQL 变量:
rocksdb_reuse_iter(为了兼容性,仍然用 rocksdb 前缀)。解决问题的思路很简单,但实现及测试、debug 还是费了好些力气。
最终效果非常振奋人心,sysbench select_random_ranges 性能提高了 70%!
MyTopling 即将开源,开源的同时,我们会推出 DBaaS 内测版,欢迎大家体验, 进入官网,扫码加入 Topling 用户群!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70013015/viewspace-2912795/ ,如需转载,请注明出处,否则将追究法律责任。