一、背景
MyTopling 是基于 ToplingDB 的 MySQL,分叉自 MyRocks,ToplingDB 则分叉自 RocksDB,兼容 RocksDB 接口,从而 MyTopling 可以复用 MyRocks 的大部分成果。
ToplingDB 和 MyTopling 目前都已经开源。
ToplingDB 的一个重要功能是 分布式 Compact(文档),去年我们实现了 托管Todis 的 分布式 Compact 支持。同样是基于 ToplingDB,分布式 Compact 自然也是 MyTopling 的重要功能。
系列文章:MyTopling 分布式 Compact(一):从多线程到多进程
系列文章:MyTopling 分布式 Compact(二):CompactionFilter
二、PropertiesCollector 介绍
在稍微复杂点的 RocksDB 应用中,都少不了使用 CompactionFilter,相对而言,PropertiesCollector 比 CompactionFilter 露脸的机会要少一些。
在 MyRocks 中,PropertiesCollector 的具体实现是Rdb_tbl_prop_coll
,用来在 Flush/Compact 过程中来收集 SST 的统计信息,MyTopling 通过 ToplingDB 的 Web 可视化系统对此信息可视化如下:
原版 MyRocks 未保存 name 字段,这里的 name 字段是 MyTopling 新增的,我们为此还向 MyRocks 提交了一个 Pull Request: properties_collector: (un)materialize: add missing m_name。
这个文件所属的 table 是 TPCC 的 bmsql_stock
,其主键是联合键 (s_w_id, s_i_id)
,上图中的 distinct per prefix 就是在该 SST 文件中,该 index_id=2170
(对应 bmsql_stock) 的 cardinality,即 s_w_id
只有 2 个不同的值,而 (s_w_id, s_i_id)
联合键有 31003 个不同的值(与总行数 rows 相同)。
CREATE TABLE `bmsql_stock` ( `s_w_id` int NOT NULL, `s_i_id` int NOT NULL, `s_quantity` int DEFAULT NULL, `s_ytd` int DEFAULT NULL, `s_order_cnt` int DEFAULT NULL, `s_remote_cnt` int DEFAULT NULL, `s_data` varchar(50) DEFAULT NULL, `s_dist_01` char(24) DEFAULT NULL, `s_dist_02` char(24) DEFAULT NULL, `s_dist_03` char(24) DEFAULT NULL, `s_dist_04` char(24) DEFAULT NULL, `s_dist_05` char(24) DEFAULT NULL, `s_dist_06` char(24) DEFAULT NULL, `s_dist_07` char(24) DEFAULT NULL, `s_dist_08` char(24) DEFAULT NULL, `s_dist_09` char(24) DEFAULT NULL, `s_dist_10` char(24) DEFAULT NULL, PRIMARY KEY (`s_w_id`,`s_i_id`));
复制
所以,和 Rdb_compact_filter 一样,Rdb_tbl_prop_coll 在执行过程中也需要访问数据字典。
三、Rdb_tbl_prop_coll 怎么支持分布式 Compact
原版 RocksDB 也有跟 ToplingDB 分布式 Compact 类似的东西,叫做 RemoteCompaction(Experimental),不支持插件化的 PropertiesCollector 。
ToplingDB 从一开始就通过 SidePlugin 来管理所有的 DB 配置信息,PropertiesCollector 就是其中之一,所以,在 MyTopling 中,我们只需要修改一点代码,将 Rdb_tbl_prop_coll_factory 和相应的序列化方法注册到 SidePlugin 体系即可,在 MyTopling 分布式 Compact(二):CompactionFilter 中我们以 CompactionFilter 为例,在架构上对此进行了详细阐述。在 Rdb_tbl_prop_coll 这里,我们深入一点细节,对整个流程进行一个梳理:
在 DB 结点上,Rdb_tbl_prop_coll 通过指针直接访问 ddl_manager 全局变量,在 Compact Worker 结点,脱离了 DB,没有相应的 ddl_manager(该全局对象仍然存在,但是个无效对象)。
我们对代码的修改方式依然是“微创手术法”,在 MyRocks 内部接口上,关键修改非常微小:
原本 MyRocks 辗转将全局变量 ddl_manager
的地址传给 Rdb_tbl_prop_coll,然后在 compact 过程中调用m_ddl_manager->safe_find
,MyTopling 在此处改为调用 m_find_key_def
:
当然,麻烦的地方肯定不在这里,而是在 Compact Worker 结点上,find_key_def
怎么工作,整个 Rdb_tbl_prop_coll 又怎么“虚拟化”成象是在 DB 上一样执行的。
首先,Rdb_tbl_prop_coll_factory_SerDe 的准备工作:
DeSerialize 和 Serialize 执行的操作相对应,在 Compact Worker 上:
在 DB 上,DeSerialize 是在 Compact 即将结束时调用:
这其中每个环节都非常简单,甚至简单到“明显没有 Bug”。
其中的 DEBG 和 TRAC 都是最开始一股脑从 Todis 中的 CompactionFilter 拷过来的,实际上从未开启过。
上面有两个函数 write_key_def_rng,read_key_def_all,这是是 MyTopling 为支持分布式 Compact 中传输数据字典而新增的,这里面的代码就不是那么“明显没有 Bug”了。好在使用了 topling-zip 中的序列化框架,使得代码简洁清晰了很多,Rdb_tbl_prop_coll_factory_SerDe 的 Serialize/DeSerialize 也用到了该序列化框架。