百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 博客教程 > 正文

Clickhouse数据复制的原理与实践

connygpt 2024-12-15 11:34 6 浏览

作者:俊达

说明

在clickhouse中,如果我们想实现数据多副本存储,以提高数据可用率和查询性能,可以使用复制表。支持数据复制的表引擎包括:

  • ReplicatedMergeTree
  • ReplicatedSummingMergeTree
  • ReplicatedReplacingMergeTree
  • ReplicatedAggregatingMergeTree
  • ReplicatedCollapsingMergeTree
  • ReplicatedVersionedCollapsingMergeTree
  • ReplicatedGraphiteMergeTree

使用复制表的前置条件是clickhouse配置了zookeeper。需要在配置文件中配置,如:

<zookeeper>
    <node>
        <host>example1</host>
        <port>2181</port>
    </node>
    <node>
        <host>example2</host>
        <port>2181</port>
    </node>
    <node>
        <host>example3</host>
        <port>2181</port>
    </node>
</zookeeper>

在clickhouse中,以表为单位进行复制。不同的表可以配置不同的复制策略。

需要注意的是,clickhouse不会复制CREATE, DROP, ATTACH, DETACH和RENAME这些操作。

而通过alter table给表增加字段的操作会进行复制。

创建复制表

建表语法:

CREATE TABLE table_name ( ... ) 
ENGINE = ReplicatedMergeTree('path_in_zookeeper', 'replica_name') 
...

创建复制表需要指定两个关键参数:

  • path_in_zookeeper: zookeeper中的路径,同一个表的多个副本,该参数必须一样。
  • replica_name: 多个副本需要配置不同的replica_name。

一般在建表时,我们会使用{shard}, {replica}等宏变量:

create table rep_table(id int, val String)
engine ReplicatedMergeTree(
    '/clickhouse/tables/{shard_id}/rep/rep_table', 
    '{replica}'
) order by id;

上面例子中,{shard_id}, {replica}都是在macros中定义的宏,我们以在系统表system.macros中查看当前实例的宏定义。

## 节点ck01
ck01 :) select * from system.macros;

SELECT *
FROM system.macros

Query id: a85a2f99-e2dd-4ba4-9b5e-519e7b5c9f40

┌─macro────┬─substitution───┐
│ cluster  │ cluster-zero   │
│ replica  │ 172.16.121.248 │
│ shard_id │ 01             │
└──────────┴────────────────┘

4 rows in set. Elapsed: 0.001 sec.


## 节点ck02
ck02 :) select * from system.macros;

SELECT *
FROM system.macros

Query id: a11a1a07-0757-414a-954a-dd716d0cda3d

┌─macro────┬─substitution──┐
│ cluster  │ cluster-zero  │
│ replica  │ 172.16.121.48 │
│ shard_id │ 01            │
└──────────┴───────────────┘

4 rows in set. Elapsed: 0.002 sec.

有几点需要注意:

1、如果replica, shard_id等宏定义在建表之后发生了变化,则可能会导致相关的表出现异常。

## ck01中将shard_id定义修改为02, 重启clickhouse后,再写入数据

ck01 :) insert into rep_table values(2, 'two', 'xx');

INSERT INTO rep_table FORMAT Values

Query id: 882aa305-a7a7-48d4-b27e-d9564c046693


0 rows in set. Elapsed: 0.004 sec.

Received exception from server (version 22.6.3):
Code: 242. DB::Exception: Received from localhost:9000. 
DB::Exception: Table is in readonly mode (replica path: /clickhouse/tables/02/rep/rep_table/replicas/172.16.121.248). (TABLE_IS_READ_ONLY)

由于在zookeeper中并不存在/clickhouse/tables/02/rep/rep_table这个路径,数据无法写入。在clickhouse的启动日志中,可以看到相关的信息:

2022.12.20 03:42:42.505570 [ 177009 ] {} <Warning> rep.rep_table (01b4ad06-4d45-4451-808b-8403e8b1b6c8):
 No metadata in ZooKeeper for /clickhouse/tables/02/rep/rep_table: table will be in readonly mode.

2、复制表的多个副本表结构必须一致
如果建表时表结构不一致,则无法创建表:

ck01 :) create table rep_table(id int, val String, id2 int)
        engine ReplicatedMergeTree('/clickhouse/tables/{shard_id}/rep/rep_table', '{replica}')
        order by id;

CREATE TABLE rep_table
(
    `id` int,
    `val` String,
    `id2` int
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard_id}/rep/rep_table', '{replica}')
ORDER BY id

Query id: 8aa10add-590a-4129-9420-a625edd9d5e1


0 rows in set. Elapsed: 0.240 sec.

Received exception from server (version 22.6.3):
Code: 122. DB::Exception: Received from localhost:9000. DB::Exception: Table columns structure in ZooKeeper is different from local table structure. Local columns:
columns format version: 1
3 columns:
`id` Int32
`val` String
`id2` Int32

Zookeeper columns:
columns format version: 1
2 columns:
`id` Int32
`val` String
. (INCOMPATIBLE_COLUMNS)

3、多个副本的replica需要唯一
如果建表时,在zookeeper中已经存在对应replica的路径,则无法创建成功:

ck02 :) create table rep_table2(id int, val String)
                engine ReplicatedMergeTree('/clickhouse/tables/{shard_id}/rep/rep_table', '{replica}')
                order by id
                ;

CREATE TABLE rep_table2
(
    `id` int,
    `val` String
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard_id}/rep/rep_table', '{replica}')
ORDER BY id

Query id: 6231b6f8-3421-4c5f-90b4-d13e1cdb6463


0 rows in set. Elapsed: 0.750 sec.

Received exception from server (version 22.6.3):
Code: 253. DB::Exception: Received from localhost:9000. DB::Exception: 
Replica /clickhouse/tables/01/rep/rep_table/replicas/172.16.121.48 already exists. (REPLICA_IS_ALREADY_EXIST)

复制表在zookeeper中存了哪些信息

zookeeper在clickhouse的数据复制中起着关键作用。

如上图所示,对复制表的操作,会在zookeeper中记录日志信息,副本通过记录在zookeeper中的信息,实现数据复制。

如下操作都会在zookeeper中记录日志:

  • insert
  • merge(optimize table)
  • alter table attach/detach partition/part
  • alter table update/delete
  • alter table add column

复制表zookeeper节点内容

通过zookeeper客户端,或者使用系统表system.zookeeper,都可以查看复制表zookeeper对应节点中存储了哪些信息

ck01 :) select name  from system.zookeeper where path='/clickhouse/tables/01/rep/rep_table';

SELECT name
FROM system.zookeeper
WHERE path = '/clickhouse/tables/01/rep/rep_table'

Query id: 59cb84f5-87ad-4ec4-8fd6-974dbce358a1

┌─name───────────────────────┐
│ alter_partition_version    │
│ metadata                   │
│ temp                       │
│ table_shared_id            │
│ log                        │
│ leader_election            │
│ columns                    │
│ blocks                     │
│ nonincrement_block_numbers │
│ replicas                   │
│ quorum                     │
│ pinned_part_uuids          │
│ block_numbers              │
│ mutations                  │
│ zero_copy_s3               │
│ zero_copy_hdfs             │
│ part_moves_shard           │

metadata:表结构信息

log: 数据复制关键信息。log节点下,每一条日志都对应着对表的一个动作。

replicas:每一个副本会在replicas下有一个节点。

mutations:对表的mutation操作(如alter table update/delete)

log节点信息

ck02 :) select name, value  from system.zookeeper where path='/clickhouse/tables/01/rep/rep_table/log' order by name\G

SELECT
    name,
    value
FROM system.zookeeper
WHERE path = '/clickhouse/tables/01/rep/rep_table/log'
ORDER BY name ASC

Query id: 82f00616-9c18-4bc6-a107-189ac4a67aa1


-- 对应一个mutation操作的日志
Row 2:
──────
name:  log-0000000008
value: format version: 4
create_time: 2022-12-20 06:06:11
source replica: 172.16.121.248
block_id:
mutate
all_1_1_0_2
to
all_1_1_0_3


-- merge 操作的日志
Row 3:
──────
name:  log-0000000009
value: format version: 4
create_time: 2022-12-20 06:06:20
source replica: 172.16.121.248
block_id:
merge
all_0_0_0_3
all_1_1_0_3
into
all_0_1_1_3
deduplicate: 0
part_type: Compact

-- alter table的日志
Row 8:
──────
name:  log-0000000014
value: format version: 4
create_time: 2022-12-20 06:08:14
source replica: 172.16.121.248
block_id:
alter
alter_version
6
have_mutation
1
columns_str_size:
61
columns format version: 1
2 columns:
`id` Int32
`val` String

metadata_str_size:
192
metadata format version: 1
date column:
sampling expression:
index granularity: 8192
mode: 0
sign column:
primary key: id
data format version: 1
partition key:
granularity bytes: 10485760

-- alter table attach partition的日志
Row 10:
───────
name:  log-0000000016
value: format version: 4
create_time: 2022-12-20 06:11:59
source replica: 172.16.121.248
block_id:
REPLACE_RANGE
drop_range_name: all_0_0_0
from_database: rep
from_table: tmp_rep
source_parts: ['all_1_1_0']
new_parts: ['all_8_8_0']
part_checksums: ['5381E04F17BD6299E7C1F56B445FB8DB']
columns_version: -1

-- insert操作对应的日志
Row 11:
───────
name:  log-0000000017
value: format version: 4
create_time: 2022-12-20 06:17:38
source replica: 172.16.121.248
block_id: all_1659522035524593032_2034088950575960742
get
all_9_9_0
part_type: Compact

mutation节点信息

ck02 :) select name, value  from system.zookeeper where path='/clickhouse/tables/01/rep/rep_table/mutations'\G

SELECT
    name,
    value
FROM system.zookeeper
WHERE path = '/clickhouse/tables/01/rep/rep_table/mutations'

Query id: 2cea07ee-0b0b-4a6d-85f0-f73fcb129f06

Row 1:
──────
name:  0000000001
value: format version: 1
create time: 2022-12-20 06:06:11
source replica: 172.16.121.248
block numbers count: 1
all    3
commands: DELETE WHERE id = 1
alter version: -1

Row 2:
──────
name:  0000000000
value: format version: 1
create time: 2022-12-20 06:05:40
source replica: 172.16.121.248
block numbers count: 1
all    2
commands: UPDATE val = \'updated\' WHERE 1
alter version: -1

Row 3:
──────
name:  0000000003
value: format version: 1
create time: 2022-12-20 06:08:01
source replica: 172.16.121.248
block numbers count: 1
all    5
commands: DROP COLUMN padding2
alter version: 5

replicas节点

每一个副本都会在replicas路径下建立一个节点。

ck02 :) select name  from system.zookeeper where path='/clickhouse/tables/01/rep/rep_table/replicas/172.16.121.48' ;

SELECT name
FROM system.zookeeper
WHERE path = '/clickhouse/tables/01/rep/rep_table/replicas/172.16.121.48'

Query id: 9f4dd647-39fa-42ed-a8a2-beb642673018

┌─name────────────────────────┐
│ is_lost                     │
│ metadata                    │
│ is_active                   │
│ mutation_pointer            │
│ columns                     │
│ max_processed_insert_time   │
│ flags                       │
│ log_pointer                 │
│ min_unprocessed_insert_time │
│ host                        │
│ parts                       │
│ queue                       │
│ metadata_version            │
└─────────────────────────────┘

replicas下的关键信息:

  • log_pointer: 当前副本处理的日志位点
  • queue:当前节点待处理任务队列
  • metadata_version: 元数据版本

添加新副本

给已有的表添加新副本时,新副本会选择一个原有的节点做全量数据同步。

从clickhouse的debug日志中可以看到复制的大概流程:

executeQuery: (from [::ffff:127.0.0.1]:42530) CREATE TABLE rep.rep_table ( `id` Int32, `val` String ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard_id}/rep/rep_table', '{replica}') ORDER BY id SETTINGS index_granularity = 8192; (stage: Complete)
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Loading data parts
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): There are no data parts
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): This table /clickhouse/tables/01/rep/rep_table is already created, will add new replica
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Creating replica /clickhouse/tables/01/rep/rep_table/replicas/ck03
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Became leader
rep.rep_table (ReplicatedMergeTreeRestartingThread): Activating replica.
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Replica 172.16.121.48 has log pointer '18', approximate 0 queue lag and 0 queue size
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Replica 172.16.121.248 has log pointer '18', approximate 0 queue lag and 0 queue size
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Will mimic 172.16.121.48
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Queued 3 parts to be fetched, 0 parts ignored

rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Fetching part all_0_1_1_6 from /clickhouse/tables/01/rep/rep_table/replicas/172.16.121.248
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Fetching part all_9_9_0 from /clickhouse/tables/01/rep/rep_table/replicas/172.16.121.248
rep.rep_table (b2fe260c-ca4f-4e6d-903a-6a4f0358777e): Fetching part all_8_8_0 from /clickhouse/tables/01/rep/rep_table/replicas/172.16.121.248

在zookeeper中添加新的replica节点

根据其他节点的log pointer和queue信息,选择复制的源端节点

将源端节点的parts信息加入到本节点的queue中。

将part下载到本节点并attach到表中。

更多技术信息请查看云掣官网云掣YunChe - 可观测运维专家 | 大数据运维托管 | 云MSP服务

相关推荐

自学Python,写一个挨打的游戏代码来初识While循环

自学Python的第11天。旋转~跳跃~,我~闭着眼!学完循环,沐浴着while的光芒,闲来无事和同事一起扯皮,我说:“编程语言好神奇,一个小小的循环,竟然在生活中也可以找到原理和例子”,同事也...

常用的 Python 工具与资源,你知道几个?

最近几年你会发现,越来越多的人开始学习Python,工欲善其事必先利其器,今天纬软小编就跟大家分享一些常用的Python工具与资源,记得收藏哦!不然下次就找不到我了。1、PycharmPychar...

一张思维导图概括Python的基本语法, 一周的学习成果都在里面了

一周总结不知不觉已经自学Python一周的时间了,这一周,从认识Python到安装Python,再到基本语法和基本数据类型,对于小白的我来说无比艰辛的,充满坎坷。最主要的是每天学习时间有限。只...

三日速成python?打工人,小心钱包,别当韭菜

随着人工智能的热度越来越高,许多非计算机专业的同学们也都纷纷投入到学习编程的道路上来。而Python,作为一种相对比较容易上手的语言,也越来越受欢迎。网络上各类网课层出不穷,各式广告令人眼花缭乱。某些...

Python自动化软件测试怎么学?路线和方法都在这里了

Python自动化测试是指使用Python编程语言和相关工具,对软件系统进行自动化测试的过程。学习Python自动化测试需要掌握以下技术:Python编程语言:学习Python自动化测试需要先掌握Py...

Python从放弃到入门:公众号历史文章爬取为例谈快速学习技能

这篇文章不谈江流所专研的营销与运营,而聊一聊技能学习之路,聊一聊Python这门最简单的编程语言该如何学习,我完成的第一个Python项目,将任意公众号的所有历史文章导出成PDF电子书。或许我这个Py...

【黑客必会】python学习计划

阅读Python文档从Python官方网站上下载并阅读Python最新版本的文档(中文版),这是学习Python的最好方式。对于每个新概念和想法,请尝试运行一些代码片段,并检查生成的输出。这将帮助您更...

公布了!2025CDA考试安排

CDA数据分析师报考流程数据分析师是指在不同行业中专门从事行业数据搜集、整理、分析依据数据作出行业研究评估的专业人员CDA证书分为1-3级,中英文双证就业面广,含金量高!!?报考条件:满18...

一文搞懂全排列、组合、子集问题(经典回溯递归)

原创公众号:【bigsai】头条号:程序员bigsai前言Hello,大家好,我是bigsai,longtimenosee!在刷题和面试过程中,我们经常遇到一些排列组合类的问题,而全排列、组合...

「西法带你学算法」一次搞定前缀和

我花了几天时间,从力扣中精选了五道相同思想的题目,来帮助大家解套,如果觉得文章对你有用,记得点赞分享,让我看到你的认可,有动力继续做下去。467.环绕字符串中唯一的子字符串[1](中等)795.区...

平均数的5种方法,你用过几种方法?

平均数,看似很简单的东西,其实里面包含着很多学问。今天,分享5种经常会用到的平均数方法。1.算术平均法用到最多的莫过于算术平均法,考试平均分、平均工资等等,都是用到这个。=AVERAGE(B2:B11...

【干货收藏】如何最简单、通俗地理解决策树分类算法?

决策树(Decisiontree)是基于已知各种情况(特征取值)的基础上,通过构建树型决策结构来进行分析的一种方式,是常用的有监督的分类算法。决策树算法是机器学习中的一种经典算法,它通过一系列的规则...

面试必备:回溯算法详解

我们刷leetcode的时候,经常会遇到回溯算法类型题目。回溯算法是五大基本算法之一,一般大厂也喜欢问。今天跟大家一起来学习回溯算法的套路,文章如果有不正确的地方,欢迎大家指出哈,感谢感谢~什么是回溯...

「机器学习」决策树——ID3、C4.5、CART(非常详细)

决策树是一个非常常见并且优秀的机器学习算法,它易于理解、可解释性强,其可作为分类算法,也可用于回归模型。本文将分三篇介绍决策树,第一篇介绍基本树(包括ID3、C4.5、CART),第二篇介绍Ran...

大话AI算法: 决策树

所谓的决策树算法,通俗的说就是建立一个树形的结构,通过这个结构去一层一层的筛选判断问题是否好坏的算法。比如判断一个西瓜是否好瓜,有20条西瓜的样本提供给你,让你根据这20条(通过机器学习)建立起...