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

​一 clickhouse简介

1.1 什么是clickhouse

ClickHouse是俄罗斯的Yandex于2016年开源的一个用于联机分析(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 测试

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;

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

;   

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') ;

2.2.5.4 zookeeper目录查看

ls /clickhouse/tables/ck_dwd/lzc_test_df

 2.2.6 数据存储位置

/var/lib/clickhouse/data/

三 clickhouse实战

3.1 数据类型

3.1.1 整型

3.1.2 浮点型

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

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 字符串

1String

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

2FixedString(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 中输出字符串值。

 3.1.7 数组 Array(t)

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

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

 

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

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);

 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;

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;

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 ;

插入数据

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

 存储

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

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;

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') ;

再次插入数据

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

合并多次插入数据的分区

optimize table ck_dwd.tb_merge_tree ;

再合并一次

optimize table ck_dwd.tb_merge_tree ;

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

过段时间以后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 - 用户密码。

数据类型支持

创建一个数据库

CREATE DATABASE IF NOT EXISTS ck_mysql

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

;

Show databases;

use ck_mysql;

show tables;

查询是否有数据: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_tinylog2  as  ck_dwd.tb_tinylog

Desc ck_dwd.tb_tinylog2

3.2.2.3 as 查询数据结构

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

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

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.

解决方案:

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

编译 mvn clean package

上传编译好的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 运行结果

四 可视化界面

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

 五 hive数据同步到clickhouse

5.1 Datax

 

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 架构

5.2.1.1 input

5.2.1.2  filter

5.2.1.3 output

5.2.2 安装使用

5.2.2.1 下载

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

5.2.2.2 解压

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

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

waterdrop-env.sh

vim ../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

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教程

         工作经验总结

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值