Clickhouse --- 副本与分片

Clickhouse — 副本与分片

1. 概述

集群作为副本与分片的基础将Clickhouse的服务从单个节点扩展到多个节点。

Clickhouse的配置相当灵活,可以把所有节点放在一个单一的集群,也可以划分为多个集群。

下面的图片和公式描述了分片和副本的关系:
S o u r c e 1. d a t a = = S o u r c e 2. d a t a S o u r c e 3. d a t a ≠ S o u r c e 4. d a t a Source1.data == Source2.data \\ Source3.data \neq Source4.data Source1.data==Source2.dataSource3.data=Source4.data
在这里插入图片描述

下面具体讲一下数据副本。

2. 数据副本

说到数据副本,那么就要说到Replicated-MergeTree,只有这种表引擎才有副本的概念与使用的能力。Replicated-MergeTree作为MergeTree的派生引擎,在其基础上添加了分布式协同的功能。如下图:
在这里插入图片描述

上述过程可以分为三步:

  1. 数据首先被写入内存缓冲区。

  2. 数据接着被写入tmp临时分区,待全部完成后写入正式分区。

  3. 添加了ZooKeeper部分,建立一系列监听节点。

    在整个通讯过程中,zookeeper不涉及表数据的传输。

2.1 特点

  • 依赖Zookeeper:在INSERT以及ALTER查询时,ReplicatedMergedTree会借助ZooKeeper实现分布式协同的能力。
  • 多主架构:可以在任意的副本进行INSERT以及ALTER,效果一致。
  • BLOCK数据块:在INSERT时,会根据max_insert_block_size的大小将数据进行切分。BLOCK的读写操作满足原子性唯一性
    • 原子性:跟mysql一样的理解,要么全部成功,要么全部失败。
    • 唯一性:BLOCK会根据数据顺序数据行数据大小等计算HASH值。写入时发现存在相同HASH的BLOCK时,会自动忽略当前写入BLOCK块。这样预防了重复写入的问题。
  • 表级别副本:副本是在表级别定义的,可以进行个性化设置。

2.2 定义形式

首先我们看一下zookeeper的配置:

下面是在/etc/clickhouse-server/config.d/zookeeper.xml的配置

<yandex>
  <zookeeper-servers>
  	<node>
    	<host>zookeeper-clickhouse</host>
      <port>2181</port>
    </node>
  </zookeeper-servers>
</yandex>

接着就是在全局配置/etc/clickhouse-server/config.xml中配置

<!--
	if 如果不使用副本,这样设置
-->
<zookeeper incl="zookeeper-servers" optinal="true"/> 

<!--
	else
-->
<include_from>/etc/clickhouse-server/config.d/zookeeper.xml</include_from>
<zookeeper incl="zookeeper-servers" optinal="false"/> 

可以通过sql命令来查看zookeeper里面的信息。

SELECT * FROM system.zookeeper WHERE path='/'

下面进入正题,介绍一下副本的定义形式:

ENGINE = ReplicatedMergeTree('zookeeper_path','replica_name')

Zookeeper_path:用户指定在zookeeper中创建的数据表的路径(可自定义)。例如

/clickhouse/tables/{shard}/table_name	
# 其中/clickhouse/tables为固定前缀,表示根目录
# {shard}表示分片编号。一张表可以有多个分片
# table_name数据表的名称

replica_name:副本位置,同一分片的不同副本需要不同的名字

比如:

# 1分片 1副本
ENGINE = ReplicatedMergeTree('/clickhouse/tables/01/test1','alvin1.icloud.com')
ENGINE = ReplicatedMergeTree('/clickhouse/tables/01/test1','alvin2.icloud.com')

#2分片 1副本
ENGINE = ReplicatedMergeTree('/clickhouse/tables/01/test1','alvin1.icloud.com')
ENGINE = ReplicatedMergeTree('/clickhouse/tables/01/test1','alvin2.icloud.com')

ENGINE = ReplicatedMergeTree('/clickhouse/tables/02/test1','alvin3.icloud.com')
ENGINE = ReplicatedMergeTree('/clickhouse/tables/02/test1','alvin4.icloud.com')

3. RelicatedMergeTree原理解析

RelicatedMergeTree中大量使用了zookeeper来实现副本之间的协同,包括主副本选举、副本状态感知、操作日志、任务队列等。下面讲一下zookeeper内部的结构。

3.1 Zookeeper内部结构

监听节点可以大致分成如下几类:

  1. 元数据:
    • /metadata:保存元数据信息,包括主键、分区键等
    • /columns:列名称和数据类型
    • /replicas:设置参数中的replica_name
  2. 判断标识:
    • /leader_election:用于主副本选举
    • /blocks:记录Block数据块的Hash信息,比如partition_id
    • /block_numbers:分区的写入顺序。各个副本在本地MERGE时,都会按照相同的顺序进行。
    • /quornum:记录quornum的数量。
  3. 操作日志:
    • /log:保存副本需要执行的任务指令。
    • /mutation:当执行ALTER DELETEALTER UPDATE查询时,操作指令会被添加到这个节点。
    • /replicas/{replica_name}/*:每个副本各自节点下的一组监听节点,指导副本在本地的执行。这里面有一些重要的节点:
      • /queue:任务队列节点
      • /log_pointer:log日志指针节点。

3.2 副本协同的核心流程

这里分成四个部分进行讲解:分别为INSERT、MERGE、MUTATION、ALTER这四个部分。

每一个部分先放上流程图,然后加上文字的描述。

3.2.1 INSERT

流程:

  1. 创建第一个副本实例

    首先从Alvin1开始,对该节点执行CREATE

    CREATE TABLE replicated_sales_1(
    	id String,
    	price Float64,
    	date DateTime
    ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/01/replicated_sales_1','alvin1.icloud.com')
    PARTITION BY toYYYYMM(create_time)
    ORDER BY id
    

    根据zookeeper_path初始化所有的节点;

    /replica下注册自己的副本实例;启动监听任务,监听/log

    参与副本选举,方式就是插入/leader_election节点,第一个插入成功的副本作为主副本。

  2. 创建第二个副本实例,在alvin2执行相同的语句

    CREATE TABLE replicated_sales_1(
    	id String,
    	price Float64,
    	date DateTime
    ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/01/replicated_sales_1','alvin2.icloud.com')
    PARTITION BY toYYYYMM(create_time)
    ORDER BY id
    
  3. 向第一个副本实例写入数据

    INSERT INTO TABLE replicated_sales_1 VALUES(1,99.9,"2021-07-07 00:00:00")
    

    首先会向本地分区写入数据,接着向/block节点写入分区的block_id

  4. 由第一个副本推送日志logEntry

    可以看出操作为GET,需要下载的分区为202107_0_0_0

  5. 第二个副本拉取日志

    注意:拉取以后**不会直接执行!**而是放到/replicas/alvin2.icloud.com/queue中。

  6. 第二个副本实例向其他副本发送下载请求

    alvin2基于/queue开始执行任务,当看到get请求时,会选择一个远端作为数据下载的来源。

    副本选择算法:

    • /replicas节点拿到所有的副本节点
    • 遍历副本选择/queue自节点最少的且log_pointer下标最大的节点进行拉取。
  7. 副本实例响应下载

  8. 第二个实例下载数据并完成本地写入

    Tmp_fetch_202107_0_0_0->202107_0_0_0

在这里插入图片描述

3.2.2 MERGE

由于细节在图中展现出来,流程仅简要概述

流程:

  1. 创建远程连接,尝试连接主副本

  2. 主副本接受通讯

  3. 由主副本指定MERGE策略并推送Log

    从日志里面可以看出,操作类型为MERGE类型,需要合并的分区为202107_0_0_0202107_1_1_0

    同时,主副本会锁住执行线程,对日志的接收情况进行监听,其由replication_alter_partitions_sync控制:

    • 0: 不做任何等待
    • 1:只等待主副本完成
    • 2:会等待所有的副本拉取完成
  4. 各个副本拉取Log

  5. 各个副本在本地执行MERGE
    在这里插入图片描述

3.2.3 MUTATION

流程:

  1. 推送MUTATION日志
  2. 所有副本监听mutation日志
  3. 主副本实例响应mutation日志推送Log日志
  4. 各个副本拉取log日志
  5. 各个副本本地执行mutation

在这里插入图片描述

3.2.4 ALTER

过程:

  1. 修改共享元数据

    ALTER TABLE replicated_sales_1 ADD COLUMN comment String
    

    执行后会修改Zookeeper内的共享节点:/metadata,/columns

  2. 监听共享元数据变更并各自执行本地修改

  3. 确认所有的副本更改完毕

在这里插入图片描述

4. 数据分片

为了解决单机情况下,存储量的限制问题。clickhouse引入了分片的概念,对于clickhouse来说,每一个服务节点都可以称作分片(shard)。

为了解决分片的负载问题,click house引入了Distributed表引擎来进行代理,解决路由问题。

为什么叫做代理?

因为Distributed表本身不存放数据,仅作为分布式表的一层透明代理,在集群内自动开展数据的写入、分发、查询、路由工作。

从集群配置来进行讲解,首先给出Distributed表引擎与分片的关系。

在这里插入图片描述

4.1 自定义分片与副本

  1. 不包含副本的分片

    <!-- 2分片 0副本 -->
    <sharding_simple> <!-- 自定义集群名字 -->
      <shard> <!-- 分片 -->
        <replica> <!-- 副本 -->
          <host>alvin1.icloud.com</host>
          <port>9000</port>
        </replica>
      </shard>
      <shard> <!-- 分片 -->
        <replica> <!-- 副本 -->
          <host>alvin2.icloud.com</host>
          <port>9000</port>
        </replica>
      </shard>
    </sharding_simple>
    

    其中,两分片0副本!

  2. N个分片N个副本

    • 一分片一副本

      <!-- 1分片 1副本 -->
      <sharding_simple> <!-- 自定义集群名字 -->
        <shard> <!-- 分片 -->
          <replica> <!-- 副本 -->
            <host>alvin1.icloud.com</host>
            <port>9000</port>
          </replica>
          <replica> <!-- 副本 -->
            <host>alvin2.icloud.com</host>
            <port>9000</port>
          </replica>
        </shard>
      </sharding_simple>
      
    • 两分片,每个分片一个副本

      <!-- 1分片 1副本 -->
      <sharding_simple> <!-- 自定义集群名字 -->
        <shard> <!-- 分片 -->
          <replica> <!-- 副本 -->
            <host>alvin1.icloud.com</host>
            <port>9000</port>
          </replica>
          <replica> <!-- 副本 -->
            <host>alvin2.icloud.com</host>
            <port>9000</port>
          </replica>
        </shard>
        <!-- 分割 -->
        <shard> <!-- 分片 -->
          <replica> <!-- 副本 -->
            <host>alvin3.icloud.com</host>
            <port>9000</port>
          </replica>
          <replica> <!-- 副本 -->
            <host>alvin4.icloud.com</host>
            <port>9000</port>
          </replica>
        </shard>
      </sharding_simple>
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值