8

一文带你走进 ClickHouse 的世界

 3 years ago
source link: https://mp.weixin.qq.com/s/xc8luGs6BEvQl-1ZN2rQuw
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

clickhouse 简介

1.1  什么是 clickhouse

ClickHouse 是俄罗斯的 Yandex 2 016 年开源的一个用于联机分析 (OLAP:Online Analytical Processing) 的列式数据库管理系统 (DBMS:Database Management System) ,简称 CH , 主要用于在线分析处理查询( OLAP ),能够使用 SQL 查询实时生成分析数据报告。

ClickHouse 是一个完全的列式数据库管理系统,允许在运行时创建表和数据库,加载数据和运行查询,而无需重新配置和重新启动服务器,支持线性扩展,简单方便,高可靠性,容错 。它在大数据领域没有走 Hadoop 生态,而是采用 Local attached storage 作为存储,这样整个 IO 可能就没有 Hadoop 那一套的局限。它的系统在生产环境中可以应用到比较大的规模,因为它的线性扩展能力和可靠性保障能够原生支持 shard + replication 这种解决方案。它还提供了一些 SQL 直接接口,有比较丰富的原生 client 。另外就是它比较快。

选择 ClickHouse   的首要原因是它比较快,但其实它的技术没有什么新的地方,为什么会快?

  • 它的数据剪枝能力比较强,分区剪枝在执行层,而存储格式用局部数据表示,就可以更细粒度地做一些数据的剪枝。它的引擎在实际使用中应用了一种现在比较流行的 LSM 方式。

  • 它对整个资源的垂直整合能力做得比较好,并发 MPP+ SMP 这种执行方式可以很充分地利用机器的集成资源。它的实现又做了很多性能相关的优化,它的一个简单的汇聚操作有很多不同的版本,会根据不同 Key 的组合方式有不同的实现。对于高级的计算指令,数据解压时,它也有少量使用。

  • ClickHouse   是一套完全由 C++ 模板 Code 写出来的实现,代码还是比较优雅的。

  • ClickHouse 是一个完全的列式数据库

1.2  特征

1.2.1  真正的列式数据库管理系统

在一个真正的列式数据库管理系统中,除了数据本身外不应该存在其他额外的数据。这意味着为了避免在值旁边存储它们的长度 «number» ,你必须支持固定长度数值类型。例如, 10 亿个 UInt8 类型的数据在未压缩的情况下大约消耗 1GB 左右的空间,如果不是这样的话,这将对 CPU 的使用产生强烈影响。即使是在未压缩的情况下,紧凑的存储数据也是非常重要的,因为解压缩的速度主要取决于未压缩数据的大小。

这是非常值得注意的,因为在一些其他系统中也可以将不同的列分别进行存储,但由于对其他场景进行的优化,使其无法有效的处理分析查询。例如: HBase BigTable Cassandra HyperTable 。在这些系统中,你可以得到每秒数十万的吞吐能力,但是无法得到每秒几亿行的吞吐能力。

需要说明的是, ClickHouse 不单单是一个数据库, 它是一个数据库管理系统。因为它允许在运行时创建表和数据库、加载数据和运行查询,而无需重新配置或重启服务。

1.2.2  数据压缩

在一些列式数据库管理系统中 ( 例如: InfiniDB CE MonetDB) 并没有使用数据压缩。但是 , 若想达到比较优异的性能,数据压缩确实起到了至关重要的作用。

1.2.3  数据的磁盘存储

许多的列式数据库 ( SAP HANA, Google PowerDrill) 只能在内存中工作,这种方式会造成比实际更多的设备预算。 ClickHouse 被设计用于工作在传统磁盘上的系统,它提供每 GB 更低的存储成本,但如果有可以使用 SSD 和内存,它也会合理的利用这些资源。

1.2.4  多核并行处理

ClickHouse 会使用服务器上一切可用的资源,从而以最自然的方式并行处理大型查询。

1.2.5  多服务器分布式处理

上面提到的列式数据库管理系统中,几乎没有一个支持分布式的查询处理。在 ClickHouse 中,数据可以保存在不同的 shard 上,每一个 shard 都由一组用于容错的 replica 组成,查询可以并行地在所有 shard 上进行处理。这些对用户来说是透明的

1.2.6  支持 sql

ClickHouse 支持基于 SQL 的声明式查询语言,该语言大部分情况下是与 SQL 标准兼容的。支持的查询包括 GROUP BY ORDER BY IN JOIN 以及非相关子查询。不支持窗口函数和相关子查询。

1.2.7  向量引擎

为了高效的使用 CPU ,数据不仅仅按列存储,同时还按向量 ( 列的一部分 ) 进行处理,这样可以更加高效地使用 CPU

1.2.8  实时的数据更新

ClickHouse 支持在表中定义主键。为了使查询能够快速在主键中进行范围查找,数据总是以增量的方式有序的存储在 MergeTree 中。因此,数据可以持续不断地高效的写入到表中,并且写入的过程中不会存在任何加锁的行为。

1.2.9  索引

按照主键对数据进行排序,这将帮助 ClickHouse 在几十毫秒以内完成对数据特定值或范围的查找。

1.2.10  适合在线查询

在线查询意味着在没有对数据做任何预处理的情况下以极低的延迟处理查询并将结果加载到用户的页面中。

1.2.11  支持近似计算

ClickHouse 提供各种各样在允许牺牲数据精度的情况下对查询进行加速的方法:

1.  用于近似计算的各类聚合函数,如: distinct values, medians, quantiles

2.  基于数据的部分样本进行近似查询。这时,仅会从磁盘检索少部分比例的数据。

3.  不使用全部的聚合条件,通过随机选择有限个数据聚合条件进行聚合。这在数据聚合条件满足某些分布条件下,在提供相当准确的聚合结果的同时降低了计算资源的使用。

1.2.12  支持数据复制和数据完整性

ClickHouse 使用异步的多主复制技术。当数据被写入任何一个可用副本后,系统会在后台将数据分发给其他副本,以保证系统在不同副本上保持相同的数据。在大多数情况下 ClickHouse 能在故障后自动恢复,在一些少数的复杂情况下需要手动恢复。

1.3  性能

1.3.1  优点

  • 为了高效的使用 CPU ,数据不仅仅按列存储,同时还按向量进行处理;

  • 数据压缩空间大,减少 IO ;处理单查询高吞吐量每台服务器每秒最多数十亿行;

  • 索引非 B 树结构,不需要满足最左原则;只要过滤条件在索引列中包含即可;即使在使用的数据不在索引中,由于各种并行处理机制 ClickHouse 全表扫描的速度也很快;

  • 写入速度非常快, 50-200M/s ,对于大量的数据更新非常适用。

1.3.2  缺点

  • 不持事务,不支持真正的删除 / 更新;

  • 不支持高并发,官方建议 qps 100 ,可以通过修改配置文件增加连接数,但是在服务器足够好的情况下;

  • 不支持真正的删除 / 更新支持 不支持事务(期待后续版本支持)

  • 不支持二级索引

  • 有限的 SQL 支持, join 实现与众不同

  • 不支持窗口功能

  • 元数据管理需要人工干预维护

  • SQL 满足日常使用 80% 以上的语法, join 写法比较特殊;最新版已支持类似 SQL join ,但性能不好;

  • 尽量做 1000 条以上批量的写入,避免逐行 insert 或小批量的 insert update delete 操作,因为 ClickHouse 底层会不断的做异步的数据合并,会影响查询性能,这个在做实时数据写入的时候要尽量避开;

  • ClickHouse 快是因为采用了并行处理机制,即使一个查询,也会用服务器一半的 CPU 去执行,所以 ClickHouse 不能支持高并发的使用场景,默认单查询使用 CPU 核数为服务器核数的一半,安装时会自动识别服务器核数,可以通过配置文件修改该参数。

1.3.3  优化

  • 关闭虚拟内存,物理内存和虚拟内存的数据交换,会导致查询变慢。

  • 为每一个账户添加 join_use_nulls 配置,左表中的一条记录在右表中不存在,右表的相应字段会返回该字段相应数据类型的默认值,而不是标准 SQL 中的 Null 值。

  • JOIN 操作时一定要把数据量小的表放在右边, ClickHouse 中无论是 Left Join Right Join 还是 Inner Join 永远都是拿着右表中的每一条记录到左表中查找该记录是否存在,所以右表必须是小表。

  • 批量写入数据时,必须控制每个批次的数据中涉及到的分区的数量,在写入之前最好对需要导入的数据进行排序。无序的数据或者涉及的分区太多,会导致 ClickHouse 无法及时对新导入的数据进行合并,从而影响查询性能。

  • 尽量减少 JOIN 时的左右表的数据量,必要时可以提前对某张表进行聚合操作,减少数据条数。有些时候,先 GROUP BY JOIN 比先 JOIN GROUP BY 查询时间更短。

  • ClickHouse 的分布式表性能性价比不如物理表高,建表分区字段值不宜过多,防止数据导入过程磁盘可能会被打满。

  • CPU 一般在 50% 左右会出现查询波动,达到 70% 会出现大范围的查询超时, CPU 是最关键的指标,要非常关注。

1.3.4  性能情况

  • 单个查询吞吐量:如果数据被放置在 page cache 中,则一个不太复杂的查询在单个服务器上大约能够以 2-10GB s (未压缩)的速度进行处理(对于简单的查询,速度可以达到 30GB s )。如果数据没有在 page cache 中的话,那么速度将取决于你的磁盘系统和数据的压缩率。例如,如果一个磁盘允许以 400MB s 的速度读取数据,并且数据压缩率是 3 ,则数据的处理速度为 1.2GB/s 。这意味着,如果你是在提取一个 10 字节的列,那么它的处理速度大约是 1-2 亿行每秒。对于分布式处理,处理速度几乎是线性扩展的,但这受限于聚合或排序的结果不是那么大的情况下。

  • 处理短查询的延时时间:数据被 page cache 缓存的情况下,它的延迟应该小于 50 毫秒 ( 最佳情况下应该小于 10 毫秒 ) 。否则,延迟取决于数据的查找次数。延迟可以通过以下公式计算得知:查找时间( 10 ms * 查询的列的数量 * 查询的数据块的数量。

  • 处理大量短查询: ClickHouse 可以在单个服务器上每秒处理数百个查询(在最佳的情况下最多可以处理数千个)。但是由于这不适用于分析型场景。建议每秒最多查询 100 次。

  • 数据写入性能:建议每次写入不少于 1000 行的批量写入,或每秒不超过一个写入请求。当使用 tab-separated 格式将一份数据写入到 MergeTree 表中时,写入速度大约为 50 200MB/s 。如果您写入的数据每行为 1Kb ,那么写入的速度为 50 000 200 000 行每秒。如果您的行更小,那么写入速度将更高。为了提高写入性能,您可以使用多个 INSERT 进行并行写入,这将带来线性的性能提升。

count: 千万级别, 500 毫秒, 1 亿 800 毫秒  2 亿 900 毫秒 3 亿 1.1 group: 百万级别 200 毫米,千万 1 秒, 1 亿 10 秒, 2 亿 20 秒, 3 亿 30 join :千万 -10 600 毫秒, 千万 - 百万: 10 秒,千万 - 千万 150

ClickHouse 并非无所不能,查询语句需要不断的调优,可能与查询条件有关,不同的查询条件表是左 join 还是右 join 也是很有讲究的。

其他补充:

  • MySQL 单条 SQL 是单线程的,只能跑满一个 core ClickHouse 相反,有多少 CPU ,吃多少资源,所以飞快;

  • ClickHouse 不支持事务,不存在隔离级别。 ClickHouse 的定位是分析性数据库,而不是严格的关系型数据库。

  • IO 方面, MySQL 是行存储, ClickHouse 是列存储,后者在 count() 这类操作天然有优势,同时,在 IO 方面, MySQL 需要大量随机 IO ClickHouse 基本是顺序 IO

  • 有人可能觉得上面的数据导入的时候,数据肯定缓存在内存里了,这个的确,但是 ClickHouse 基本上是顺序 IO 。对 IO 基本没有太高要求,当然,磁盘越快,上层处理越快,但是 99% 的情况是, CPU 先跑满了(数据库里太少见了,大多数都是 IO 不够用)。

1.4  应用场景

  • 绝大多数请求都是用于读访问的

  • 数据需要以大批次(大于 1000 行)进行更新,而不是单行更新;或者根本没有更新操作

  • 数据只是添加到数据库,没有必要修改

  • 读取数据时,会从数据库中提取出大量的行,但只用到一小部分列

  • 表很 “宽”,即表中包含大量的列

  • 查询频率相对较低(通常每台服务器每秒查询数百次或更少)

  • 对于简单查询,允许大约 50 毫秒的延迟

  • 列的值是比较小的数值和短字符串(例如,每个 URL 只有 60 个字节)

  • 在处理单个查询时需要高吞吐量(每台服务器每秒高达数十亿行)

  • 不需要事务

  • 数据一致性要求较低

  • 每次查询中只会查询一个大表。除了一个大表,其余都是小表

  • 查询结果显著小于数据源。即数据有过滤或聚合。返回结果不超过单个服务器内存大小

clickhouse 安装

2.1 单机安装

2.1.1 添加官方存储库

sudo yum install yum-utils

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

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

如果您想使用最新的版本,请用 testing 替代 stable( 我们只推荐您用于测试环境 ) prestable 有时也可用。

2.1.2 安装 clickhouse 的服务端和客户端

sudo yum install clickhouse-server clickhouse-client

2.1.3 启动

service clickhouse-server start

日志默认路径:

/var/log/clickhouse-server

如果你在 clickhouse-server 没有找到任何有用的信息或根本没有任何日志,您可以使用命令查看 system.d :

$ sudo journalctl -u clickhouse-server

2.1.4 启动客户端

clickhouse-client

/usr/bin/clickhouse-client --host 192.168. xxx . xxx   -m

命令行参数

  • --host, -h - – 服务端的 host 名称 , 默认是 localhost 。您可以选择使用 host 名称或者 IPv4 IPv6 地址。

  • --port – 连接的端口,默认值: 9000 。注意 HTTP 接口以及 TCP 原生接口使用的是不同端口。

  • --user, -u – 用户名。默认值: default

  • --password – 密码。默认值:空字符串。

  • --query, -q – 使用非交互模式查询。

  • --database, -d – 默认当前操作的数据库 . 默认值:服务端默认的配置(默认是 default )。

  • --multiline, -m – 如果指定,允许多行语句查询( Enter 仅代表换行,不代表查询语句完结)。

  • --multiquery, -n – 如果指定 , 允许处理用 ; 号分隔的多个查询,只在非交互模式下生效。

  • --format, -f – 使用指定的默认格式输出结果。

  • --vertical, -E – 如果指定,默认情况下使用垂直格式输出结果。这与– format=Vertical 相同。在这种格式中,每个值都在单独的行上打印,这种方式对显示宽表很有帮助。

  • --time, -t – 如果指定,非交互模式下会打印查询执行的时间到 stderr 中。

  • --stacktrace – 如果指定,如果出现异常,会打印堆栈跟踪信息。

  • --config-file – 配置文件的名称。

  • --secure – 如果指定,将通过安全连接连接到服务器。

  • --history_file — 存放命令历史的文件的路径。

  • --param_<name> — 查询参数配置查询参数 .

2.1.5  测试

jErqeyi.png!mobile

2.2 集群安装

clickhouse 的集群安装就是在每台机器上安装 CH 的服务端以及客户端 !! 所以在每台机器上重复单机安装步骤 !

2.2.1 修改 config.xml

修改 /etc/clickhouse-server/ 目录下的 config.xml

<listen_host>::</listen_host>

<!-- <listen_host>::1</listen_host> -->

<!-- <listen_host>127.0.0.1</listen_host> -->

<macros>

<shard>01</shard>

<replica> 192.168.xxx.xxx </replica>

</macros>

2.2.2 新建 metrika.xml

/etc/ 下创建集群配置文件 metrika.xml 文件 ( 这个文件需要创建 ), CH 启动的时候会加载这个配置文件以集群的形式启动 CH

<yandex>

<clickhouse_remote_servers>

<wedoctor_ch_cluster1>

<shard>

<internal_replication>true</internal_replication>

<replica>

<host>192.168.xxx.xxx</host>

<port>9000</port>

</replica>

</shard>

<shard>

<replica>

<internal_replication>true</internal_replication>

<host>192.168.xxx.xxx</host>

<port>9000</port>

</replica>

</shard>

</wedoctor_ch_cluster1>

</clickhouse_remote_servers>

<zookeeper-servers>

<node index="1">

<host>192.168.xxx.xxx</host>

<port>2181</port>

</node>

<node index="2">

<host>192.168.xxx.xxx</host>

<port>2181</port>

</node>

<node index="3">

<host>192.168.xxx.xxx</host>

<port>2181</port>

</node>

</zookeeper-servers>

<macros>

<shard>01</shard>

<replica>host1</replica>

</macros>

<networks>

<ip>::/0</ip>

</networks>

<clickhouse_compression>

<case>

<min_part_size>10000000000</min_part_size>                                        

<min_part_size_ratio>0.01</min_part_size_ratio>                                                                                                                                       

<method>lz4</method>

</case>

</clickhouse_compression>

</yandex>

2.2.3 分发修改

将配置文件分发到其他的 CH 节点上 , 并修改红色字体为自己的主机映射名 !

在每台机器上启动 CH 服务 . 以集群的形式启动 , 如果想要再以单节点的形式启动那么就删除 /etc/ 下的

metrika.xml 文件即可单节点的形式启动 !

2.2.4 查看 CH 的集群情况

select * from system.clusters;

2.2.5 测试

2.2.5.1 新建库

create database ck_dwd on CLUSTER wedoctor_ch_cluster1;

mEbyArV.png!mobile

2.2.5.2 新建表

drop table if exists ck_dwd.lzc_test_df ON CLUSTER wedoctor_ch_cluster1;

CREATE TABLE ck_dwd.lzc_test_df ON CLUSTER wedoctor_ch_cluster1

(                                 

id String COMMENT ' 主键 ',                                                      

name String COMMENT ' 名称 ',                            

is_deleted Int32 COMMENT ' 是否删除 0- 1- ',                                    

gmt_created DateTime COMMENT ' 创建时间 ',                                      

gmt_modified DateTime COMMENT ' 修改时间 '

)                                     

engine=ReplicatedMergeTree('/clickhouse/tables/ck_dwd/lzc_test_df','{shard}_{replica}')

order by (id)

settings index_granularity = 8192

;   

AZZR3iE.png!mobile

2.2.5.3 插入数据

insert into ck_dwd.lzc_test_df values(1,' 马云 ',1,'2019-01-01 00:00:00','2019-01-01 00:00:00') ;

eq2imaj.png!mobile

2.2.5.4 zookeeper 目录查看

ls /clickhouse/tables/ck_dwd/lzc_test_df

2.2.6 数据存储位置

/var/lib/clickhouse/data/

ZBbYRvj.png!mobile

muUBraN.png!mobile

clickhouse 实战

3.1 数据类型

AFF3myV.png!mobile

3.1.1  整型

JVvqmiZ.png!mobile

3.1.2  浮点型

ry6jQbM.png!mobile

建议尽可能以整数形式存储数据。例如,将固定精度的数字转换为整数值,如时间用毫秒为单位表示,因为浮点型进行计算时可能引起四舍五入的误差。

uMjeuyj.png!mobile

3.1.3  Decimal

Decimal(P,S),Decimal32(S),Decimal64(S),Decimal128(S)

有符号的定点数,可在加、减和乘法运算过程中保持精度。对于除法,最低有效数字会被丢弃(不舍入)。

参数

  • P - 精度。有效范围: [1:38] ,决定可以有多少个十进制数字(包括分数)。

  • S - 规模。有效范围: [0 P] ,决定数字的小数部分中包含的小数位数。

对于不同的 P 参数值 Decimal 表示,以下例子都是同义的:

-P [1:9]- 对于 Decimal32(S) -P [10:18]- 对于 Decimal64( 小号 ) -P [19:38]- 对于 Decimal128 S)

十进制值范围

  • Decimal32(S) - ( -1 * 10^(9 - S),1*10^(9-S) )

  • Decimal64(S) - ( -1 * 10^(18 - S),1*10^(18-S) )

  • Decimal128(S) - ( -1 * 10^(38 - S),1*10^(38-S) )

例如, Decimal32(4) 可以表示 -99999.9999 99999.9999 的数值,步长为 0.0001

3.1.4  布尔值

没有单独的类型来存储布尔值。可以使用 UInt8 类型,取值限制为 0 或 1。

3.1.5  字符串

1 String

字符串可以任意长度的。它可以包含任意的字节集,包含空字节。

2 FixedString(N)

固定长度 N 的字符串, N 必须是严格的正自然数。当服务端读取长度小于 N 的字符串时候,通过在字符串末尾添加空字节来达到 N 字节长度。当服务端读取长度大于 N 的字符串时候,将返回错误消息。

String 相比,极少会使用 FixedString ,因为使用起来不是很方便。

3.1.6  枚举

·  Enum8   'String'= Int8  对描述。

·  Enum16   'String'= Int16  对描述。

CREATE TABLE t_enum( x Enum8('hello' = 1, 'world' = 2))ENGINE = TinyLog

INSERT INTO t_enum VALUES ('hello'), ('world'), ('hello')

当您从表中查询数据时, ClickHouse Enum  中输出字符串值。

qmA3EjE.png!mobile

3.1.7  数组 Array(t)

SELECT array(1, 2) AS x, toTypeName(x);

EVZJZjf.png!mobile

SELECT [1, 2] AS x, toTypeName(x)

Jvuy2iU.png!mobile

数组里面数据类型必须一致,否则会抛出异常

SELECT array(1, 'a');

Received exception from server (version 1.1.54388):

Code: 386. DB::Exception: Received from localhost:9000, 127.0.0.1. DB::Exception: There is no supertype for types UInt8, String because some of them are String/FixedString and some of them are not.

3.1.8  元组

SELECT tuple(1,'a') AS x, toTypeName(x);

neI3eaQ.png!mobile

3.1.9  Date

CREATE TABLE dt(

`timestamp` Date,

`event_id` UInt8)ENGINE = TinyLog;

INSERT INTO dt Values (1546300800, 1), ('2019-01-01', 2);

SELECT * FROM dt;

6ZB3qeY.png!mobile

3.1.10  DateTime

CREATE TABLE dt(

`timestamp` DateTime('Europe/Moscow'),

`event_id` UInt8)ENGINE = TinyLog;

INSERT INTO dt Values (1546300800, 1), ('2019-01-01 00:00:00', 2);

SELECT * FROM dt;

ZnqQveJ.png!mobile

3.1  表引擎

表引擎(即表的类型)决定了:

数据的存储方式和位置,写到哪里以及从哪里读取数据

1.  支持哪些查询以及如何支持。

2.  并发数据访问。

3.  索引的使用(如果存在)。

4.  是否可以执行多线程请求。

5.  数据复制参数。

在读取时,引擎只需要输出所请求的列,但在某些情况下,引擎可以在响应请求时部分处理数据。对于大多数正式的任务,应该使用 MergeTree 族中的引擎。

3.1.1  日志引擎

这些引擎是为需要快速编写许多小表(最多约 100 万行)并在以后整体读取它们的情况下开发的。

共同属性

引擎:

  • 将数据存储在磁盘上。

  • 写入时将数据追加到文件末尾。

  • 支持并发数据访问的锁。

    INSERT 查询期间,表被锁定,并且其他用于读取和写入数据的查询都等待表解锁。如果没有数据写入查询,那么可以同时执行任意数量的数据读取查询。

  • 不支持突变。

  • 不支持索引。

    这意味着 SELECT 查询数据范围效率不高。

  • 不要自动写数据。

    如果某些操作中断了写操作(例如,异常的服务器关闭),则可能会获得带有损坏数据的表。

差异性

TinyLog 引擎是该系列中最简单的引擎,功能最差,效率最低。该 TinyLog 引擎不支持通过单个查询中的多个线程进行并行数据读取。它比支持单个查询并行读取的系列中其他引擎读取数据的速度慢,并且使用几乎与 Log 引擎一样多的文件描述符,因为它将每一列存储在单独的文件中。仅在简单情况下使用它。

Log StripeLog 引擎支持并行数据读取。读取数据时, ClickHouse 使用多个线程。每个线程处理一个单独的数据块。该 Log 引擎使用对表的每列一个单独的文件。 StripeLog 将所有数据存储在一个文件中。结果, StripeLog 引擎使用更少的文件描述符,但是 Log 引擎在读取数据时提供了更高的效率。

3.1.1.1  TinyLog

该表引擎通常与一次写入方法一起使用:一次写入数据,然后根据需要多次读取。例如,您可以将 TinyLog-type 表用于小批量处理的中间数据。请注意,将数据存储在大量小表中效率很低。

查询在单个流中执行。换句话说,此引擎旨在用于相对较小的表(最多约 1,000,000 行)。如果您有许多小表,则使用此表引擎很有意义,因为它 比Log引擎更简单(需要打开的文件更少)。

新建表

create table ck_dwd.tb_tinylog on CLUSTER wedoctor_ch_cluster1

(

id Int8

,name String

,age Int8

) engine=TinyLog ;

iIVbqqM.png!mobile

插入数据

insert into ck_dwd.tb_tinylog values(1,' 小红 ',56),(2,' 小明 ',55),(3,' 小白 ',123) ;

MfE3Urm.png!mobile

存储

数据存储在机器的磁盘上 , 每列一个文件 , 插入数据向列文件的后面追加

qMfEvyQ.png!mobile

3.1.1.2  StripeLog

该引擎属于日志引擎家族。请在“日志引擎系列”文章中查看日志引擎的常见属性及其区别。

当您需要编写少量数据(少于一百万行)的表时,请使用此引擎。

建表语法:

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

(

column1_name [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],

column2_name [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],

...

) ENGINE = StripeLog

新建表

CREATE TABLE ck_dwd.stripe_log_table on CLUSTER wedoctor_ch_cluster1

(

timestamp DateTime,

message_type String,

message String

)

ENGINE = StripeLog

;

插入数据

INSERT INTO ck_dwd.stripe_log_table VALUES (now(),'REGULAR','The first regular message');

INSERT INTO ck_dwd.stripe_log_table VALUES (now(),'REGULAR','The second regular message'),(now(),'WARNING','The first warning message');

查询

我们使用两个 INSERT 查询在 data.bin 文件内部创建两个数据块。

在选择数据时, ClickHouse 使用多个线程。每个线程读取一个单独的数据块,并在完成时独立返回返回的行。结果,在大多数情况下,输出中的行块顺序与输入中的相同块顺序不匹配。

例如:

select * from ck_dwd.stripe_log_table;

e6BRVfz.png!mobile

3.1.1.3  Log

日志与TinyLog的不 同之处在于,列文件中存在一个小的标记 文件。这些标记写在每个数据块上,并包含偏移量,这些偏移量指示从何处开始读取文件以跳过指定的行数。这样就可以在多个线程中读取表数据。对于并发数据访问,读操作可以同时执行,而写操作则阻止读操作和其他操作。日志引擎不支持索引。同样,如果写入表失败,则表将损坏,并且从表中读取将返回错误。日志引擎适用于临时数据,一次写入表以及测试或演示目的。

3.1.2  MergeTree Family

3.2.2.1 MergeTree

3.1.2.1  建表语法

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, ...]

3.1.2.2  参数解读

·  ENGINE— 引擎的名称和参数。 ENGINE = MergeTree() 。该 MergeTree 引擎没有参数。

·  ORDER BY — 排序键。

列名称或任意表达式的元组。范例: ORDER BY (CounterID, EventDate)

如果PRIMARY KEY子句未明确定义主键,则ClickHouse会将排序键用作主键。

ORDER BY tuple()如果不需要排序,请使用语法。请参阅选择主键。  

· PARTITION BY—分区键。可选的。

要按月分区,请使用toYYYYMM(date_column)表达式,其中date_column是日期类型为Date的列。此处的分区名称具有"YYYYMM"格式。

· PRIMARY KEY—主键(与排序键)不同。可选的。

默认情况下,主键与排序键(由ORDER BY子句指定)相同。因此,在大多数情况下,不必指定单独的PRIMARY KEY子句。

·  SAMPLE BY— 用于采样的表达式。可选的。

如果使用采样表达式,则主键必须包含它。范例: SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID))

·  TTL— 规则列表,用于指定行的存储持续时间并定义 磁盘和卷之间零件自动移动的逻辑。可选的。

结果必须有一个 Date 或一 DateTime 列。例: TTL date + INTERVAL 1 DAY

规则的类型 DELETE|TO DISK 'xxx'|TO VOLUME 'xxx' 指定了如果满足表达式(到达当前时间)将对零件执行的操作:删除过期的行,将零件(如果零件中的所有行都满足表达式)移动到指定的磁盘( TO DISK 'xxx' )或到( TO VOLUME 'xxx' )。规则的默认类型为删除( DELETE )。可以指定多个规则的列表,但最多只能有一个 DELETE 规则。

·  SETTINGS— 控制 MergeTree (可选)行为的其他参数:

²  index_granularity— 索引标记之间的最大数据行数。默认值: 8192 。请参见 数据存储。

²  index_granularity_bytes— 数据粒度的最大大小(以字节为单位)。默认值: 10Mb 。要仅按行数限制颗粒大小,请设置为 0 (不建议)。

²  min_index_granularity_bytes— 数据粒度的最小允许大小(以字节为单位)。默认值: 1024b 。为防止意外创建具有非常低的 index_granularity_bytes 的表提供保护。

²  enable_mixed_granularity_parts— 启用或禁用过渡以通过 index_granularity_bytes 设置控制颗粒尺寸。在版本 19.11 之前,只有 index_granularity 用于限制颗粒大小的设置。 index_granularity_bytes 从具有大行(数十和数百 MB )的表中选择数据时,此设置可提高 ClickHouse 性能。如果您的表具有大行,则可以为表启用此设置以提高 SELECT 查询效率。

²  use_minimalistic_part_header_in_zookeeper— ZooKeeper 中数据部分头的存储方法。如果为 use_minimalistic_part_header_in_zookeeper=1 ,则 ZooKeeper 存储的数据较少。

²  min_merge_bytes_to_use_direct_io— 使用对存储磁盘的直接 I / O 访问所需的最小合并操作数据量。合并数据部分时, ClickHouse 会计算要合并的所有数据的总存储量。如果卷超过 min_merge_bytes_to_use_direct_io 字节, ClickHouse 将使用直接 I / O 接口( O_DIRECT 选项)读取数据并将数据写入存储磁盘。如果为 min_merge_bytes_to_use_direct_io = 0 ,则直接 I / O 被禁用。默认值: 10 * 1024 * 1024 * 1024 字节。

²  merge_with_ttl_timeout— 重复与 TTL 合并之前的最小延迟(以秒为单位)。默认值: 86400 1 天)。

²  write_final_mark— 启用或禁用在数据部分的末尾(最后一个字节之后)写入最终索引标记。默认值: 1. 不要关闭它。

²  merge_max_block_size— 合并操作的块中的最大行数。默认值: 8192

²  storage_policy— 存储策略。

²  min_bytes_for_wide_part min_rows_for_wide_part— 可以以 Wide 格式存储的数据部分中的最小字节数 / 行数。您可以设置这些设置之一,全部或全部。

²  max_parts_in_total — 所有分区中的最大零件数。

3.1.2.3  示例

建表

create table ck_dwd.tb_merge_tree on CLUSTER wedoctor_ch_cluster1(

id Int8 ,

name String ,

ctime Date

)

engine=MergeTree()

order by id

partition by name ;

插入数据

insert into ck_dwd.tb_merge_tree values

(1,'aa','2020-08-07'),(4,'aa','2020-08-07'),(3,'bb','2020-08-07'),(2,'bb','2020-08-07') ;

YVfEry.png!mobile

qa6n2yM.png!mobile

再次插入数据

insert into ck_dwd.tb_merge_tree values(5,'aa','2020-08-07'),(6,'bb','2020-08-07') ;

aiiimqj.png!mobile

VZJnYfE.png!mobile

合并多次插入数据的分区

optimize table ck_dwd.tb_merge_tree ;

6BVNnmy.png!mobile

再合并一次

optimize table ck_dwd.tb_merge_tree ;

FVzIFjA.png!mobile

合并完成之后的存储结构:

QBFfAzR.png!mobile

过段时间以后 CK 内部自动的会删除合并前的多余的文件夹

3.2.2.2 ReplacingMergeTree

  • 该引擎与 MergeTree的 不同之处在于,它删除具有相同 排序键值 ORDER BY 表部分,不是 PRIMARY KEY )的重复条目。

  • 重复数据删除仅在合并期间发生。合并发生在后台的未知时间,因此您无法为此计划。某些数据可能仍未处理。尽管您可以使用 OPTIMIZE 查询运行计划外的合并,但不要指望使用它,因为 OPTIMIZE 查询将读写大量数据。

  • 因此, ReplacingMergeTree 它适合在后台清除重复数据以节省空间,但不能保证不存在重复数据。

建表语法

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

(

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

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

...

) ENGINE = ReplacingMergeTree([ver])

[PARTITION BY expr]

[ORDER BY expr]

[PRIMARY KEY expr]

[SAMPLE BY expr]

[SETTINGS name=value, ...]

行的唯一性由 ORDER BY 表部分而不是决定 PRIMARY KEY

  • 在数据合并的时候 根据主键分区内自动的去重主键重复的数据 , 我们可以指定一个字段作为数据的版本 , 当去除重复数据的时候保留版本大的数据 !

  • CH内部会自动的合并数据并去重重复数据 , 当然我们也可以手动的执行合并 , 但是每次处罚命令只能合并一个分区的数据 , 一般情况下等待他自己合并数据即可 ! 所以我们无法保证表中不存在重复主键数据

3.2.2.3 SummingMergeTree

引擎从 MergeTree 继承。区别在于,在合并 SummingMergeTree 表的数据部分时, ClickHouse 会将所有具有相同主键(或更准确地说,具有相同 排序键 )的行替换为一行,该行包含具有数值数据类型的列的汇总值。如果排序键的组成方式是单个键值对应于大量行,则这将大大减少存储量并加快数据选择的速度。

建议与一起使用引擎 MergeTree 。将完整数据存储在 MergeTree 表中,并 SummingMergeTree 用于汇总数据存储,例如,在准备报告时。这样的方法将防止您由于主键构成不正确而丢失有价值的数据。

建表语法

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

(

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

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

...

) ENGINE = SummingMergeTree([columns])

[PARTITION BY expr]

[ORDER BY expr]

[SAMPLE BY expr]

[SETTINGS name=value, ...]

建表:

CREATE TABLE summtt

(

key UInt32,

value UInt32

)

ENGINE = SummingMergeTree()

ORDER BY key

插入数据

INSERT INTO summtt Values(1,1),(1,2),(2,1)

3.2.2.4 CollapsingMergeTree

3.2.2.5 VersionedCollapsingMergeTree

3.2.2.6 AggregatingMergeTree

3.2.2.7 GraphiteMergeTree

3.2  sql 语法

3.2.1  创建数据库

3.2.1.1  本地引擎

create database ck_dwd on CLUSTER wedoctor_ch_cluster1;

3.3.1.2 Mysql 引擎

MySQL 引擎用于将远程的 MySQL 服务器中的表映射到 ClickHouse 中,并允许您对表进行 INSERT SELECT 查询,以方便您在 ClickHouse MySQL 之间进行数据交换。

MySQL 数据库引擎会将对其的查询转换为 MySQL 语法并发送到 MySQL 服务器中,因此您可以执行诸如 SHOW TABLES SHOW CREATE TABLE 之类的操作。

创建数据库

CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster]

ENGINE = MySQL('host:port', ['database' | database], 'user', 'password')

引擎参数

·  host:port — MySQL 服务器地址。

·  database — 远程数据库名称。

·  user — MySQL 用户。

·  password - 用户密码。

数据类型支持

InAB7vU.png!mobile

创建一个数据库

CREATE DATABASE IF NOT EXISTS ck_mysql

ENGINE = MySQL('192.168. xxx . xxx :330 2 ', test, root , ' root ')

;

Show databases;

eqMzQbu.png!mobile

use ck_mysql;

show tables;

nAZRRv3.png!mobile

查询是否有数据: select * from order_detail limit 10;

数据插入到ck,发现 mysql 的数据也会发生变化

3.2.2  创建表

3.2.2.1  基本建表语法

create table ck_dwd.tb_tinylog on CLUSTER wedoctor_ch_cluster1

(

id Int8

,name String

,age Int8

) engine=TinyLog ;  

3.2.2.2  as 其他表结构

create table  ck_dwd.tb_tinylog 2   as  ck_dwd.tb_tinylog

Desc ck_dwd.tb_tinylog 2

jmIbMra.png!mobile

3.2.2.3  as 查询数据结构

create table if not exists   ck_dwd.tb_tinylog 3  engine=TinyLog as select id , name ,age from ck_dwd.tb_tinylog ;

Yviauev.png!mobile

3.2.2.4  列属性

默认值

create table ck_dwd.tb_t1(

id Int8 ,

name String,

role String default 'vip')

engine=TinyLog ;

TTL

定义值的存储时间。只能为 MergeTree 系列表指定。确定值的生存期。

当列中的值过期时, ClickHouse 会将其替换为列数据类型的默认值。如果数据部分中的所有列值均已过期,则 ClickHouse 将从文件系统中的数据部分删除此列。

TTL 可以为整个表和每个单独的列设置该子句。表级 TTL 也可以指定在磁盘和卷之间自动移动数据的逻辑

CREATE TABLE     ck_dwd. tb_ttl

(

ctime DateTime,

name String

)

ENGINE = MergeTree

PARTITION BY toYYYYMM(ctime)

ORDER BY ctime

TTL ctime + INTERVAL 15 SECOND;  -- 设置表过期时间为 15

CREATE TABLE  ck_dwd. tb_ttl

(

d DateTime,

a Int TTL d + INTERVAL 1 MONTH,

b Int TTL d + INTERVAL 1 MONTH,

c Int TTL d + INTERVAL 10 SECOND,

e String

)

ENGINE = MergeTree

PARTITION BY toYYYYMM(d)

ORDER BY d;

insert into tb_ttl values(now() , 100,100,88,'hangge') ;

10 秒以后查询数据 ,TTL 过期的数据被替换成了默认值

3.2.2.5  临时表

ClickHouse 支持具有以下特征的临时表:

  • 会话结束时,包括连接断开,临时表都会消失。

  • 临时表仅使用内存引擎。

  • 无法为临时表指定数据库。它是在数据库外部创建的。

  • 无法在所有群集服务器上通过分布式 DDL 查询创建临时表(通过使用 ON CLUSTER ):该表仅存在于当前会话中。

如果一个临时表与另一个表具有相同的名称,并且查询指定表名而不指定 DB ,则将使用该临时表

CREATE TEMPORARY TABLE [IF NOT EXISTS] table_name(

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

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

...)

在大多数情况下,临时表不是手动创建的,而是在使用外部数据进行查询或分布式时创建的 (GLOBAL) IN

可以使用 ENGINE = Memory 的表代替临时表。

3.3  函数

3.3.1  普通函数

3.3.2  表函数

3.3.2.1  File

数据文件必须在指定的目录下

/var/lib/clickhouse/user_files

vqMfuaA.png!mobile

3Afemee.png!mobile

3.3.2.2  Url

3.3.2.3  Mysql

SELECT * FROM mysql(' ip : port ', ' db ', ' table_name ', ' user_name ', ' password ') limit 10;

3.3.2.4  Jdbc

SELECT * FROM jdbc('jdbc:mysql:// ip : port /?user= user_name &password= password ', ' db ', ' table_name ') limit 10;

发现报错

DB::Exception: jdbc-bridge is not running. Please, start it manually.

juUneee.png!mobile

解决方案:

下载 https://github.com/ClickHouse/clickhouse-jdbc-bridge

编译 mvn clean package

BVbARfz.png!mobile

YvMj2qb.png!mobile

上传编译好的 jar 包到安装 clickhouse 所在的机器

启动 clickhouse-jdbc-bridge

java -jar clickhouse-jdbc-bridge-2.0.0-SNAPSHOT.jar   --driver-path ./ --listen-host 0.0.0.0 &

3.3.2.5  Hdfs

SELECT *FROM hdfs('hdfs://hdfs1:9000/test', 'CSV', 'column1 UInt32, column2 UInt32, column3 UInt32') LIMIT 2

3.4  导出数据

clickhouse-client --query="SELECT * FROM ck_dwd.tb_tinylog FORMAT CSV " > 20201228.csv

3.5  JAVA 代码访问 clickhouse

3.5.1  新增依赖

<dependency>

<groupId>ru.yandex.clickhouse</groupId>

<artifactId>clickhouse-jdbc</artifactId>

<version>0.1.54</version>

</dependency>

<dependency>

<groupId>org.apache.httpcomponents</groupId>

<artifactId>httpclient</artifactId>

<version>4.5.7</version>

</dependency>

<dependency>

<groupId>org.apache.httpcomponents</groupId>

<artifactId>httpcore</artifactId>

<version>4.4.12</version>

</dependency>

</dependencies>

3.5.2  代码

package com.wedoctor;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.Statement;

public class Clickhouse_connect {

public static void main(String[] args) throws  Exception{

Class.forName("ru.yandex.clickhouse.ClickHouseDriver");

String address = "jdbc:clickhouse://192.168.xxx.xxx:8123/ck_dwd";

Connection conn = DriverManager.getConnection(address);

Statement st = conn.createStatement();

ResultSet resultSet = st.executeQuery("select * from tb_tinylog");

while(resultSet.next()){

String name = resultSet.getString("name");

String age = resultSet.getString("age");

System.out.println(name+"---"+age);

}

resultSet.close();

st.close() ;

conn.close();

}

}

3.6.3 运行结果

yeaE3av.png!mobile

可视化界面

http://ui.tabix.io/#!/login

7bquumi.png!mobile

QRZv2yE.png!mobile

五 hive数据同步到 clickhouse

5.1  Datax

BJrmiaF.png!mobile

5.2 waterdrop

Waterdrop 是生产环境中的海量数据计算引擎,可以满足你的流式,离线, etl ,聚合等计算需求。 InterestingLab 是一个以为用户简化和普及大数据处理为核心目标的开源技术团队。核心项目 Waterdrop 是基于 Spark Flink 构建的配置化,零开发成本的大规模流式及离线处理工具。目前已有 360 、滴滴、华为、微博、新浪、一点资讯、永辉集团、水滴筹等多个行业的公司在线上使用。

项目地址 : https://github.com/InterestingLab/waterdrop

文档地址: https://interestinglab.github.io/waterdrop-docs/

快速入门: https://interestinglab.github.io/waterdrop-docs/#/zh-cn/v1/quick-start

行业应用案例: https://interestinglab.github.io/waterdrop-docs/#/zh-cn/v1/case_study/

插件开发: https://interestinglab.github.io/waterdrop-docs/#/zh-cn/v1/developing-plugin

Waterdrop 的设计与实现原理: https://mp.weixin.qq.com/s/lYECVCYdKsfcL64xhWEqPg

Flink 技术分享公开课视频: http://www.julyedu.com/video/play/262/7691

5.2.1 架构

AfE7n26.png!mobile

5.2.1.1 input

aUZnM3v.png!mobile

5.2.1.2  filter

QFrm2m2.png!mobile

5.2.1.3 output

N7VjYvZ.png!mobile

5.2.2 安装使用

5.2.2.1 下载

https://github.com/InterestingLab/waterdrop/releases

Fj6n6nu.png!mobile

5.2.2.2 解压

tar -zxvf waterdrop-1.4.2-with-spark.zip

5.2.2.3 配置文件修改 (hive-->clickhouse)

waterdrop-env.sh

v im ../config/waterdrop-env.sh

#!/usr/bin/env bash

# Home directory of spark distribution.

SPARK_HOME=/usr/local/spark-current/

wy_zh_user_df.conf

spark {

spark.app.name = "hive-ck"

spark.executor.instances = 8

spark.executor.cores = 2

spark.executor.memory = "2g"

spark.sql.catalogImplementation = "hive"

spark.yarn.queue="root. test "

}

input {

hive {

pre_sql = "select * from wedw_tmp. test _df"

table_name = " test _df"

}

}

filter {

}

output {

clickhouse {

host = "10.20. xxx . xxx :8123"

database = " ck "

clickhouse.socket_timeout=600000

table = " test _df"

username = " xxx "

password = "xxxx"

bulk_size = 50000

retry = 3

}

}

启动 waterdrop 同步数据

/home/pgxl/liuzc/waterdrop-1.4.2/bin/start-waterdrop.sh --master yarn --deploy-mode client --config /home/pgxl/liuzc/waterdrop-1.4.2/config/ test .conf

Zvi6RzE.png!mobile

5.2.3 常见问题

5.2.3.1 Too many parts ( 304 ). Merges are processing significantly slower than inserts

merge 速度跟不上插入速度,也就是 insert ,可能原因:   数据是否可能跨多个分区, 如果这样的话每次写入有多个 partition merge 压力很大 ,可以减少并发

spark.executor.instances = 4

5.2.3.2 read time out 

超时问题,可适当增加 超时时间

clickhouse.socket_timeout=600000

5.2.3.3 找不到类

需要看一下 spark 的配置  

5.2.3.4 同一段 sql ,在 hive 里面查和在 ck 里面查,结果不一样?

结果不一样,是因为 clickhouse sql 里面,在 -- 注释掉的地方就截断了,导致后面的 where 条件全没了,这个需要注意!!!

参考:

          clickhouse官网

多易教育clickhouse教程

工作经验总结

2020大数据面试题真题总结(附答案)

一文探究数据仓库体系(2.7万字建议收藏)

一文探究Hadoop(3万字长文,建议收藏)

一文带你走进HIVE的世界(1.8W字建议收藏)

一文带你全方位了解Flink(3.2W字建议收藏)

你要悄悄学会HBase,然后惊艳所有人(1.7万字建议收藏)

一文带你深入了解kafka并提供52道企业常见面试题(1.8W字建议收藏)

非常全面的DolphinScheduler(海豚调度)安装使用文档

Hive调优,数据工程师成神之路

数据质量那点事

简述元数据管理

简单聊一聊大数据学习之路

Y7B7FvJ.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK