开发者社区 > 博文 > ClickHouse初体验
分享
  • 打开微信扫码分享

  • 点击前往QQ分享

  • 点击前往微博分享

  • 点击复制链接

ClickHouse初体验

  • 京东云技术交付部
  • 2021-01-21
  • IP归属:未知
  • 97720浏览

前言


ClickHouse是俄罗斯著名搜索引擎公司Yandex开发的一款开源OLAP数据库,由于性能极其优异,即使数十亿上百亿的数据查询都可以秒出结果,因此得到了越来越多全球大厂的青睐。京东智联云基于原生ClickHouse的PaaS产品JCHDB近期发布,对于很多爱好数据库的小伙伴,这是一个很不错的学习平台,可以省去搭建环境的资源消耗和繁琐的配置。本文将从使用者的角度带着大家利用JCHDB来揭开ClickHouse的神秘面纱。


一、创建ClickHouse实例


1、在京东智联云创建ClickHouse非常简单,用户在控制台“云服务”->“数据库与缓存”下选择“分析型云数据库JCHDB”进入实例列表界面,点击“创建”按钮,进入下面界面:

这里我们选最新版本20.0.2.3,下面两个参数比较重要,一个是副本数,一个是分片数,这里我们分别选单副本,2分片,ClickHouse中还有一个分区的概念,这三个名词初学者很容易混淆,我们后面单独讲。
其他参数按照默认值即可,单击创建界面右边“立即购买”按钮即可开始创建实例。

2、几分钟后,控制台实例列表中可以看到创建成功的实例:

3、点击进入实例,可以看到产品提供的功能列表,目前支持查看实例信息,监控(空间/连接数/QPS等),账号管理,节点信息(zk各节点、shard各节点的cpu/内存/空间信息)


二、连接到ClickHouse


1、找一台内网云主机作为客户端,使用root按照下面步骤安装ClickHouse客户端:

yum install yum-utils

rpm --import https://repo.clickhouse.tech/CLICKHOUSE-KEY.GPG

yum-config-manager --add-repo https://repo.clickhouse.tech/rpm/stable/x86_64

yum install clickhouse-server clickhouse-client

(1)控制台创建数据库测试账号



(2)云主机上使用clickhouse-client命令连接到数据库,hostname和端口参考实例信息:



出现笑脸符号:)即成功登陆clickhouse,熟悉MySQL的同学可以尝试执行show databases/show tables/show grants/show privileges命令看看,都可以正常执行,clickhouseMySQL的很多命令都兼容。


三、高性能初体验


1. tcpH工具下载数git clone https://github.com/amosbird/ssb-dbgen.gitcd ssb-dbgenmake


2. 生成测试数据,其中c/l/p/s分别代表customer/lineorder/part/supplier四张测试相关的用户表/订单表/配件表/供应商表

$ ./dbgen -s 100 -T c $ ./dbgen -s 100 -T l $ ./dbgen -s 100 -T p $ ./dbgen -s 100 -T s


3.创建customer/lineorder/part/supplier四张表

CREATE TABLE customer
(
C_CUSTKEY UInt32,
C_NAME String,
C_ADDRESS String,
C_CITY LowCardinality(String),
C_NATION LowCardinality(String),
C_REGION LowCardinality(String),
C_PHONE String,
C_MKTSEGMENT LowCardinality(String)
)
ENGINE = MergeTree ORDER BY (C_CUSTKEY);

CREATE TABLE lineorder
(
LO_ORDERKEY UInt32,
LO_LINENUMBER UInt8,
LO_CUSTKEY UInt32,
LO_PARTKEY UInt32,
LO_SUPPKEY UInt32,
LO_ORDERDATE Date,
LO_ORDERPRIORITY LowCardinality(String),
LO_SHIPPRIORITY UInt8,
LO_QUANTITY UInt8,
LO_EXTENDEDPRICE UInt32,
LO_ORDTOTALPRICE UInt32,
LO_DISCOUNT UInt8,
LO_REVENUE UInt32,
LO_SUPPLYCOST UInt32,
LO_TAX UInt8,
LO_COMMITDATE Date,
LO_SHIPMODE LowCardinality(String)
)
ENGINE = MergeTree PARTITION BY toYear(LO_ORDERDATE) ORDER BY (LO_ORDERDATE, LO_ORDERKEY);

CREATE TABLE part
(
P_PARTKEY UInt32,
P_NAME String,
P_MFGR LowCardinality(String),
P_CATEGORY LowCardinality(String),
P_BRAND LowCardinality(String),
P_COLOR LowCardinality(String),
P_TYPE LowCardinality(String),
P_SIZE UInt8,
P_CONTAINER LowCardinality(String)
)
ENGINE = MergeTree ORDER BY P_PARTKEY;

CREATE TABLE supplier
(
S_SUPPKEY UInt32,
S_NAME String,
S_ADDRESS String,
S_CITY LowCardinality(String),
S_NATION LowCardinality(String),
S_REGION LowCardinality(String),
S_PHONE String
)
ENGINE = MergeTree ORDER BY S_SUPPKEY;


4.加载数据

$ clickhouse-client -h hostname -u --passowrd ***  --query "INSERT INTO customer FORMAT CSV" < customer.tbl

$ clickhouse-client -h hostname -u --passowrd *** --query "INSERT INTO part FORMAT CSV" < part.tbl

$ clickhouse-client -h hostname -u --passowrd *** --query "INSERT INTO supplier FORMAT CSV" < supplier.tbl

$ clickhouse-client -h hostname -u --passowrd *** --query "INSERT INTO lineorder FORMAT CSV" < lineorder.tbl


5.将这些表关联,聚合成一个大表

SET max_memory_usage = 20000000000;

SET max_memory_usage = 20000000000, allow_experimental_multiple_joins_emulation = 1;

CREATE TABLE lineorder_flat
ENGINE = MergeTree
PARTITION BY toYear(LO_ORDERDATE)
ORDER BY (LO_ORDERDATE, LO_ORDERKEY) AS
SELECT l.*, c.*, s.*, p.*
FROM lineorder l
ANY INNER JOIN customer c ON (c.C_CUSTKEY = l.LO_CUSTKEY)
ANY INNER JOIN supplier s ON (s.S_SUPPKEY = l.LO_SUPPKEY)
ANY INNER JOIN part p ON (p.P_PARTKEY = l.LO_PARTKEY);

ALTER TABLE lineorder_flat DROP COLUMN C_CUSTKEY, DROP COLUMN S_SUPPKEY, DROP COLUMN P_PARTKEY;


6.终于到激动人心的时候,先看看聚合后的表有多少数据:

SELECT count(1)FROM lineorder_flat

┌───count(1)─┐

│ 1200075804 │

└────────────┘

1 rows in set. Elapsed: 0.012 sec.

12亿多,秒出结果,


7.再来个复杂的:

SELECT    sum(LO_REVENUE),    toYear(LO_ORDERDATE) AS year,    P_BRAND

FROM lineorder_flat

WHERE (P_BRAND >= 'MFGR#2221') AND (P_BRAND <= 'MFGR#2228') AND (S_REGION = 'ASIA')

GROUP BY    year,    P_BRAND

ORDER BY    year ASC,    P_BRAND ASC

┌─sum(LO_REVENUE)─┬─year─┬─P_BRAND───┐

│    132900698876 │ 1992 │ MFGR#2221 │

│    130846528624 │ 1992 │ MFGR#2222 │

│    133873545374 │ 1992 │ MFGR#2223 │

│    128094383868 │ 1992 │ MFGR#2224 │

│    131489118276 │ 1992 │ MFGR#2225 │ 

。。。。。。

└─────────────────┴──────┴───────────┘ 

56 rows in set. Elapsed: 3.918 sec. Processed 1.20 billion rows, 11.17 GB (306.31 million rows/s., 2.85 GB/s.) 

最后一行的执行结果显示,SQL执行了3.918秒,处理了10亿多条数据,平均每秒3亿多行/2.85G的处理速度,这种性能大大超出了我们对数据库的传统认知。

下图是ClickHouse官方提供的和主要竞品VerticaGreenplum性能对比,从图中可以看出,即使和olap竞品相比,性能也要高1-2个数量级,和传统oltp数据库就更不用说了。   



四、如何创建一个表


上面例子中,在创建测试表时,发现建表sql最后有这么一个字句“ENGINE = MergeTree ORDER BY S_SUPPKEY”。和MySQL类似,ENGINE字句后面需要指定表的存储引擎,ClickHouse目前版本支持6大类20多种存储引擎,其中MergeTree是最重要的一种,类似于MySQL中的InnoDB其他很多存储引擎都是继承于它。MergeTree从字面理解,是合并树的意思,当MergeTree在写入一批数据时,数据会以数据片段的形式写入磁盘,为了避免片段过多,后台会有一个线程,定期合并这些数据片段,属于一个分区(partition)的片段会合并成一个新的片段,MergeTree的名称由此而来。

一个完整的MergeTree建表语法如下:

CREATE TABLE [IF NOT EXISTS] [db.]table_name  [ON CLUSTER cluster]

(    

name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],    

name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],    

...    

INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,   

 INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2) 

ENGINE = MergeTree()

ORDER BY expr

[PARTITION BY expr]

[PRIMARY KEY expr]

[SAMPLE BY expr]

[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]

[SETTINGS name=value, ...]

 

相关参数简单介绍如下:   

·       ORDER BY是必填项,表明了在一个数据片段中的排序键。

·       PARTITION BY是选填项,指定了表的分区键,和MySQL分区类似,如果不指定,默认只有一个分区all,分区信息可以从系统表system.parts查询。

·       PRIMARY KEY是选填项,设置后会自动创建索引,可以不写,默认和排序键相同,但和其他数据库不同,PRIMARY KEY并不能保证数据的唯一性,如果需要去重,需要ReplaceMergeTree(后面讲)。

·       SAMPLE BY是选填项,用来做抽样表达式,指定数据以何种标准进行采样,对于需要抽样的场景很适用。

·       TTL是选填项,用来对表或列做TTL生命周期管理,比如下面的表中记录如果当前时间超过create_time10s即被删除:

create table ttl_table2(id String,create_time DateTime,code String )  order by id TTL create_time + interval 10 second;

·       SETTING是选填项,后面参数较多,常见的是index_granularity,默认8192,含义是索引生成粒度,即每生成8192条记录才生成一条索引,这样生成的索引也称之为稀疏索引。这和大多OLTP数据库都不太一样。

看一个简单例子:

CREATE TABLE t2(

 `id` UInt16,   

 `create_time` Date,   

 `comment` Nullable(String)

)

ENGINE = MergeTree()

PARTITION BY toYYYYMM(create_time)

PRIMARY KEY (id, create_time)

ORDER BY (id, create_time)

SETTINGS index_granularity = 8192

Ok. 

0 rows in set. Elapsed: 0.010 sec.

insert into t2 values(0, '2020-03-20', null);

insert into t2 values(0, '2020-03-20', null);

insert into t2 values(1, '2020-03-21', null);

insert into t2 values(1, '2020-03-21', null);

insert into t2 values(2, '2020-03-22', null);

insert into t2 values(2, '2020-03-23', null);

insert into t2 values(2, '2020-03-24', null);


SELECT *FROM t2

┌─id─┬─create_time─┬─comment─┐

│  0 │  2020-03-20 │ ᴺᵁᴸᴸ    │

│  0 │  2020-03-20 │ ᴺᵁᴸᴸ    │

│  1 │  2020-03-21 │ ᴺᵁᴸᴸ    │

│  1 │  2020-03-21 │ ᴺᵁᴸᴸ    │

│  2 │  2020-03-22 │ ᴺᵁᴸᴸ    │

└────┴─────────────┴─────────┘

┌─id─┬─create_time─┬─comment─┐

│  2 │  2020-03-23 │ ᴺᵁᴸᴸ    │

└────┴─────────────┴─────────┘

┌─id─┬─create_time─┬─comment─┐

│  2 │  2020-03-24 │ ᴺᵁᴸᴸ    │

└────┴─────────────┴─────────┘

7 rows in set. Elapsed: 0.003 sec.

从查询结果看,主键为idcreate_time,但显然id01的都有两条主键相同的记录,完全颠覆我们认知。此时如果将表中记录设置一个TTL时间,create_time过期10s的记录都删除,我们看看什么效果:

alter table t2 modify ttl create_time+interval 10 second;

Ok.0 rows in set. Elapsed: 0.012 sec.

10s以后。。。

select * from t2;Ok.0 rows in set. Elapsed: 0.003 sec.

显然,数据符合我们的预期,10s后所有数据都过期,结果为空。


五、MergeTree家族


上面例子中,我们发现MergeTree和其他数据库一个重要的区别是主键可以重复,这在一些场景下可能无法接受。ClickHouse提供了ReplacingMergeTree来解决此类问题。除此之外,ClickHouse还提供了一些其他MergeTree的衍生存储引擎,用来解决一些特殊场景下的存储和性能需求。最常见的包括如下几类:

  • ReplacingMergeTree
  • SummingMergeTree
  • AggregatingMergeTree
  • CollapsingMergeTree
  • VersionedCollapsingMergeTree

ReplacingMergeTree,指的是如果有记录主键重复,新记录将会替换掉旧记录,类似于MySQLreplace into语句。看下面例子:

CREATE TABLE t_replace

(    

`id` UInt16,    

`create_time` Date,    

`comment` Nullable(String)

)

ENGINE = ReplacingMergeTree()

PARTITION BY toYYYYMM(create_time)

PRIMARY KEY (id, create_time)

ORDER BY (id, create_time)

SETTINGS index_granularity = 8192 

Ok.

0 rows in set. Elapsed: 0.008 sec. 

insert into t_replace values(0, '2020-03-20', 1);

insert into t_replace values(0, '2020-03-20', 2);

insert into t_replace values(1, '2020-03-21', 1);

insert into t_replace values(1, '2020-03-21', 2);

insert into t_replace values(2, '2020-03-22', null);

insert into t_replace values(2, '2020-03-23', null);

insert into t_replace values(2, '2020-03-24', null);


SELECT *FROM t_replace 

┌─id─┬─create_time─┬─comment─┐

│  0 │  2020-03-20 │ 2       │

│  1 │  2020-03-21 │ 2       │

│  2 │  2020-03-22 │ ᴺᵁᴸᴸ    │

│  2 │  2020-03-23 │ ᴺᵁᴸᴸ    │

│  2 │  2020-03-24 │ ᴺᵁᴸᴸ    │

└────┴─────────────┴─────────┘

5 rows in set. Elapsed: 0.003 sec.

显然,重复的记录只保留了最新两条,符合预期,需要注意的是,有时候会看到表里的数据并未去重,这是因为MergeTree的合并程序需要一段时间才会进行,此时需要手工执行以下优化命令,强制触发文件的合并:

optimize table t_replace;

optimize table t_replace final;

由于后台合并进程时间无法估算,且optimize命令在大数据量时较消耗资源,建议查询唯一值的时候还是通过distinctgroup by命令去重比较保险。


SummingMergeTree,顾名思义,这个存储引擎用来做sum汇总。经常有这样的场景,需要记录各种明细,但不需要查询明细,而只需要关注明细在某个维度上的汇总。在MergeTree的基础上做groupsum虽然也可以达到类似需求,但在存储和查询开销上都有额外的消耗,SummingMergeTree就是为此而创建的。

举个简单例子:

--创建SummingMergeTree测试表:

CREATE TABLE t_sum

(    

`key` UInt32,    

`value` UInt32

)

ENGINE = SummingMergeTree()

ORDER BY key

Ok.

0 rows in set. Elapsed: 0.010 sec. 

--插入3条记录

insert into t_sum values(1,1),(1,2),(2,3);

Ok.

3 rows in set. Elapsed: 0.008 sec. 

--后台合并前

select * from t_sum;

SELECT *FROM t_sum

┌─key─┬─value─┐

│   1 │     1 │

│   1 │     2 │

│   2 │     3 │

└─────┴───────┘

3 rows in set. Elapsed: 0.003 sec. 

--手工合并后

optimize table t_sum final;

OPTIMIZE TABLE t_sum FINAL

Ok.

0 rows in set. Elapsed: 0.003 sec. 

select * from t_sum;

SELECT *FROM t_sum

┌─key─┬─value─┐

│   1 │     3 │

│   2 │     3 │

└─────┴───────┘

2 rows in set. Elapsed: 0.003 sec.

--为了保证统计正确,数据仍然需要做聚合

SELECT key, sum(value) FROM t_sum GROUP BY key; 

SELECT    key,    sum(value)FROM t_sumGROUP BY key

┌─key─┬─sum(value)─┐

│   2 │          3 │

│   1 │          3 │

└─────┴────────────┘

2 rows in set. Elapsed: 0.004 sec.

需要注意的是,进行聚合的条件是order by后面字段,聚合所有非主键的数值类型字段,如果指定了分区,在聚合仅仅限于分区内部。

AggregatingMergeTree,SummingMergeTree非常类似,也是进行预聚合后统计。区别在于使用的范围更广,而不仅仅使用sum函数。这种存储引擎更多的在物化视图上使用,如下例:

--创建基础表

CREATE TABLE t_agg_basic

(    

`id` String,    

`name` String,    

`value` UInt32

)

ENGINE = MergeTree()

ORDER BY id

Ok.

0 rows in set. Elapsed: 0.008 sec.

--创建物化视图

CREATE MATERIALIZED VIEW t_agg_view

ENGINE = AggregatingMergeTree()

PARTITION BY name

ORDER BY (id, name) 

AS

SELECT    id,    name,    sum(value) AS sum_value,    avg(value) AS avg_value

FROM t_agg_basic

GROUP BY    id,    name

Ok. 

0 rows in set. Elapsed: 0.011 sec.

--基础表插入明细数据

insert into table t_agg_basic values('001','bj',10),('001','bj',20),('002','sh',30),('002','sh',40);

INSERT INTO t_agg_basic VALUES

Ok.

4 rows in set. Elapsed: 0.005 sec.

--物化视图自动聚合SELECT *FROM t_agg_view

┌─id──┬─name─┬─sum_value─┬─avg_value─┐

│ 002 │ sh   │        70 │        35 │

└─────┴──────┴───────────┴───────────┘

┌─id──┬─name─┬─sum_value─┬─avg_value─┐

│ 001 │ bj   │        30 │        15 │

└─────┴──────┴───────────┴───────────┘

2 rows in set. Elapsed: 0.004 sec.

其他几种存储引擎,CollapsingMergeTree/VersionedCollapsingMergeTree来消除ReplacingMergeTree的限制,使用场景相对较少,由于篇幅所限,这里不再举例。


六、分区/分片/副本的区别


分区:将表按照分区键在逻辑上分成多个数据段,后台文件合并时,同一个分区的数据段将合并成新的数据段。和MySQL等关系数据库的分区概念类似,通过分区可以在查询和运维方面提供很多方便。前例创建表时指定partition关键字即可创建分区表。 

分片:将表中的数据按照一定的规则拆分为多个部分,每个部分的数据均存储在不同的计算节点上,每个计算节点上的数据称为一个分片。

副本:对于Replicated MergeTree 系列复制表,可以设置每个表有多份完全一样的数据存放在不同的计算节点上,每一份数据都是完整的,并且称为一个副本。


分片和副本信息可以通过查询system.clusters获取详细信息。下面SQL显示了我们测试集群的详细信息:

select * from system.clusters;

SELECT *FROM system.clusters

┌─cluster─┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─────────────┬─host_address─┬─port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─estimated_recovery_time─┐

│ default │         1 │            1 │           1 │ chi-ck-ew1skhn4ep-0-0 │ 11.32.0.154  │ 9000 │        0 │ default │                  │            0 │                       0 │

│ default │         2 │            1 │           1 │ chi-ck-ew1skhn4ep-1-0 │ 127.0.0.1    │ 9000 │        1 │ default │                  │            0 │                       0 │

└─────────┴───────────┴──────────────┴─────────────┴───────────────────────┴──────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────────────┘

2 rows in set. Elapsed: 0.005 sec.

可以看出:我们集群的名称为default;有两个分片,分片号分别为12;单副本,这里单副本的概念其实是没有副本,两个分片都是单个实例。


七、本地表和分布式表


在测试过程中,经常发现每次连接到不同的shard节点,创建的表在下一次连接后经常就看不见了,这是因为我们创建的表是本地表(local表),只在本地保存。但对应用来说更希望创建表时,每个节点都能同时创建完成,对外可以看到的是一个统一的表,此时,就需要用到ClickHouse的分布式引擎,也称为Distributed

分布式引擎,本身不存储数据,但可以在多个服务器上进行分布式查询,类似于分库分表中间件。读是自动并行的。读取时,远程服务器表的索引(如果存在)会被使用。下例中我们首先利用ClickHouseReplicatedMergeTree在各个节点创建表结构,然后创建分布式引擎,来获取一个完整的数据结果。

--连接到任何一个shard节点执行下面建表命令:

CREATE TABLE log_test2 ON CLUSTER default

(    `ts` DateTime,    `uid` UInt32,    `biz` String)

ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/log_test2', '{replica}')

PARTITION BY toYYYYMMDD(ts)

ORDER BY ts

SETTINGS index_granularity = 8192

┌─host──────────────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐

│ chi-ck-ew1skhn4ep-1-0 │ 9000 │      0 │       │                   1 │                0 │

│ chi-ck-ew1skhn4ep-0-0 │ 9000 │      0 │       │                   0 │                0 │

└───────────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘

2 rows in set. Elapsed: 0.119 sec.

--分别连接到不同的shard节点(通过hostname()看是否为同一个节点),分别插入下面两条数据

--shard1SELECT hostname()

┌─hostname()──────────────────────┐

│ chi-ck-ew1skhn4ep-default-1-0-0 │

└─────────────────────────────────┘

1 rows in set. Elapsed: 0.003 sec.

--插入数据

insert into log_test2 values(now(),1,1);

INSERT INTO log_test2 VALUES 

Ok.

1 rows in set. Elapsed: 0.011 sec.

--查询数据

SELECT *FROM log_test2

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:09:12 │   1 │ 1   

│└─────────────────────┴─────┴─────┘

1 rows in set. Elapsed: 0.003 sec.

--shard2SELECT hostname()

┌─hostname()──────────────────────┐

│ chi-ck-ew1skhn4ep-default-0-0-0 │

└─────────────────────────────────┘

1 rows in set. Elapsed: 0.003 sec.

--插入数据

insert into log_test2 values(now(),2,2);

INSERT INTO log_test2 VALUES

Ok.

1 rows in set. Elapsed: 0.012 sec.

--查询数据

SELECT *FROM log_test2

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:10:57 │   2 │ 2   │

└─────────────────────┴─────┴─────┘

1 rows in set. Elapsed: 0.004 sec.

--创建分布式引擎:
CREATE TABLE log_test_all ON CLUSTER default

(    `ts` DateTime,    `uid` UInt32,    `biz` String)

ENGINE = Distributed(default, default, log_test2, uid)

┌─host──────────────────┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐

│ chi-ck-ew1skhn4ep-0-0 │ 9000 │      0 │       │                   1 │                0 │

│ chi-ck-ew1skhn4ep-1-0 │ 9000 │      0 │       │                   0 │                0 │

└───────────────────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘

2 rows in set. Elapsed: 0.114 sec.

--查看分布式表:

select * from log_test_all;

SELECT *FROM log_test_all

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:09:12 │   1 │ 1   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:10:57 │   2 │ 2   │

└─────────────────────┴─────┴─────┘

2 rows in set. Elapsed: 0.007 sec.


--往分布式表中插入数据,此时新数据会按照指定的key进行hash计算后路由到对应节点:

select * from log_test_all;

SELECT *FROM log_test_all

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:09:12 │   1 │ 1   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:19:33 │   5 │ 5   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:19:09 │   3 │ 3   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:19:45 │   6 │ 6   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:19:24 │   4 │ 4   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:10:57 │   2 │ 2   │

└─────────────────────┴─────┴─────┘

6 rows in set. Elapsed: 0.009 sec.

--shard1查询

select * from log_test2;

SELECT *FROM log_test2

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:19:33 │   5 │ 5   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:19:09 │   3 │ 3   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:09:12 │   1 │ 1   │

└─────────────────────┴─────┴─────┘

3 rows in set. Elapsed: 0.004 sec.

--shard2查询:

select * from log_test2;

SELECT *FROM log_test2

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:19:45 │   6 │ 6   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:19:24 │   4 │ 4   │

└─────────────────────┴─────┴─────┘

┌──────────────────ts─┬─uid─┬─biz─┐

│ 2020-11-25 20:10:57 │   2 │ 2   │

└─────────────────────┴─────┴─────┘

3 rows in set. Elapsed: 0.004 sec.

可以看出,数据通过分布式表准确的进行了数据的路由分发和查询汇总。


八、一点感悟


ClickHouse是近些年OLAP领域突显的一批黑马,它最大的特点就是快,快的原因很多,比如大量使用并行/列存/向量化/压缩/稀疏索引等各种常见技术外,更多是一些设计思想上的创新,比如一个字符串搜素算法,会按照字符串的使用场景来使用不同的算法;对于普通的去重计数函数,会根据字符串的大小来选择不同的算法;类似的优化在ClickHouse中使用非常普遍。

本文只是介绍了一些ClickHouse的基本概念和一些初级用法,真正要用到生产环境,还要很多细节需要查阅文档并充分测试后进行。


在此感谢各位童鞋阅读,如果能够对大家有所帮助,欢迎点赞转发。

同时欢迎扫码关注京东云技术中台团队的公众号:云服务飞行团;更多精彩内容会持续放送!

共0条评论