ClickHouse 的数据副本机制

将数据副本单独拿出来讲,是因为 ClickHouse 的副本机制灵活多变。在 ClickHouse 中要将数据写入副本,有 3 种方式:

  • 写分布式表 + internal_replication=false
  • 写分布式表 + zk + Replicated*MergeTree 引擎 + internal_replication=true​
  • 写本地表 + zk + Replicated*MergeTree 引擎 + internal_replication=true

在理解这 3 个方案前,我们需要先理解一些前置知识。

分片副本 vs 表副本

分片副本是集群里的概念,当配置一个分片时,可以配置每个分片有几个副本。

<remote_servers>
    <my-cluster-001>
        <shard>
            <!-- Optional. Shard weight when writing data. Default: 1. -->
            <weight>1</weight>
            <!-- Optional. Whether to write data to just one of the replicas. Default: false (write data to all replicas). -->
            <internal_replication>false</internal_replication>
            <replica>
                <host>example01-01-1</host>
                <port>9000</port>
            </replica>
            <replica>
                <host>example01-01-2</host>
                <port>9000</port>
            </replica>
        </shard>
        <shard>
            <weight>2</weight>
            <internal_replication>false</internal_replication>
            <replica>
                <host>example01-02-1</host>
                <port>9000</port>
            </replica>
            <replica>
                <host>example01-02-2</host>
                <port>9440</port>
            </replica>
        </shard>
    </my-cluster-001>
</remote_servers>

如上配置中,每个分片设置了两个副本,从配置的结构也可以看出,每个分片的副本数是很灵活的,比如可以配置为分片1有两个副本,分片2有三个副本。

第一次接触 ClickHouse 的人,可能会认为这样配置以后,数据就会自动在分片的副本之间同步。但是事实并非这样。

表副本是表的副本,这个概念来自 Replicated*MergeTree 系列表引擎。创建一个复制表的示例如下:

CREATE TABLE table_name
(
    EventDate DateTime,
    CounterID UInt32,
    UserID UInt32,
    ver UInt16
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/table_name', '{replica}')
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID);

示例中 { } 括起来的部分是宏变量,可以在配置文件中修改,这里不展开。

Replicated*MergeTree 表引擎有两个关键参数:

  • path - zk 中的路径,所有表副本的必须一致
  • replica_name - 副本名称,每个表副本有各自的名称

如上,具有相同 path 的表会自动同步数据

总结:

  1. 分片副本之间不会自动同步数据
  2. 表副本之间会自动同步数据

副本与分布式表的关系

了解副本的概念后,我们再来讨论这与分布式表有什么关系?

这一切都来源于一个配置 internal_replication,这个配置可以在 config.xml 中修改。他的作用是控制写分布式表的行为:

  • internal_replication = false 写分布式表时,写入分片的所有副本
  • internal_replication = true 写分布式表时,只写入分片的一个副本

当本地表为非复制表时,写入所有副本,无可厚非;当本地表为复制表时,写入所有副本似乎就有点多余了,因此在这种情况,我们可以借助复制表本身的能力来写入副本。

总结

综上所述,我们就有 3 种方案来管理数据副本:

  • 写分布式表 + internal_replication=false
  • 写分布式表 + zk + Replicated*MergeTree 引擎 + internal_replication=true​
  • 写本地表 + zk + Replicated*MergeTree 引擎 + internal_replication=true

方案一最简单,但是官方不推荐,因为在实现上没有保证数据一致性。

方法二利用分布表的写入能力 + 复制表的同步能力,这种方案在某种程度上也是可行的,不过业内很多都不推荐写分布式表,因为写分布式表,数据需要分发,会产生很多临时数据,会产生写放大,对 CPU 和内存造成额外消耗。

方案三由业务控制数据分片,直接写入本地表,该方案最灵活,性能也最好。主流的做法是在写入前端加一个 LB,由 LB 分发数据写入。

本文链接:参与评论 »

--EOF--

提醒:本文最后更新于 1044 天前,文中所描述的信息可能已发生改变,请谨慎使用。

Comments