Clickhouse 开发实践总结

1 列式存储与行式存储对比

  • 采用行式存储时,数据在磁盘上的组织结构为:

  • 行式存储

    好处是想查某个人所有的属性时,可以通过一次磁盘查找加顺序读取就可以。但是当想查所有人的年龄时,需要不停的查找,或者全表扫描才行,遍历的很多数据都是不需要的。

  • 采用列式存储时,数据在磁盘上的组织结构为:

  • 列式存储

  • 这时想查所有人的年龄只需把年龄那一列拿出来就可以了

  • 对于列的聚合,计数,求和等统计操作原因优于行式存储。

  • 由于某一列的数据类型都是相同的,针对于数据存储更容易进行数据压缩,每一列选择更优的数据压缩算法,大大提高了数据的压缩比重。

  • 由于数据压缩比更好,一方面节省了磁盘空间,另一方面对于 cache 也有了更大的发挥空间。

2 数据类型-典型的cpp

  • 整型:int8/int16/int32/int64 uint8/uint16/uint32/uint64

  • 浮点型:float32 float64

  • decimal型:Decimal32(s)->Decimal(9-s,s) Decimal64(s)->Decimal(18-s,s) Decimal128(s)-Decimal(38-s,s)

  • 字符串:String FixedString(N)固定长度

  • 枚举型:Enum8 用 'String'= Int8 对描述 Enum16 用 'String'= Int16 对描述

  • 日期类型:date-2个字节 datetime datetime64

  • 数组类型:Array

3表引擎

3.1 TinyLog

  • 以列文件的形式保存在磁盘上,不支持索引,没有并发控制

3.2 Memory

  • 内存引擎,数据以未压缩的原始形式直接保存在内存当中,服务器重启数据就会消失

3.3 MergeTree:ClickHouse 中最强大的表引擎当属 MergeTree(合并树)引擎

create table t_order_mt(
id UInt32,
sku_id String,
total_amount Decimal(16,2),
create_time Datetime
) engine =MergeTree
partition by toYYYYMMDD(create_time)
primary key (id)
order by (id,sku_id);

1) partition by 分区(可选)

  • 分区目录:MergeTree 是以列文件+索引文件+表定义文件组成的,但是如果设定了分区那么这些文件就会保存到不同的分区目录中

  • 分区并行计算:

  • 数据写入与分区合并:任何一个批次的数据写入都会产生一个临时分区,不会纳入任何一个已有的分区。手动合并optimize table xxxx final;

2) primary key 主键(可选)

  • 只提供了数据的一级索引,但是却不是唯一约束。这就意味着是可以存在相同 primary key 的数据的。

  • index granularity: 直接翻译的话就是索引粒度,指在稀疏索引中两个相邻索引对应数据的间隔。一般不修改

3) order by(必选)

  • 分区内排序

  • 不设置主键的情况,很多处理会依照 order by 的字段进行处理

  • 主键必须是 order by 字段的前缀字段。(id,sku_id) 那么主键必须是 id 或者(id,sku_id)

4)二级索引

create table t_order_mt2(
id UInt32,
,
Decimal(16,2),
Datetime,
INDEX a total_amount TYPE minmax GRANULARITY 5
) engine =MergeTree
partition by toYYYYMMDD()
primary key (id)
order by (id, sku_id); --GRANULARITY N 是设定二级索引对于一级索引粒度的粒度

5)TTL

  • 列级:

create table t_order_mt3(
id UInt32,
,
Decimal(16,2) TTL create_time+interval 10 SECOND,
Datetime
) engine =MergeTree
partition by toYYYYMMDD()
primary key (id)
order by (id, sku_id);

  • 表级

alter table t_order_mt3 MODIFY TTL create_time + INTERVAL 10 SECOND;
-- 涉及判断的字段必须是 Date 或者 Datetime 类型,推荐使用分区的日期字段。

3.4 ReplacingMergeTree - 去重

1) 去重时机

  • 数据的去重只会在合并的过程中出现。合并会在未知的时间在后台进行,所以你无法预先作出计划。有一些数据可能仍未被处理。

2) 去重范围

  • 如果表经过了分区,去重只会在分区内部进行去重,不能执行跨分区的去重

create table t_order_rmt(
id UInt32,
,
Decimal(16,2) ,
Datetime
) engine =ReplacingMergeTree()
partition by toYYYYMMDD()
primary key (id)
order by (id, sku_id);

3)ReplacingMergeTree()

  • 填入的参数为版本字段,重复数据保留版本字段值最大的。如果不填版本字段,默认按照插入顺序保留最后一条。

3.5 SummingMergeTree - 预聚合

  • 以 SummingMergeTree()中指定的列作为汇总数据列

  • 可以填写多列必须数字列,如果不填,以所有非维度列且为数字列的字段为汇总数据列

  • 以 order by 的列为准,作为维度列

  • 其他的列按插入顺序保留第一行

  • 不在一个分区的数据不会被聚合

  • 只有在同一批次插入(新版本)或分片合并时才会进行聚合

4 sql

1)Update 和 Delete

  • 虽然可以实现修改和删除,但是和一般的 OLTP 数据库不一样,Mutation 语句是一种很“重”的操作,而且不支持事务。“重”的原因主要是每次修改或者删除都会导致放弃目标数据的原有分区,重建新分区。所以尽量做批量的变更,不要进行频繁小数据的操作。

5 分片与副本集群

ClickHouse 的集群是表级别的,实际企业中,大部分做了高可用,但是没有用分片,避免降低查询性能以及操作集群的复杂性。

5.1 高可用集群

clickhouse高可用

  • 每个节点/etc/clickhouse-server/ config.xml

<yandex>
<zookeeper-servers>
<node index="1">
<host>hadoop01</host>
<port>2181</port>
</node>
<node index="2">
<host>hadoop02</host>


<node index="3">
<host>hadoop03</host>


</zookeeper-servers>
</yandex>

  • 创建表设置分片与副本

create table t_order_rep2 (
id UInt32,
,
Decimal(16,2),
Datetime
) engine =ReplicatedMergeTree('/clickhouse/table/01/t_order_rep','rep_01')
partition by toYYYYMMDD()
primary key (id)
order by (id,sku_id);
  • ReplicatedMergeTree 中,第一个参数是分片的 zk_path 一般按照: /clickhouse/table/{shard}/{table_name} 的格式写,如果只有一个分片就写 01 即可

  • 第二个参数是副本名称,相同的分片副本名称不能相同

5.2 带有路由高可用集群

  • 配置每个节点/etc/clickhouse-server/config.xml

<?xml version="1.0"?>

<remote_servers>
<ck_cluster> <!-- 集群名称-->
<shard> <!--集群的第一个分片-->
<internal_replication>true</internal_replication>
<replica> <!--该分片的第一个副本-->
<host>hadoop102</host>
<port>9000</port>
</replica>
<replica> <!--该分片的第二个副本-->
<host>hadoop103</host>


</shard>

<shard> <!--集群的第二个分片-->
<internal_replication>true</internal_replication>
<replica> <!--该分片的第一个副本-->
<host>hadoop104</host>
<port>9000</port>
</replica>

</ck_cluster>
</remote_servers>


<node index=>
<host>hadoop102</host>
<port>2181</port>
</node>
<node index=>
<host>hadoop103</host>
<port>2181</port>

<node index=>





<macros>
<shard>01</shard> <!--不同机器放的分片数不一样-->
<replica>rep_1_1</replica> <!--不同机器放的副本数不一样-->
</macros>

  • 分片和副本名称从配置文件的宏定义中获取

create table st_order_mt on cluster gmall_cluster (
id UInt32,
,
Decimal(16,2),
Datetime
) engine
= ReplicatedMergeTree('/clickhouse/tables/{shard}/st_order_mt','{replica}')
partition by toYYYYMMDD()
primary key (id)
order by (id,sku_id);

  • Distributed类mysql之mycat

create table st_order_mt_all2 on cluster ck_cluster
(
id UInt32,
,
Decimal(16,2),
Datetime
) engine = Distributed(ck_cluster,default, st_order_mt,hiveHash(sku_id));

  • Distributed(集群名称,库名,本地表名,分片键)

  • 分片键必须是整型数字,所以用 hiveHash 函数转换,也可以 rand()

6 OLAP技术演进与选型

主流OLAP引擎对比

6.1 ClickHouse优点

  • 灵活,支持明细数据SQL查询,并用物化视图加速。

  • 多核(垂直扩展),可以在一台机器上使用多线程去进行查询;分布式处理,它有不同的分片,这样的话可以进行水平扩展,MPP架构。

  • 支持实时批量数据摄入。

  • 列式存储、向量化引擎、代码编译生成。向量化引擎和代码编译生成基本是为了解决算子瓶颈,如果不通过这些技术的话一般是个火山模型,火山模型会有一些虚函数以及分支判断之间的一些开销。通过这两种方法,向量化可以去平摊开销,代码编译可以把它转成以数据为中心进而消除开销。但是这两种方法也不是万能的,比如说当Aggregation或者Join数据量比较大时候需要物化到内存,物化到内存的时候瓶颈也就产生了,因此也不会有非常大的性能争议。

  • 主键索引,ClickHouse会按照用户设置的主键进行排序,ClickHouse中MergeTree的文件就是按照这个逐渐进行排序的,Bloom Filter、minmax等做了二级索引。

6.2 ClickHouse缺点

  • 没有高速,低延迟的更新和删除方法。不擅长单行数据,行级别数据的更新删除方法一般都是异步进行。

  • 稀疏索引使得点查性能不佳。点查没办法用ClickHouse,最好的是用KV类型的Redis或者HBASE。

  • 不支持事务。尽管事务现在对OLAP也会有一些用途,但是不是非常大的用途。

6.3 ClickHouse的应用场景:

  • 用户行为分析,精细化运营分析:日活、留存率分析、路径分析、有序漏斗转化率分析、Session分析等。

  • 实时日志分析,监控分析,实时数仓。

猜你喜欢

转载自blog.csdn.net/ytp552200ytp/article/details/125019388