利用复制表ReplicatedMergeTree实现ClickHouse高可用

  |   0 评论   |   1,119 浏览

ClickHouse高可用的方案

ClickHouse中为了实现分布式查询,一般使用Distributed表,其子表可以使用各种引擎,但最常用也是功能最强大的引擎就是*MergeTree系列了。在实际使用中通常有以下两种高可用的方案。

一、子表使用MergeTree引擎,Insert写Distributed表

在这种情况下,分布式表会跨服务器分发插入数据。 为了写入分布式表,必须要配置分片键(最后一个参数)。当然,如果只有一个分片,则写操作在没有分片键的情况下也能工作,因为这种情况下分片键没有意义,所有数据都将发送到一个分片。

通常将internal_replication参数设置为false,这样写操作会将数据写入所有副本以实现高可用。实质上,这意味着要分布式表本身来复制数据。这种方式不如使用复制表的好,因为不会检查副本的一致性,并且随着时间的推移,副本数据可能会有些不一样。

在这种方案的实际使用中,笔者多次遇到DistrDirMonitor导致的故障

阻塞


kernel: INFO: task DistrDirMonitor:56052 blocked for more than 120 seconds.
kernel: "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.

OomKill

kernel: Out of memory: Kill process 56074 (DistrDirMonitor) score 894 or sacrifice child
kernel: Killed process 56074 (DistrDirMonitor) total-vm:450312764kB, anon-rss:174896660kB, file-rss:0kB, shmem-rss:0kB

二、子表使用ReplicatedMergeTree引擎,Insert写子表

可以将internal_replication参数设置为true,因为可以由ClickHouse来保证数据副本的一致性。你可以自已指定要将哪些数据写入哪些服务器,并直接在每个分片上执行写入,并且你可以使用任何分片方案对于复杂业务特性的需求,这可能是非常重要的。官方推荐这种方案。

使用复制表并不影响效率

SELECT 查询并不需要借助 ZooKeeper ,复本并不影响 SELECT 的性能,查询复制表与非复制表速度是一样的。查询分布式表时,ClickHouse的处理方式可通过设置 max_replica_delay_for_distributed_queries 和 fallback_to_stale_replicas_for_distributed_queries 修改。

对于每个 INSERT 语句,会通过几个事务将十来个记录添加到 ZooKeeper。(确切地说,这是针对每个插入的数据块; 每个 INSERT 语句的每 max_insert_block_size = 1048576 行和最后剩余的都各算作一个块。)相比非复制表,写 zk 会导致 INSERT 的延迟略长一些。但只要你按照建议每秒不超过一个 INSERT 地批量插入数据,不会有任何问题。一个 ZooKeeper 集群能给整个 ClickHouse 集群支撑协调每秒几百个 INSERT。数据插入的吞吐量(每秒的行数)可以跟不用复制的数据一样高。

启用复制表

修改config.xml配置

配置ZooKeeper集群的地址

<zookeeper>
    <node index="1">
        <host>example1</host>
        <port>2181</port>
    </node>
    <node index="2">
        <host>example2</host>
        <port>2181</port>
    </node>
    <node index="3">
        <host>example3</host>
        <port>2181</port>
    </node>
</zookeeper>

给分片配置多个副本

<remote_servers>
    <logs>
        <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>true</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>true</internal_replication>
            <replica>
                <host>example01-02-1</host>
                <port>9000</port>
            </replica>
            <replica>
                <host>example01-02-2</host>
                <secure>1</secure>
                <port>9440</port>
            </replica>
        </shard>
    </logs>
</remote_servers>

注意internal_replication应配置为true

为每个分片配置macros

<macros>
    <shard>02</shard>
    <replica>example05-02-1.cxy7.com</replica>
</macros>

image.png

创建复制表

CREATE TABLE log_test
(
    ts  DateTime,
    uid String,
    biz String
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/log_test',
           '{replica}') PARTITION BY toYYYYMMDD(ts) ORDER BY (ts) SETTINGS index_granularity = 8192

创建分布式表

CREATE TABLE log_test_cluster
(
    ts  DateTime,
    uid String,
    biz String
) ENGINE = Distributed(bip_ck_cluster, default, log_test, rand())

添加一点数据

INSERT INTO log_test VALUES ('2019-06-07 20:01:01', 'a', 'show');
INSERT INTO log_test VALUES ('2019-06-07 20:01:02', 'b', 'show');
INSERT INTO log_test VALUES ('2019-06-07 20:01:03', 'a', 'click');
INSERT INTO log_test VALUES ('2019-06-08 20:01:04', 'c', 'show');
INSERT INTO log_test VALUES ('2019-06-08 20:01:05', 'c', 'click');