Clickhouse原理与应用实践(五)副本与分片

1、概述

  • 下图中N1和N2内容不同,则N1 N2互为分片。如果内容相同,则互为副本。
分片与副本

2、数据副本

  • 只有使用了ReplicatedMergeTree复制表系列引擎,才能应用副本能力。
  • ReplicatedMergeTree增加了Zookeeper部分,会进一步在ZooKeeper内创建一系列的监听节点,并以此实现多个实例之间的通信。
  • ZooKeeper不会涉及表数据传输。
ReplicatedMergeTree

2.1 副本的特定

  • 副本是定义在表级别的
  • 多主架构
  • Block数据块是数据写入的基本定远,并且具有写入的原子性和唯一性。会计算Hash信息并记录,通过Hash摘要对比是否唯一。

2.2 副本定义形式

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

  • 通常的zk_path命名 /clickhouse/tables/{shard}/table_name
  • zk_path用于指定在ZK中创建的数据表的路径
  • 对于zk_path,同一张数据表的同一个分片的不同副本应该定义相同的路径。
  • 对于replica_name,同一张数据表的同一个分片的不同副本应该定义不同名称。
命名的例子

3、ReplicatedMergeTree

3.1 数据结构

  • 大量运用ZooKeeper能力,实现副本之间协同。

ZooKeeper内的节点结构

  • 元数据
    /metadata
    /columns
    /replicas
  • 判断标识
    /leader_election
    /blocks hash摘要
    /block_numbers
    /quorum 至少有quorum数量副本写入成功后才算写入成功
  • 操作日志
    /log
    /mutations ALTER DELTE ALTER UPDATE等操作的记录
    /replicas/{replica_name}/*

Entry日志多项数据结构

  • /log /mutations 是分发操作指令的信息通道,发送指令的方式是为这些父节点添加子节点。
  • 添加的子节点在Clickhouse中被统一抽象为Entry对象,具体实体由LogEntry和MutationEntry对象承载。

3.2 副本协同的核心流程

INSERT

INSERT流程
  • 由执行了INSERT操作的副本向/log节点推送操作日志。
  • 副本会一直监听/log节点变化,拉取LogEntry,将其转为任务对象放至队列。
  • 基于/queue队列开始执行任务,会选择一个远端副本作为数据的下载来源。选取拥有最大log_pointer的,并且/queue子节点数量最少的。然后建立起连接开始下载。

MERGE

  • 无论MERGE操作从哪个副本发起,其合并计划都会交由主副本来制定。
MERGE过程

MUTATION

  • 也是由主节点来制定计划

ALTER

  • 修改ZK内的共享元数据节点。
    /metadata /columns
ALTER

4、数据分片

  • ClickHouse中的每个服务节点都称为一个shard
  • ClickHouse数据分片需要结合Distributed表引擎一同使用,使得查询、写入能够进行路由。
  • Distributed表引擎本身不存储任何数据,知识作为分布式表的一层透明代理。

4.1 基于集群实现分布式DDL

CREATE/DROP/RENAME/ALTER TABLE ON CLUSTER cluster_name

数据结构

  • 默认分布式DDL在ZK内使用的根路径为/clickhouse/task_queue/ddl
  • /query-[seq]/active /query-[seq]/finished
  • DDLLogEntry日志对象数据中包含了 query、hosts、initiator

分布式DDL执行流程

  • 谁执行谁负责推送
  • 拉取日志并执行
  • 步骤1执行后,客户端会阻塞180秒,等待所有host执行完毕。
分布式DDL执行

5、Distributed原理解析

  • 由两部分组成,本地表和分布式表,分布式表以all后缀命名。
  • 采用读时检查,如果它们表结构不兼容,只有在查询时才抛出错误。

5.1 定义形式

ENGINE = Distributed(cluster, database, table [,shaeding_key])

CREATE TABLE test_shard_2_all ON CLUSTER sharding_simple ()
ENGINE = Distributed(sharding_simple, defalult, test_shard_2_local, rand())
  • cluster 集群名称
  • sharding_key 分片键,选填参数
读取模式
  • Distributed表不支持任何MUTATION类型操作

5.2 分片规则

  • 集群配置的分片权重,权重越大,写入数据越多
  • slot 数量等于所有分片的权重之和
  • 选择函数,slot = shard_value % sum_weight

5.3 写入流程

  • 在第一个分片节点写入本地分片数据
  • 建立远端连接,准备发送远端数据分片
  • 发送数据
  • 远端分片写入本地
  • 第一个分片确认完成写入
写入流程

副本写入

  • 可以用Distributed复制,也可以依赖ReplicatedgeTree
副本写入

5.4 查询过程

  • 多副本路由:randon\nearest_hostname(错误最少)\in_order(错误最少中的按定义逐个选择)\first_or_random 四种方式

  • 分布式查询是在本地查之后union的结果

分布式查询执行计划

使用Global优化分布式子查询

  • 使用本地表的问题
SELECT uniq(id) FROM test_query_all WHERE repo = 100 AND id IN (SELECT id FROM test_query_local WHERE repo = 200)
  • 扫的本地表里刚好没有这个数据,有希望在全局里找。
问题1
  • 使用分布式表又会有查询放大的问题,每次扫all都是全局广播,就会变成指数增长
SELECT uniq(id) FROM test_query_all WEHRE repo = 100 AND id IN (SELECT id FROM test_query_all WHERE repo = 200)
问题2
  • 所以有一个GLOBAL关键字,可以将中间过程缓存
GLOBAL JOIN

你可能感兴趣的:(Clickhouse原理与应用实践(五)副本与分片)