[experimental] MaterializedMySQL

这是一个实验性的特性,不应该在生产中使用.

创建ClickHouse数据库,包含MySQL中所有的表,以及这些表中的所有数据。

ClickHouse服务器作为MySQL副本工作。它读取binlog并执行DDL和DML查询。

创建数据库

  1. CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster]
  2. ENGINE = MaterializedMySQL('host:port', ['database' | database], 'user', 'password') [SETTINGS ...]
  3. [TABLE OVERRIDE table1 (...), TABLE OVERRIDE table2 (...)]

引擎参数

  • host:port — MySQL 服务地址.
  • database — MySQL 数据库名称.
  • user — MySQL 用户名.
  • password — MySQL 用户密码.

引擎配置

  • max_rows_in_buffer — 允许在内存中缓存数据的最大行数(对于单个表和无法查询的缓存数据)。当超过这个数字时,数据将被物化。默认值:65 505
  • max_bytes_in_buffer - 允许在内存中缓存数据的最大字节数(对于单个表和无法查询的缓存数据)。当超过这个数字时,数据将被物化。默认值: 1 048 576
  • max_rows_in_buffers - 允许在内存中缓存数据的最大行数(用于数据库和无法查询的缓存数据)。当超过这个数字时,数据将被物化。默认值: 65 505
  • max_bytes_in_buffers - 允许在内存中缓存数据的最大字节数(用于数据库和无法查询的缓存数据)。当超过这个数字时,数据将被物化。默认值: 1 048 576
  • max_flush_data_time - 允许数据在内存中缓存的最大毫秒数(对于数据库和无法查询的缓存数据)。当超过这个时间,数据将被物化。默认值: 1000
  • max_wait_time_when_mysql_unavailable - MySQL不可用时的重试间隔(毫秒)。负值禁用重试。默认值:1000。 — allows_query_when_mysql_lost—允许在MySQL丢失时查询物化表。默认值:0(false)。
  1. CREATE DATABASE mysql ENGINE = MaterializedMySQL('localhost:3306', 'db', 'user', '***')
  2. SETTINGS
  3. allows_query_when_mysql_lost=true,
  4. max_wait_time_when_mysql_unavailable=10000;

MySQL服务器端配置

为了MaterializedMySQL的正确工作,有一些必须设置的MySQL端配置设置:

  • default_authentication_plugin = mysql_native_password,因为 MaterializedMySQL 只能授权使用该方法。
  • gtid_mode = on,因为基于GTID的日志记录是提供正确的 MaterializedMySQL复制的强制要求。

    :::info “注意” 当打开gtid_mode时,您还应该指定enforce_gtid_consistency = on

虚拟列

当使用MaterializeMySQL数据库引擎时,ReplacingMergeTree表与虚拟的_sign_version列一起使用。

  • _version — 事务版本. 类型 UInt64.
  • _sign — 删除标记. 类型 Int8. 可能的值:
    • 1 — 行没有删除,
    • -1 — 行已被删除.

支持的数据类型

MySQL ClickHouse
TINY Int8
SHORT Int16
INT24 Int32
LONG UInt32
LONGLONG UInt64
FLOAT Float32
DOUBLE Float64
DECIMAL, NEWDECIMAL Decimal
DATE, NEWDATE Date
DATETIME, TIMESTAMP DateTime
DATETIME2, TIMESTAMP2 DateTime64
YEAR UInt16
TIME Int64
ENUM Enum
STRING String
VARCHAR, VAR_STRING String
BLOB String
GEOMETRY String
BINARY FixedString
BIT UInt64
SET UInt64

Nullable 已经被支持.

MySQL中的Time 类型,会被ClickHouse转换成微秒来存储

不支持其他类型。如果MySQL表包含此类类型的列,ClickHouse抛出异常”Unhandled data type”并停止复制。

规范和推荐用法

兼容性限制

除了数据类型的限制之外,还有一些限制与MySQL数据库相比有所不同,这应该在复制之前解决:

  • MySQL 中的每个表都应该包含 PRIMARY KEY
  • 对于表的复制,那些包含 ENUM 字段值超出范围的行(在 ENUM 签名中指定)将不起作用。

DDL Queries

MySQL DDL 语句会被转换成对应的ClickHouse DDL 语句,比如: (ALTER, CREATE, DROP, RENAME). 如果ClickHouse 无法解析某些语句DDL 操作,则会跳过。

数据复制

MaterializedMySQL不支持直接的 INSERTDELETEUPDATE 查询。然而,它们在数据复制方面得到了支持:

  • MySQL INSERT查询被转换为_sign=1的INSERT查询。
  • MySQL DELETE查询被转换为INSERT,并且_sign=-1
  • 如果主键被修改了,MySQL的 UPDATE 查询将被转换为 INSERT_sign=1 和INSERT 带有_sign=-1;如果主键没有被修改,则转换为INSERT_sign=1

MaterializedMySQL 数据表查询

SELECT 查询从 MaterializedMySQL表有一些细节:

  • 如果在SELECT查询中没有指定_version,则 FINAL修饰符被使用,所以只有带有 MAX(_version)的行会返回每个主键值。

  • 如果在SELECT查询中没有指定 _sign,则默认使用 WHERE _sign=1。所以被删除的行不是 包含在结果集中。

  • 结果包括列注释,以防MySQL数据库表中存在这些列注释。

索引转换

在ClickHouse表中,MySQL的 PRIMARY KEYINDEX 子句被转换为 ORDER BY 元组。

ClickHouse只有一个物理排序,由 order by 条件决定。要创建一个新的物理排序,请使用materialized views

注意

  • _sign=-1 的行不会被物理地从表中删除。
  • 级联 UPDATE/DELETE 查询不支持 MaterializedMySQL 引擎,因为他们在 MySQL binlog中不可见的 — 复制很容易被破坏。 — 禁止对数据库和表进行手工操作。
  • MaterializedMySQLoptimize_on_insert设置的影响。当MySQL服务器中的一个表发生变化时,数据会合并到 MaterializedMySQL 数据库中相应的表中。

表重写

表覆盖可用于自定义ClickHouse DDL查询,从而允许您对应用程序进行模式优化。这对于控制分区特别有用,分区对MaterializedMySQL的整体性能非常重要。

这些是你可以对MaterializedMySQL表重写的模式转换操作:

  1. CREATE DATABASE db_name ENGINE = MaterializedMySQL(...)
  2. [SETTINGS ...]
  3. [TABLE OVERRIDE table_name (
  4. [COLUMNS (
  5. [col_name [datatype] [ALIAS expr] [CODEC(...)] [TTL expr], ...]
  6. [INDEX index_name expr TYPE indextype[(...)] GRANULARITY val, ...]
  7. [PROJECTION projection_name (SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY]), ...]
  8. )]
  9. [ORDER BY expr]
  10. [PRIMARY KEY expr]
  11. [PARTITION BY expr]
  12. [SAMPLE BY expr]
  13. [TTL expr]
  14. ), ...]

示例:

  1. CREATE DATABASE db_name ENGINE = MaterializedMySQL(...)
  2. TABLE OVERRIDE table1 (
  3. COLUMNS (
  4. userid UUID,
  5. category LowCardinality(String),
  6. timestamp DateTime CODEC(Delta, Default)
  7. )
  8. PARTITION BY toYear(timestamp)
  9. ),
  10. TABLE OVERRIDE table2 (
  11. COLUMNS (
  12. client_ip String TTL created + INTERVAL 72 HOUR
  13. )
  14. SAMPLE BY ip_hash
  15. )

COLUMNS列表是稀疏的;根据指定修改现有列,添加额外的ALIAS列。不可能添加普通列或实体化列。具有不同类型的已修改列必须可从原始类型赋值。在执行CREATE DATABASE 查询时,目前还没有验证这个或类似的问题,因此需要格外小心。

您可以为还不存在的表指定重写。

!!! warning “警告” 如果使用时不小心,很容易用表重写中断复制。例如:

  1. * 如果一个ALIAS列被添加了一个表覆盖,并且一个具有相同名称的列后来被添加到源MySQL表,在ClickHouse中转换后的ALTER table查询将失败并停止复制。
  2. * 目前可以添加引用可空列的覆盖,而非空列是必需的,例如 `ORDER BY` `PARTITION BY`。这将导致CREATE TABLE查询失败,也会导致复制停止。

使用示例

MySQL 查询语句:

  1. mysql> CREATE DATABASE db;
  2. mysql> CREATE TABLE db.test (a INT PRIMARY KEY, b INT);
  3. mysql> INSERT INTO db.test VALUES (1, 11), (2, 22);
  4. mysql> DELETE FROM db.test WHERE a=1;
  5. mysql> ALTER TABLE db.test ADD COLUMN c VARCHAR(16);
  6. mysql> UPDATE db.test SET c='Wow!', b=222;
  7. mysql> SELECT * FROM test;
  1. ┌─a─┬───b─┬─c────┐
  2. 2 222 Wow!
  3. └───┴─────┴──────┘

ClickHouse中的数据库,与MySQL服务器交换数据:

创建的数据库和表:

  1. CREATE DATABASE mysql ENGINE = MaterializedMySQL('localhost:3306', 'db', 'user', '***');
  2. SHOW TABLES FROM mysql;
  1. ┌─name─┐
  2. test
  3. └──────┘

数据插入之后:

  1. SELECT * FROM mysql.test;
  1. ┌─a─┬──b─┐
  2. 1 11
  3. 2 22
  4. └───┴────┘

删除数据后,添加列并更新:

  1. SELECT * FROM mysql.test;
  1. ┌─a─┬───b─┬─c────┐
  2. 2 222 Wow!
  3. └───┴─────┴──────┘

来源文章