天天看點

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

摘要: 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) 并發處理資料,尤其在表屬性較多時查詢效率明顯提升。列存方式中實體相鄰的資料類型通常相同,是以天然适合資料壓縮進而達到極緻的資料壓縮比。

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

使用方法

  • 部署Master-MySQL

    開啟BinLog功能:ROW模式

    開啟GTID模式:解決位點同步時MySQL主從切換問題(BinLog reset導緻位點失效)

# my.cnf關鍵配置
gtid_mode=ON
enforce_gtid_consistency=1
binlog_format=ROW      
  • 部署Slave-ClickHouse

    擷取 ClickHouse/Master 代碼編譯安裝

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

  • 建立Master-MySQL中database及table
creat databases master_db;
use master_db;
CREATE TABLE IF NOT EXISTS `runoob_tbl`(
   `runoob_id` INT UNSIGNED AUTO_INCREMENT,
   `runoob_` VARCHAR(100) NOT NULL,
   `runoob_author` VARCHAR(40) NOT NULL,
   `submission_date` DATE,
   PRIMARY KEY ( `runoob_id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 插入幾條資料
INSERT INTO runoob_tbl (runoob_, runoob_author, submission_date) VALUES ("MySQL-learning", "Bob", NOW());
INSERT INTO runoob_tbl (runoob_, runoob_author, submission_date) VALUES ("MySQL-learning", "Tim", NOW());      
  • 建立 Slave-ClickHouse 中 MaterializeMySQL database
# 開啟materialize同步功能
SET allow_experimental_database_materialize_mysql=1;
# 建立slave庫,參數分别是("mysqld服務位址", "待同步庫名", "授權賬戶", "密碼")
CREATE DATABASE slave_db ENGINE = MaterializeMySQL('192.168.6.39:3306', 'master_db', 'root', '3306123456');      

此時可以看到ClickHouse中已經有從MySQL中同步的資料了:

DESKTOP:) select * from  runoob_tbl;

SELECT *
FROM runoob_tbl

Query id: 6e2b5f3b-0910-4d29-9192-1b985484d7e3

┌─runoob_id─┬─runoob_title───┬─runoob_author─┬─submission_date─┐
│         1 │ MySQL-learning │ Bob           │      2021-01-06 │
└───────────┴────────────────┴───────────────┴─────────────────┘
┌─runoob_id─┬─runoob_title───┬─runoob_author─┬─submission_date─┐
│         2 │ MySQL-learning │ Tim           │      2021-01-06 │
└───────────┴────────────────┴───────────────┴─────────────────┘
2 rows in set. Elapsed: 0.056 sec.      

工作原理

  • BinLog Event

MySQL中BinLog Event主要包含以下幾類:

1. MYSQL_QUERY_EVENT    -- DDL
2. MYSQL_WRITE_ROWS_EVENT -- insert
3. MYSQL_UPDATE_ROWS_EVENT -- update
4. MYSQL_DELETE_ROWS_EVENT -- delete      

事務送出後,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語句:

mysql> show create table runoob_tbl\G;
*************************** 1. row ***************************
Table: runoob_tbl
Create Table: CREATE TABLE `runoob_tbl` (
  `runoob_id` int unsigned NOT NULL AUTO_INCREMENT,
  `runoob_` varchar(100) NOT NULL,
  `runoob_author` varchar(40) NOT NULL,
  `submission_date` date DEFAULT NULL,
  PRIMARY KEY (`runoob_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
---------------------------------------------------------------
cat /metadata/slave_db/runoob_tbl.sql
ATTACH TABLE _ UUID '14dbff59-930e-4aa8-9f20-ccfddaf78077'
(
    `runoob_id` UInt32,
    `runoob_` String,
    `runoob_author` String,
    `submission_date` Nullable(Date),
    `_sign` Int8 MATERIALIZED 1,
    `_version` UInt64 MATERIALIZED 1
)
ENGINE = ReplacingMergeTree(_version)
PARTITION BY intDiv(runoob_id, 4294967)
ORDER BY tuple(runoob_id)
SETTINGS index_granularity = 8192      

可以看到:

1、在DDL轉化時預設增加了2個隐藏字段:_sign(-1删除, 1寫入) 和 _version(資料版本)

2、預設将表引擎設定為 ReplacingMergeTree,以 _version 作為 column version

3、原DDL主鍵字段 runoob_id 作為ClickHouse排序鍵和分區鍵

此外還有許多DDL處理,比如增加列、索引等,相應代碼在Parsers/MySQL 目錄下。

  • Delete/Update操作

Update:

# Mysql端:
UPDATE runoob_tbl set runoob_author='Mike' where runoob_id=2;

mysql> select * from runoob_tbl;
+-----------+----------------+---------------+-----------------+
| runoob_id | runoob_title   | runoob_author | submission_date |
+-----------+----------------+---------------+-----------------+
|         1 | MySQL-learning | Bob           | 2021-01-06      |
|         2 | MySQL-learning | Mike          | 2021-01-06      |
+-----------+----------------+---------------+-----------------+
2 rows in set (0.00 sec)

----------------------------------------------------------------
# ClickHouse端:
DESKTOP:) select *, _sign, _version from runoob_tbl order by runoob_id;

SELECT
    *,
    _sign,
    _version
FROM runoob_tbl
ORDER BY runoob_id ASC

Query id: c5f4db0a-eff6-4b49-a429-b55230c26301

┌─runoob_id─┬─runoob_title───┬─runoob_author─┬─submission_date─┬─_sign─┬─_version─┐
│         1 │ MySQL-learning │ Bob           │      2021-01-06 │     1 │        2 │
│         2 │ MySQL-learning │ Mike          │      2021-01-06 │     1 │        4 │
│         2 │ MySQL-learning │ Tim           │      2021-01-06 │     1 │        3 │
└───────────┴────────────────┴───────────────┴─────────────────┴───────┴──────────┘
3 rows in set. Elapsed: 0.003 sec.      

可以看到,ClickHouse資料也實時同步了更新操作。

  • Delete:
# Mysql端
mysql> DELETE from runoob_tbl where runoob_id=2;

mysql> select * from runoob_tbl;
+-----------+----------------+---------------+-----------------+
| runoob_id | runoob_title   | runoob_author | submission_date |
+-----------+----------------+---------------+-----------------+
|         1 | MySQL-learning | Bob           | 2021-01-06      |
+-----------+----------------+---------------+-----------------+
1 row in set (0.00 sec)

----------------------------------------------------------------
# ClickHouse端
DESKTOP:) select *, _sign, _version from runoob_tbl order by runoob_id;

SELECT
    *,
    _sign,
    _version
FROM runoob_tbl
ORDER BY runoob_id ASC

Query id: e9cb0574-fcd5-4336-afa3-05f0eb035d97

┌─runoob_id─┬─runoob_title───┬─runoob_author─┬─submission_date─┬─_sign─┬─_version─┐
│         1 │ MySQL-learning │ Bob           │      2021-01-06 │     1 │        2 │
└───────────┴────────────────┴───────────────┴─────────────────┴───────┴──────────┘
┌─runoob_id─┬─runoob_title───┬─runoob_author─┬─submission_date─┬─_sign─┬─_version─┐
│         2 │ MySQL-learning │ Mike          │      2021-01-06 │    -1 │        5 │
└───────────┴────────────────┴───────────────┴─────────────────┴───────┴──────────┘
┌─runoob_id─┬─runoob_title───┬─runoob_author─┬─submission_date─┬─_sign─┬─_version─┐
│         2 │ MySQL-learning │ Mike          │      2021-01-06 │     1 │        4 │
│         2 │ MySQL-learning │ Tim           │      2021-01-06 │     1 │        3 │
└───────────┴────────────────┴───────────────┴─────────────────┴───────┴──────────┘
4 rows in set. Elapsed: 0.002 sec.      

可以看到,删除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 檔案:

[FavonianKong@Wsl[20:42:37]slave_db]
$ cat ./.metadata
Version:        2
Binlog File:    mysql-bin.000003
Binlog Position:355005999
Data Version:   5      

這樣當 ClickHouse 再次啟動時,它會把 {‘mysql-bin.000003’, 355005999} 二進制組通過協定告知 MySQL Server,MySQL 從這個位點開始發送資料:

s1> ClickHouse 發送 {‘mysql-bin.000003’, 355005999} 位點資訊給 MySQL
s2> MySQL 找到本地 mysql-bin.000003 檔案并定位到 355005999 偏移位置,讀取下一個 Event 發送給 ClickHouse
s3> ClickHouse 接收 binlog event 并完成同步操作
s4> ClickHouse 更新 .metadata位點      

存在問題:

如果MySQL Server是一個叢集,通過VIP對外服務,MaterializeMySQL建立 database 時 host 指向的是VIP,當叢集主從發生切換後,{Binlog File, Binlog Position} 二進制組不一定是準确的,因為BinLog可以做reset操作。

s1> ClickHouse 發送 {'mysql-bin.000003’, 355005999} 給叢集新主 MySQL
s2> 新主 MySQL 發現本地沒有 mysql-bin.000003 檔案,因為它做過 reset master 操作,binlog 檔案是 mysql-bin.000001
s3> 産生錯誤複制      

為了解決這個問題,v20.9.1版本後上線了 GTID 同步模式,廢棄了不安全的位點同步模式。

  • GTID同步

GTID模式為每個 event 配置設定一個全局唯一ID和序号,直接告知 MySQL 這個 GTID 即可,于是.metadata變為:

[FavonianKong@Wsl[21:30:19]slave_db]
Version:        2
Binlog File:    mysql-bin.000003
Executed GTID:  0857c24e-4755-11eb-888c-00155dfbdec7:1-783
Binlog Position:355005999
Data Version:   5      

其中 0857c24e-4755-11eb-888c-00155dfbdec7 是生成 Event的主機UUID,1-783是已經同步的event區間

于是流程變為:

s1> ClickHouse 發送 GTID:0857c24e-4755-11eb-888c-00155dfbdec7:1-783 給 MySQL
s2> MySQL 根據 GTID 找到本地位點,讀取下一個 Event 發送給 ClickHouse
s3> ClickHouse 接收 BinLog Event 并完成同步操作
s4> ClickHouse 更新 .metadata GTID資訊      

源碼分析

  • 概述

在最新源碼 (v20.13.1.1) 中,ClickHouse 官方對 DatabaseMaterializeMySQL 引擎的相關源碼進行了重構,并适配了 GTID 同步模式。ClickHouse 整個項目的入口 main 函數在 /ClickHouse/programs/main.cpp 檔案中,主程式會根據接收指令将任務分發到 ClickHouse/programs 目錄下的子程式中處理。本次分析主要關注 Server 端 MaterializeMySQL 引擎的工作流程。

  • 源碼目錄

與 MaterializeMySQL 相關的主要源碼路徑:

ClickHouse/src/databases/MySQL   //MaterializeMySQL存儲引擎實作
ClickHouse/src/Storages/         //表引擎實作
ClickHouse/src/core/MySQL*       //複制相關代碼
ClickHouse/src/Interpreters/     //Interpreters實作,SQL的rewrite也在這裡處理
ClickHouse/src/Parsers/MySQL     //解析部分實作,DDL解析等相關處理在這裡      
  • 服務端主要流程

ClickHouse 使用 POCO 網絡庫處理網絡請求,Client連接配接的處理邏輯在 ClickHouse/src/Server/*Handler.cpp 的 hander方法裡。以TCP為例,除去握手,初始化上下文以及異常處理等相關代碼,主要邏輯可以抽象成:

// ClickHouse/src/Server/TCPHandler.cpp
TCPHandler.runImpl()
{
    ...
    while(true) {
        ...
        if (!receivePacket())  //line 184
                continue
        /// Processing Query   //line 260
        state.io = executeQuery(state.query, *query_context, ...);
    ...
}      
  • 資料同步預處理

Client發送的SQL在executeQuery函數處理,主要邏輯簡化如下:

// ClickHouse/src/Interpreters/executeQuery.cpp
static std::tuple executeQueryImpl(...)
{
    ...
    // line 354,解析器可配置
    ast = parseQuery(...); 
    ...
    // line 503, 根據文法樹生成interpreter
    auto interpreter = InterpreterFactory::get(ast, context, ...);
    ...
    // line 525, 執行器interpreter執行後傳回結果
    res = interpreter->execute();
    ...
}      

主要有三點:

1、解析SQL語句并生成文法樹 AST

2、InterpreterFactory 工廠類根據 AST 生成執行器

3、interpreter->execute()

跟進第三點,看看 InterpreterCreateQuery 的 excute() 做了什麼:

// ClickHouse/src/Interpreters/InterpreterCreateQuery.cpp
BlockIO InterpreterCreateQuery::execute()
{
    ...
    // CREATE | ATTACH DATABASE
    if (!create.database.empty() && create.table.empty())
        // line 1133, 當使用MaterializeMySQL時,會走到這裡建庫
        return createDatabase(create);  
}      

這裡注釋很明顯,主要執行 CREATE 或 ATTACH DATABASE,繼續跟進 createDatabase() 函數:

// ClickHouse/src/Interpreters/InterpreterCreateQuery.cpp
BlockIO InterpreterCreateQuery::createDatabase(ASTCreateQuery & create)
{
    ...
    // line 208, 這裡會根據 ASTCreateQuery 參數,從 DatabaseFactory 工廠擷取資料庫對象
    // 具體可以參考 DatabasePtr DatabaseFactory::getImpl() 函數
    DatabasePtr database = DatabaseFactory::get(create, metadata_path, ...); 
    ...
    // line 253, 多态調用,在使用MaterializeMySQL時
    // 上方get函數傳回的是 DatabaseMaterializeMySQL
    database->loadStoredObjects(context, ...);
}      

到這裡,相當于将任務分發給DatabaseMaterializeMySQL處理,接着跟蹤 loadStoredObjects 函數:

//ClickHouse/src/Databases/MySQL/DatabaseMaterializeMySQL.cpp
template
void DatabaseMaterializeMySQL::loadStoredObjects(Context & context, ...)
{
    Base::loadStoredObjects(context, has_force_restore_data_flag, force_attach);
    try
    {
        // line87, 這裡啟動了materialize的同步線程
        materialize_thread.startSynchronization(); 
        started_up = true;
    }
    catch (...)
  ...
}      

跟進startSynchronization() 綁定的執行函數:

// ClickHouse/src/Databases/MySQL/MaterializeMySQLSyncThread.cpp
void MaterializeMySQLSyncThread::synchronization()
{
    ...
    // 全量同步在 repareSynchronized() 進行
    if (std::optional metadata = prepareSynchronized())
    {
        while (!isCancelled())
        {
            UInt64 max_flush_time = settings->max_flush_data_time;
            BinlogEventPtr binlog_event = client.readOneBinlogEvent(...);
            {
                //增量同步偵聽binlog_envent
                if (binlog_event)
                    onEvent(buffers, binlog_event, *metadata);
            }
        }
    }
  ...
}      
  • 全量同步

MaterializeMySQLSyncThread::prepareSynchronized 負責DDL和全量同步,主要流程簡化如下:

// ClickHouse/src/Databases/MySQL/MaterializeMySQLSyncThread.cpp
std::optional MaterializeMySQLSyncThread::prepareSynchronized()
{
    while (!isCancelled())
    {
        ...
        try
        {
            //構造函數内會擷取MySQL的狀态、MySQL端的建表語句,
            MaterializeMetadata metadata(connection, ...);
            // line345, DDL相關轉換
            metadata.transaction(position, [&]() 
            {
             cleanOutdatedTables(database_name, global_context);
                dumpDataForTables(connection, metadata, global_context, ...);
            });
 
            return metadata;
        }
        ...
   }
}      

ClickHouse作為MySQL從節點,在MaterializeMetadata構造函數中對MySQL端進行了一系列預處理:

1、将打開的表關閉,同時對表加上讀鎖并啟動事務

2、TablesCreateQuery通過SHOW CREATE TABLE 語句擷取MySQL端的建表語句

3、擷取到建表語句後釋放表鎖

繼續往下走,執行到 metadata.transaction() 函數,該調用傳入了匿名函數作為參數,一直跟進該函數會發現最終會執行匿名函數,也就是cleanOutdatedTables以及dumpDataForTables函數,主要看一下 dumpDataForTables 函數:

// ClickHouse/src/Databases/MySQL/MaterializeMySQLSyncThread.cpp
static inline void dumpDataForTables(...)
{
    ...
    //line293, 這裡執行建表語句
    tryToExecuteQuery(..., query_context, database_name, comment); 
}      

繼續跟蹤 tryToExecuteQuery 函數,會調用到 executeQueryImpl() 函數,上文提到過這個函數,但這次我們的上下文資訊變了,生成的執行器發生變化,此時會進行 DDL 轉化以及 dump table 等操作:

// ClickHouse/src/Interpreters/executeQuery.cpp
static std::tuple executeQueryImpl(...)
{
    ... 
    // line 354,解析器可配置
    ast = parseQuery(...); 
    ...
    // line 503,這裡跟之前上下文資訊不同,生成interpreter也不同
    auto interpreter = InterpreterFactory::get(ast,context, ...);
    ...
    // line 525, 執行器interpreter執行後傳回結果
    res = interpreter->execute(); 
    ...
}      

此時 InterpreterFactory 傳回 InterpreterExternalDDLQuery,跟進去看 execute 函數做了什麼:

// ClickHouse/src/Interpreters/InterpreterExternalDDLQuery.cpp
BlockIO InterpreterExternalDDLQuery::execute()
{
    ...
    if (external_ddl_query.from->name == "MySQL")
    {
#ifdef USE_MYSQL
        ...
        // line61, 當全量複制執行DDL時,會執行到這裡
        else if (...->as())
            return MySQLInterpreter::InterpreterMySQLCreateQuery(
            external_ddl_query.external_ddl, cogetIdentifierName(arguments[0]),
            getIdentifierName(arguments[1])).execute();
#endif
    }
 ...
  return BlockIO();
}      

繼續跟進去看看 getIdentifierName(arguments[1])).execute() 做了什麼事情:

// ClickHouse/src/Interpreters/MySQL/InterpretersMySQLDDLQuery.h
class InterpreterMySQLDDLQuery : public IInterpreter
{
    public:
    ...
    BlockIO execute() override
    {
        ...
        // line68, 把從MySQL擷取到的DDL語句進行轉化
        ASTs rewritten_queries = InterpreterImpl::getRewrittenQueries(
                   query, context, mapped_to_database, mysql_database);
 
        // line70, 這裡執行轉化後的DDL語句
        for (const auto & rewritten_query : rewritten_queries)
            executeQuery(..., queryToString(rewritten_query), ...);

        return BlockIO{};
    }
    ...
}      

進一步看 InterpreterImpl::getRewrittenQueries 是怎麼轉化 DDL 的:

// ClickHouse/src/Interpreters/MySQL/InterpretersMySQLDDLQuery.cpp
ASTs InterpreterCreateImpl::getRewrittenQueries(...)
{
    ...
    // 檢查是否存在primary_key, 沒有直接報錯
    if (primary_keys.empty())
        throw Exception("cannot be materialized, no primary keys.", ...);
    ...
    // 添加 _sign 和 _version 列.
    auto sign_column_name = getUniqueColumnName(columns_name_and_type, "_sign");
    auto version_column_name = getUniqueColumnName(columns_name_and_type, "_version");

    // 這裡悄悄把建表引擎修改成了ReplacingMergeTree
    storage->set(storage->engine, makeASTFunction("ReplacingMergeTree", ...));
    ...
    return ASTs{rewritten_query};
}      

完成DDL轉換之後就會去執行新的DDL語句,完成建表操作,再回到 dumpDataForTables:

// ClickHouse/src/Databases/MySQL/MaterializeMySQLSyncThread.cpp
static inline void dumpDataForTables(...)
{
    ...
    //line293, 這裡執行建表語句
    tryToExecuteQuery(..., query_context, database_name, comment);
    ...
    // line29, 這裡開始 dump 資料并存放到MySQLBlockInputStream
   MySQLBlockInputStream input(connection, ...);
}      
  • 增量同步

還記得startSynchronization() 綁定的執行函數嗎?全量同步分析都是在 prepareSynchronized()進行的,那增量更新呢?

// ClickHouse/src/Databases/MySQL/MaterializeMySQLSyncThread.cpp
void MaterializeMySQLSyncThread::synchronization()
{
    ...
    // 全量同步在 repareSynchronized() 進行
    if (std::optional metadata = prepareSynchronized())
    {
        while (!isCancelled())
        {
            UInt64 max_flush_time = settings->max_flush_data_time;
            BinlogEventPtr binlog_event = client.readOneBinlogEvent(...);
            {
                //增量同步偵聽binlog_envent
                if (binlog_event)
                    onEvent(buffers, binlog_event, *metadata);
            }
        }
    }
    ...
}      

可以看到,while 語句裡有一個 binlog_event 的偵聽函數,用來偵聽 MySQL 端 BinLog 日志變化,一旦 MySQL 端執行相關操作,其 BinLog 日志會更新并觸發 binlog_event,增量更新主要在這裡進行。

// ClickHouse/src/Databases/MySQL/MaterializeMySQLSyncThread.cpp
void MaterializeMySQLSyncThread::onEvent(Buffers & buffers, const BinlogEventPtr & receive_event, MaterializeMetadata & metadata)
{ 
    // 增量同步通過監聽binlog event實作,目前支援四種event:MYSQL_WRITE_ROWS_EVENT、
    // MYSQL_UPDATE_ROWS_EVENT、MYSQL_DELETE_ROWS_EVENT 和 MYSQL_QUERY_EVENT
    // 具體的流程可以查找對應的 onHandle 函數, 不在此詳細分析
    if (receive_event->type() == MYSQL_WRITE_ROWS_EVENT){...}
    else if (receive_event->type() == MYSQL_UPDATE_ROWS_EVENT){...}
    else if (receive_event->type() == MYSQL_DELETE_ROWS_EVENT){...}
    else if (receive_event->type() == MYSQL_QUERY_EVENT){...}
    else {/* MYSQL_UNHANDLED_EVENT*/}
}      

小結

MaterializeMySQL 引擎是 ClickHouse 官方2020年主推的特性,由于該特性在生産環境中屬于剛需且目前剛上線不久,整個子產品處于高速疊代的狀态,是以有許多待完善的功能。例如複制過程狀态檢視以及資料的一緻性校驗等。感興趣的話可參考Github上的2021-Roadmap,裡面會更新一些社群最近得計劃。以上内容如有了解錯誤還請指正。

引用

ClickHouse社群文檔

ClickHouse社群源碼

MySQL實時複制與實作

MaterializeMySQL引擎分析

本文分享自華為雲社群《MySQL到ClickHouse的高速公路-MaterializeMySQL引擎》,原文作者:FavonianKong 。

點選關注,第一時間了解華為雲新鮮技術~