写点什么

Materialize MySQL 引擎:MySQL 到 Click House 的高速公路

发布于: 2021 年 01 月 21 日

摘要: MySQL 到 ClickHouse 数据同步原理及实践

引言


熟悉 MySQL 的朋友应该都知道,MySQL 集群主从间数据同步机制十分完善。令人惊喜的是,ClickHouse 作为近年来炙手可热的大数据分析引擎也可以挂载为 MySQL 的从库,作为 MySQL 的 "协处理器" 面向 OLAP 场景提供高效数据分析能力。早先的方案比较直截了当,通过第三方插件将所有 MySQL 上执行的操作进行转化,然后在 ClickHouse 端逐一回放达到数据同步。终于在 2020 年下半年,Yandex 公司在 ClickHouse 社区发布了 MaterializeMySQL 引擎,支持从 MySQL 全量及增量实时数据同步。MaterializeMySQL 引擎目前支持 MySQL 5.6/5.7/8.0 版本,兼容 Delete/Update 语句,及大部分常用的 DDL 操作。


基础概念


  • MySQL & ClickHouse


MySQL 一般特指完整的 MySQL RDBMS,是开源的关系型数据库管理系统,目前属于 Oracle 公司。MySQL 凭借不断完善的功能以及活跃的开源社区,吸引了越来越多的企业和个人用户。


ClickHouse 是由 Yandex 公司开源的面向 OLAP 场景的分布式列式数据库。ClickHouse 具有实时查询,完整的 DBMS 及高效数据压缩,支持批量更新及高可用。此外,ClickHouse 还较好地兼容 SQL 语法并拥有开箱即用等诸多优点。


  • Row Store & Column Store


MySQL 存储采用的是 Row Store,表中数据按照 Row 为逻辑存储单元在存储介质中连续存储。这种存储方式适合随机的增删改查操作,对于按行查询较为友好。但如果选择查询的目标只涉及一行中少数几个属性,Row 存储方式也不得不将所有行全部遍历再筛选出目标属性,当表属性较多时查询效率通常较低。尽管索引以及缓存等优化方案在 OLTP 场景中能够提升一定的效率,但在面对海量数据背景的 OLAP 场景就显得有些力不从心了。


ClickHouse 则采用的是 Column Store,表中数据按照 Column 为逻辑存储单元在存储介质中连续存储。这种存储方式适合采用 SIMD (Single Instruction Multiple Data) 并发处理数据,尤其在表属性较多时查询效率明显提升。列存方式中物理相邻的数据类型通常相同,因此天然适合数据压缩从而达到极致的数据压缩比。


image


使用方法


  • 部署 Master-MySQL


开启 BinLog 功能:ROW 模式

开启 GTID 模式:解决位点同步时 MySQL 主从切换问题(BinLog reset 导致位点失效)


image


  • 部署 Slave-ClickHouse


获取 ClickHouse/Master 代码编译安装

推荐使用 GCC-10.2.0,CMake 3.15,ninja1.9.0 及以上


  • 创建 Master-MySQL 中 database 及 table


image


  • 创建 Slave-ClickHouse 中 MaterializeMySQL database


image


此时可以看到 ClickHouse 中已经有从 MySQL 中同步的数据了:


image


工作原理


  • BinLog Event


MySQL 中 BinLog Event 主要包含以下几类:


image


事务提交后,MySQL 将执行过的 SQL 处理 BinLog Event,并持久化到 BinLog 文件


ClickHouse 通过消费 BinLog 达到数据同步,过程中主要考虑3个方面问题:


1、DDL 兼容:由于 ClickHouse 和 MySQL 的数据类型定义有区别,DDL 语句需要做相应转换


2、Delete/Update 支持:引入_version字段,控制版本信息


3、Query 过滤:引入_sign字段,标记数据有效性


  • DDL 操作


对比一下 MySQL 的 DDL 语句以及在 ClickHouse 端执行的 DDL 语句:


image


可以看到:


1、在 DDL 转化时默认增加了 2 个隐藏字段:_sign(-1 删除, 1 写入) 和 _version(数据版本)

2、默认将表引擎设置为 ReplacingMergeTree,以 _version 作为 column version

3、原 DDL 主键字段 runoob_id 作为 ClickHouse 排序键和分区键


此外还有许多 DDL 处理,比如增加列、索引等,相应代码在 Parsers/MySQL 目录下。


  • Delete/Update 操作


Update:


image


可以看到,ClickHouse 数据也实时同步了更新操作。


  • Delete:


image


可以看到,删除 id 为 2 的行只是额外插入了_sign == -1的一行记录,并没有真正删掉。


  • 日志回放


MySQL 主从间数据同步时 Slave 节点将 BinLog Event 转换成相应的 SQL 语句,Slave 模拟 Master 写入。类似地,传统第三方插件沿用了 MySQL 主从模式的 BinLog 消费方案,即将 Event 解析后转换成 ClickHouse 兼容的 SQL 语句,然后在 ClickHouse 上执行(回放),但整个执行链路较长,通常性能损耗较大。不同的是,MaterializeMySQL 引擎提供的内部数据解析以及回写方案隐去了三方插件的复杂链路。回放时将 BinLog Event 转换成底层 Block 结构,然后直接写入底层存储引擎,接近于物理复制。此方案可以类比于将 BinLog Event 直接回放到 InnoDB 的 Page 中。


同步策略


  • 日志回放


v20.9.1 版本前是基于位点同步的,ClickHouse 每消费完一批 BinLog Event,就会记录 Event 的位点信息到 .metadata 文件:


image


这样当 ClickHouse 再次启动时,它会把 {‘mysql-bin.000003’, 355005999} 二元组通过协议告知 MySQL Server,MySQL 从这个位点开始发送数据:


image


存在问题:


如果 MySQL Server 是一个集群,通过 VIP 对外服务,MaterializeMySQL 创建 database 时 host 指向的是 VIP,当集群主从发生切换后,{Binlog File, Binlog Position} 二元组不一定是准确的,因为 BinLog 可以做 reset 操作。


image


为了解决这个问题,v20.9.1 版本后上线了 GTID 同步模式,废弃了不安全的位点同步模式。


  • GTID 同步


GTID 模式为每个 event 分配一个全局唯一 ID 和序号,直接告知 MySQL 这个 GTID 即可,于是.metadata变为:


image


其中 0857c24e-4755-11eb-888c-00155dfbdec7 是生成 Event 的主机 UUID,1-783 是已经同步的 event 区间


于是流程变为:


image


源码分析


  • 概述


在最新源码 (v20.13.1.1) 中,ClickHouse 官方对 DatabaseMaterializeMySQL 引擎的相关源码进行了重构,并适配了 GTID 同步模式。ClickHouse 整个项目的入口 main 函数在 /ClickHouse/programs/main.cpp 文件中,主程序会根据接收指令将任务分发到 ClickHouse/programs 目录下的子程序中处理。本次分析主要关注 Server 端 MaterializeMySQL 引擎的工作流程。


  • 源码目录


与 MaterializeMySQL 相关的主要源码路径:


image


  • 服务端主要流程


ClickHouse 使用 POCO 网络库处理网络请求,Client 连接的处理逻辑在 ClickHouse/src/Server/*Handler.cpp 的 hander 方法里。以 TCP 为例,除去握手,初始化上下文以及异常处理等相关代码,主要逻辑可以抽象成:


image


  • 数据同步预处理


Client 发送的 SQL 在 executeQuery 函数处理,主要逻辑简化如下:


image


主要有三点:


1、解析 SQL 语句并生成语法树 AST

2、InterpreterFactory 工厂类根据 AST 生成执行器

3、interpreter->execute()


跟进第三点,看看 InterpreterCreateQuery 的 excute() 做了什么:


image


这里注释很明显,主要执行 CREATE 或 ATTACH DATABASE,继续跟进 createDatabase() 函数:


image


到这里,相当于将任务分发给 DatabaseMaterializeMySQL 处理,接着跟踪 loadStoredObjects 函数:


image


跟进 startSynchronization() 绑定的执行函数:


image


  • 全量同步


MaterializeMySQLSyncThread::prepareSynchronized 负责 DDL 和全量同步,主要流程简化如下:


image


ClickHouse 作为 MySQL 从节点,在 MaterializeMetadata 构造函数中对 MySQL 端进行了一系列预处理:


1、将打开的表关闭,同时对表加上读锁并启动事务

2、TablesCreateQuery 通过 SHOW CREATE TABLE 语句获取 MySQL 端的建表语句

3、获取到建表语句后释放表锁


继续往下走,执行到 metadata.transaction() 函数,该调用传入了匿名函数作为参数,一直跟进该函数会发现最终会执行匿名函数,也就是 cleanOutdatedTables 以及 dumpDataForTables 函数,主要看一下 dumpDataForTables 函数:


image


继续跟踪 tryToExecuteQuery 函数,会调用到 executeQueryImpl() 函数,上文提到过这个函数,但这次我们的上下文信息变了,生成的执行器发生变化,此时会进行 DDL 转化以及 dump table 等操作:


image


此时 InterpreterFactory 返回 InterpreterExternalDDLQuery,跟进去看 execute 函数做了什么:


image


继续跟进去看看 getIdentifierName(arguments[1])).execute() 做了什么事情:


image


进一步看 InterpreterImpl::getRewrittenQueries 是怎么转化 DDL 的:


image


完成 DDL 转换之后就会去执行新的 DDL 语句,完成建表操作,再回到 dumpDataForTables:


image


  • 增量同步


还记得 startSynchronization() 绑定的执行函数吗?全量同步分析都是在 prepareSynchronized()进行的,那增量更新呢?


image


可以看到,while 语句里有一个 binlog_event 的侦听函数,用来侦听 MySQL 端 BinLog 日志变化,一旦 MySQL 端执行相关操作,其 BinLog 日志会更新并触发 binlog_event,增量更新主要在这里进行。


image


小结


MaterializeMySQL 引擎是 ClickHouse 官方 2020 年主推的特性,由于该特性在生产环境中属于刚需且目前刚上线不久,整个模块处于高速迭代的状态,因此有许多待完善的功能。例如复制过程状态查看以及数据的一致性校验等。感兴趣的话可参考 Github 上的 2021-Roadmap,里面会更新一些社区最近得计划。以上内容如有理解错误还请指正。


引用


ClickHouse 社区文档


ClickHouse社区源码


MySQL 实时复制与实现


MaterializeMySQL引擎分析


本文分享自华为云社区《MySQL 到 ClickHouse 的高速公路-MaterializeMySQL 引擎》,原文作者:FavonianKong 。


点击关注,第一时间了解华为云新鲜技术~


发布于: 2021 年 01 月 21 日阅读数: 78
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
Materialize MySQL引擎:MySQL到Click House的高速公路