ClickHouse 安装、配置、使用、原理
一、安装
1、yum 在线安装
sudo rpm --import https://repo.clickhouse.tech/CLICKHOUSE-KEY.GPG
sudo yum-config-manager --add-repo https://repo.clickhouse.tech/rpm/stable/x86_64
2、rpm 离线安装
二、配置
1、修改主配置文件
sudo vim /etc/clickhouse-server/config.xml --主配置文件
修改 <include_from> 自定义的配置文件
<yandex>
...
<!--<include_from>/etc/clickhouse-server/config.d/metrika.xml</include_from>-->
<include_from>/etc/clickhouse-server/config.d/metrika-shard.xml</include_from>
...
</yandex>
2、只配置Zookeeper,没有副本和分片的引擎
[root@hadoop102 ~]# vim /etc/clickhouse-server/config.d/metrika.xml
<?xml version="1.0"?>
<yandex>
<zookeeper-servers>
<node index="1">
<host>hadoop102</host>
<port>2181</port>
</node>
<node index="2">
<host>hadoop103</host>
<port>2181</port>
</node>
<node index="3">
<host>hadoop104</host>
<port>2181</port>
</node>
</zookeeper-servers>
</yandex>
3、ZK、副本、分片配置
[root@hadoop102 ~]# vim /etc/clickhouse-server/config.d/metrika-shard.xml
<?xml version="1.0"?>
<yandex>
<clickhouse_remote_servers>
<gmall_cluster> <!-- 集群名称-->
<shard> <!--集群的第一个分片-->
<internal_replication>true</internal_replication>
<replica> <!--该分片的第一个副本-->
<host>hadoop102</host>
<port>9000</port>
</replica>
<replica> <!--该分片的第二个副本-->
<host>hadoop103</host>
<port>9000</port>
</replica>
</shard>
<shard> <!--集群的第二个分片-->
<internal_replication>true</internal_replication>
<replica> <!--该分片的第一个副本-->
<host>hadoop104</host>
<port>9000</port>
</replica>
</shard>
</gmall_cluster>
</clickhouse_remote_servers>
<zookeeper-servers>
<node index="1">
<host>hadoop102</host>
<port>2181</port>
</node>
<node index="2">
<host>hadoop103</host>
<port>2181</port>
</node>
<node index="3">
<host>hadoop104</host>
<port>2181</port>
</node>
</zookeeper-servers>
<macros>
<shard>01</shard> <!--不同机器放的分片数不一样-->
<replica>rep_1_1</replica> <!--不同机器放的副本数不一样-->
</macros>
</yandex>
三、常用命令
1、集群命令
systemctl start clickhouse-server
systemctl status clickhouse-server
systemctl stop clickhouse-server
2、常用SQL
# 手动合并数据块
OPTIMIZE TABLE dd.ttl2 FINAL;
# 删除表
drop table dd.ttl2;
# 删除表
drop table dd.ttl2 on cluster;
# 查看表描述
desc dd.ttl2
# 查看建表语句
show create table dd.mm1;
四、分片副本表测试
1、每台机器执行建库、建表语句
ReplicatedMergeTree 创建的本地表元数据不会同步到副本节点和集群中的其他节点;
# 创建测试库
create database dd;
# 创建测试副本表
create table dd.mm (
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
)
engine =ReplicatedMergeTree('/clickhouse/tables/dd/{shard}/mm','{replica}')
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id,sku_id);
2、查询系统元数据
select total_replicas,table,engine,database,is_leader,zookeeper_path,replica_name,replica_path from system.replicas;
结果:
3、插入数据
在哪个节点上插入的数据只能在当前节点和对应的副本节点可以查到
插入每列都相同的数据,数据量和内容不变(原因TODO)
1、ReplicatedMergeTree 副本同步
1、zookeeper:在执行insert和Alter语句的时候,通过zookeeper协调同步,查询数据的时候不会用到zookeeper
2、表级别的副本:副本是表级别的,每张表的副本数量,在集群的位置可以自定义(未验证)
3、zookeeper 会为每张表创建一组监听节点,
1)/matedata : 保存元信息,主键、分区键等
2)/columns:保存列字段信息,名称和数据类型
3)/replicas:保存副本名称
4)/leader_election: 用于主副本的选举,MERGE和MUTATION操作(ALTER DELETE和ALTER UPDATE)由主副本完成,完成后再借助zookeeper将消息事件发送到其他副本
5)/log:常规操作日志节点(INSERT、MERGE和DROP
PARTITION),它是整个工作机制中最为重要的一环,保存了副本需要
执行的任务指令,log使用了ZooKeeper的持久顺序型节点,每条指令
的名称以log-为前缀递增,例如log-0000000000、log-0000000001
等。每一个副本实例都会监听/log节点,当有新的指令加入时,它们
会把指令加入副本各自的任务队列,并执行任务。
6)/mutations:MUTATION操作日志节点,作用与log日志类似,当
执行ALERT DELETE和ALERT UPDATE查询时,操作指令会被添加到这个
节点。mutations同样使用了ZooKeeper的持久顺序型节点,但是它的
命名没有前缀,每条指令直接以递增数字的形式保存,例如
0000000000、0000000001等。
7)/queue:任务队列节点,用于执行具体的操作任务。当副本
从/log或/mutations节点监听到操作指令时,会将执行任务添加至该
节点下,并基于队列执行。
8)·/log_pointer:log日志指针节点,记录了最后一次执行的log
日志下标信息,例如log_pointer:4对应了/log/log-0000000003(从
0开始计数)。
9)/mutation_pointer:mutations日志指针节点,记录了最后一
次执行的mutations日志名称,例如mutation_pointer:0000000000对
应了/mutations/000000000。
2、副本协同核心流程
1、副本协同的核心流程主要有INSERT、MERGE、MUTATION和ALTER四种,分别对应了数据写入、分区合并、数据修改和元数据修改。
2、INSERT和ALTER查询是分布式执行的。借助ZooKeeper的事件
通知机制,多个副本之间会自动进行有效协同,但是它们不会使用ZooKeeper存储任何分区数据。
而其他查询并不支持分布式执行,包括SELECT、CREATE、DROP、RENAME和ATTACH。例如,为了创
建多个副本,我们需要分别登录每个ClickHouse节点,在它们本地执行各自的CREATE语句(后面将会介绍如何利用集群配置简化这一操作)。接下来,会依次介绍上述流程的工作机理。为了便于理解,我先来整体认识一下各个流程的介绍方法。
3、INSERT的核心执行流程
1)创建第一个副本实例
create 语句会触发ReplicatedMergeTree初始化操作:
- 根据zk_path初始化所有的ZooKeeper节点。 在/replicas/节点下注册自己的副本实例ch5.nauu.com。
- 启动监听任务,监听/log日志节点。
- 参与副本选举,选举出主副本,选举的方式是向/leader_election/插入子节点,第一个插入成功的副本就是主副本。
2)创建第二个副本实例
create 语句会触发ReplicatedMergeTree初始化操作:
- 在/replicas/节点下注册自己的副本实例ch6.nauu.com。
- 启动监听任务,监听/log日志节点。
- 参与副本选举,选举出主副本。在这个例子中,CH5副本成为主副本。
3)向第一个副本实例写入数据
INSERT INTO TABLE replicated_sales_1 VALUES(‘A001’,100,‘2019-05-10 00:00:00’)
- 首先会在本地完成分区目录的写入
- 接着向/blocks节点写入该数据分区的block_id
- 该block_id将作为后续去重操作的判断依据。如果此时再次执行刚才的INSERT语句,试图写入重复数据,则会出现如下提示:
Block with ID 201905_2955817577822961065_12656761735954722499 already exists; ignoring it. - 只有当写入副本个数大于或等于insert_quorum(默认0)时,整个写入操
作才算成功。
4)由第一个副本实例推送Log日志
由执行了INSERT的副本向/log节点推送操作日志
5)第二个副本实例拉取Log日志
- CH6副本会一直监听/log节点变化,当CH5推送了/log/log-0000000000之后,CH6便会触发日志的拉取任务并更新log_pointer,将其指向最新日志下标。
- 在拉取了LogEntry之后,它并不会直接执行,而是将其转为任务对象放至队列
6)第二个副本实例向其他副本发起下载请求
- CH6基于/queue队列开始执行任务。
- 当看到type类型为get的时候,ReplicatedMergeTree会明白此时在远端的其他副本中已经成功写入了数据分区,而自己需要同步这些数据。
- CH6上的第二个副本实例会开始选择一个远端的其他副本作为数据的下载来源。
7)第一个副本实例响应数据下载
8)第二个副本实例下载数据并完成本地写入
总结:可以看到,在INSERT的写入过程中,ZooKeeper不会进行任何实质性的数据传输。本着谁执行谁负责的原则,在这个案例中由CH5首先在本地写入了分区数据。之后,也由这个副本负责发送Log日志,通知其他副本下载数据。如果设置了insert_quorum并且insert_quorum>=2,则还会由该副本监控完成写入的副本数量。其他副本在接收到Log日志之后,会选择一个最合适的远端副本,点对点地下载分区数据。
4、MERGE的核心执行流程
当ReplicatedMergeTree触发分区合并动作时,即会进入这个部分的流程;
无论合并是哪个副本发起的,合并计划都是主副本制定的。
1)创建远程连接,尝试与主副本通信
副本通过zk上的 /replicas 找到主副本,尝试建立连接
2)主副本接收并建立通信
3)由主副本制定MERGE计划并推送Log日志
由主副本CH5制定MERGE计划,并判断哪些分区需要被合并,将合并计划转换
为Log日志对象并推送Log日志,以通知所有副本开始合并。
4)各个副本分别拉取Log日志
会分别拉取日志到本地,并推送到各自的/queue任务队列
5)各个副本分别在本地执行MERGE
基于各自的/queue队列开始执行任务,各个副本开始在本地执行MERGE
insert into dd.mm values
(101,'sku_001',1000.00,'2020-06-02 12:00:01') ,
(102,'sku_002',2000.00,'2020-06-02 12:00:01') ,
(103,'sku_004',2500.00,'2020-06-02 12:00:01') ,
(104,'sku_002',2000.00,'2020-06-01 12:00:00') ,
(105,'sku_003',600.00 ,'2020-06-02 12:00:00');
4、观察磁盘
[root@hadoop102 ~]# ll /var/lib/clickhouse/data/dd/mm
总用量 24
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:10 20200601_0_8_2
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:15 20200602_0_0_0
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 19:16 20200602_0_1_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:16 20200602_1_1_0
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 17:54 detached
-rw-r----- 1 clickhouse clickhouse 1 6月 12 17:54 format_version.txt
5、MergeTree在磁盘上的物理存储结构
1、分区目录下会有多个文件
2、checksums.txt
是其他各个文件校验文件
3、columns.txt
是列信息文件,保存列名称和字段类型
4、count.txt
是计数文件,记录当前分区目录下的行数
5、primary.idx
: 一级索引文件,默认8192索引粒度的稀疏索引,PRIMARY KEY 或者 ORDER BY的字段
6、[Column].bin
:压缩数据文件,默认LZ4,按照ORDER BY
的字段排序
7、[Column].mrk
:标记文件,保存了.bin
中数据的偏移信息,建立了 primary.idx
和 .bin
文件的映射关系,.mrk
和 primary.idx
一一对应, 每列数据有一个 .mrk
文件
8、skp_idx_[Column].idx
与skp_idx_[Column].mrk
:二级索引文件和标记文件
9、ttl.txt
:记录了 ttl 相关的信息,MergeTree 合并时才会触发TTL
9、密集索引和稀疏索引对比
[root@hadoop102 ~]# ll /var/lib/clickhouse/data/dd/mm/20200602_0_1_1/
总用量 56
-rw-r----- 1 clickhouse clickhouse 437 6月 12 19:16 checksums.txt
-rw-r----- 1 clickhouse clickhouse 118 6月 12 19:16 columns.txt
-rw-r----- 1 clickhouse clickhouse 1 6月 12 19:16 count.txt
-rw-r----- 1 clickhouse clickhouse 42 6月 12 19:16 create_time.bin
-rw-r----- 1 clickhouse clickhouse 48 6月 12 19:16 create_time.mrk2
-rw-r----- 1 clickhouse clickhouse 45 6月 12 19:16 id.bin
-rw-r----- 1 clickhouse clickhouse 48 6月 12 19:16 id.mrk2
-rw-r----- 1 clickhouse clickhouse 8 6月 12 19:16 minmax_create_time.idx
-rw-r----- 1 clickhouse clickhouse 4 6月 12 19:16 partition.dat
-rw-r----- 1 clickhouse clickhouse 8 6月 12 19:16 primary.idx
-rw-r----- 1 clickhouse clickhouse 53 6月 12 19:16 sku_id.bin
-rw-r----- 1 clickhouse clickhouse 48 6月 12 19:16 sku_id.mrk2
-rw-r----- 1 clickhouse clickhouse 58 6月 12 19:16 total_amount.bin
-rw-r----- 1 clickhouse clickhouse 48 6月 12 19:16 total_amount.mrk2
6.MergeTree分区目录的合并过程
1、一次insert语句都会生成一批分区目录
2、10 - 15 分钟后台任务会合并相同分区目录的数据,生成新的目录,后台任务默认8分钟删除一次旧目录
3、新目录合并后,MinBlockNum
是所有和合并目录的最小值,MaxBlockNum
是所以合并目录的最小值,Level
(合并层级)最开始是0, ,合并后是所有合并目录的最大值 + 1
7、创建分片副本表的另一种方式 on cluster
1、这种方式不需要每台节点都执行命令,在集群中一个节点执行就可以
2、但是刚建完show tables
看不到,但是select
可以查询到数据,system.replicas
表也有记录
3、其余的是上面的方式一样
create table dd.mm1 on cluster gmall_cluster (
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
) engine =ReplicatedMergeTree('/clickhouse/tables/dd/{shard}/mm1','{replica}')
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id,sku_id);
五、分布式表测试
1、创建表
create table dd.mm2 on cluster gmall_cluster
(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
)engine = Distributed(gmall_cluster, dd, mm1, hiveHash(sku_id))
注意:分布式表不支持 分区、主键、排序
2、系统表没有记录
# 这个语句查询不到分布式表
select total_replicas,table,engine,database,is_leader,zookeeper_path,replica_name,replica_path from system.replicas;
3、查询到的是所有分片的数据
select * from dd.mm2;
4、hadoop102 上插入插入 2020-07-04 数据
insert into dd.mm2 values
(301,'sku_001',1000.00,'2020-07-04 12:00:01'),
(302,'sku_002',2000.00,'2020-07-04 12:00:01'),
(303,'sku_004',2500.00,'2020-07-04 12:00:01'),
(304,'sku_002',2000.00,'2020-07-04 12:00:01'),
(305,'sku_003',600.00 ,'2020-07-04 12:00:01');
5、数据被路由到hadoop102、hadoop103、hadoop104
[root@hadoop104 mm1]# ll
总用量 24
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:33 20200702_0_0_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 19:43 20200702_1_1_0
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 19:47 20200703_0_0_0
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 19:50 20200704_0_0_0
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:20 detached
-rw-r----- 1 clickhouse clickhouse 1 6月 12 18:20 format_version.txt
[atguigu@hadoop103 ~]$ sudo ls -al /var/lib/clickhouse/data/dd/mm1
总用量 36
drwxr-x--- 8 clickhouse clickhouse 4096 6月 12 19:50 .
drwxr-x--- 5 clickhouse clickhouse 4096 6月 12 18:44 ..
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:33 20200601_0_1_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:33 20200602_0_3_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:33 20200701_0_0_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:33 20200702_0_1_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 19:50 20200704_0_0_0
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:20 detached
-rw-r----- 1 clickhouse clickhouse 1 6月 12 18:20 format_version.txt
[root@hadoop102 mm1]# ll
总用量 28
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:33 20200601_0_1_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:33 20200602_0_3_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:33 20200701_0_0_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:33 20200702_0_1_1
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 19:50 20200704_0_0_0
drwxr-x--- 2 clickhouse clickhouse 4096 6月 12 18:20 detached
-rw-r----- 1 clickhouse clickhouse 1 6月 12 18:20 format_version.txt
6、插入重复数据测试
每列都一样的数据会被去重
hadoop102 :) select count(1) from dd.mm2;
SELECT count(1)
FROM dd.mm2
┌─count(1)─┐
│ 44 │
└──────────┘
1 rows in set. Elapsed: 0.017 sec.
hadoop102 :) insert into dd.mm2 values \
:-] (301,'sku_001',1000.00,'2020-07-04 12:00:01'), \
:-] (302,'sku_002',2000.00,'2020-07-04 12:00:01'), \
:-] (303,'sku_004',2500.00,'2020-07-04 12:00:01'), \
:-] (304,'sku_002',2000.00,'2020-07-04 12:00:01'), \
:-] (305,'sku_003',600.00 ,'2020-07-04 12:00:01');
INSERT INTO dd.mm2 VALUES
Ok.
5 rows in set. Elapsed: 0.011 sec.
hadoop102 :) select count(1) from dd.mm2;
SELECT count(1)
FROM dd.mm2
┌─count(1)─┐
│ 44 │
└──────────┘
1 rows in set. Elapsed: 0.020 sec.
六、TTL机制
1、字段级
过期数据置为默认值
1、创建TTL表
create table dd.ttl (
id UInt32,
sku_id String,
total_amount Decimal(16,2) TTL create_time + interval 10 SECOND,
create_time Datetime
) engine =ReplicatedMergeTree('/clickhouse/tables/dd/{shard}/ttl','{replica}')
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id,sku_id);
2、插入数据
insert into dd.ttl values (101,'sku_001',1000.00,now());
3、反复查询
hadoop102 :) select * from dd.ttl;
SELECT *
FROM dd.ttl
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │ 1000.00 │ 2021-06-13 00:10:03 │
└─────┴─────────┴──────────────┴─────────────────────┘
1 rows in set. Elapsed: 0.019 sec.
hadoop102 :) select * from dd.ttl;
SELECT *
FROM dd.ttl
┌──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 101 │ sku_001 │ 0.00 │ 2021-06-13 00:10:03 │
└─────┴─────────┴──────────────┴─────────────────────┘
1 rows in set. Elapsed: 0.006 sec.
2、行级TTL
过期数据删除行
1、创建普通本地表
create table dd.ttl2 (
id UInt32,
sku_id String,
total_amount Decimal(16,2) ,
create_time Datetime
) engine =ReplicatedMergeTree('/clickhouse/tables/dd/{shard}/ttl2','{replica}')
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id,sku_id);
2、修改为 TTL 行级表
alter table dd.ttl2 MODIFY TTL create_time + INTERVAL 10 SECOND ;
2、插入数据
insert into dd.ttl2 values (101,'sku_001',1000.00,now());
3、反复查询
TTL 时间已经过了,还没有删除,手动执行合并数据块OPTIMIZE TABLE dd.ttl2 FINAL
,数据被删除。
hadoop102 :) select now(), * from dd.ttl2;
SELECT
now(),
*
FROM dd.ttl2
┌───────────────now()─┬──id─┬─sku_id──┬─total_amount─┬─────────create_time─┐
│ 2021-06-13 00:14:05 │ 101 │ sku_001 │ 1000.00 │ 2021-06-13 00:13:31 │
└─────────────────────┴─────┴─────────┴──────────────┴─────────────────────┘
1 rows in set. Elapsed: 0.007 sec.
hadoop102 :) OPTIMIZE TABLE dd.ttl2 FINAL;
OPTIMIZE TABLE dd.ttl2 FINAL
Ok.
0 rows in set. Elapsed: 0.223 sec.
hadoop102 :) select now(), * from dd.ttl2;
SELECT
now(),
*
FROM dd.ttl2
Ok.
0 rows in set. Elapsed: 0.009 sec.
3、TTL 触发时机
1、只有在MergeTree合并分区时,才会触发删除TTL过期数据的逻辑
4、TTL 全局启停
TTL 不能删除
SYSTEM STOP/START TTL MERGES