# clickhouse笔记

https://developer.aliyun.com/article/762097?spm=a2c6h.14164896.0.0.bf39420cnHkODH

https://help.aliyun.com/document_detail/156340.html?spm=a2c4g.11186623.6.610.47ce6bcft384OY

https://clickhouse.tech/docs/zh/engines/table-engines/mergetree-family/mergetree/


复制表、分布式表机制. 分片方式.
https://blog.csdn.net/nazeniwaresakini/article/details/105858390

## 能力
* 数据类型:整数/浮点/decimal/字符串/时间/boolean/数组/元组/domain
* 可用JDBC连接
* TTL: 设定数据过期时间,并自动归档
* 参数设置: 
    config.xml或user.xml,需重启集群.
    云数据库ClickHouse仅支持user.xml里的参数设置
* 数据库引擎:延迟引擎/mysql/自带数据库引擎
* 表引擎: ClickHouse表引擎一共分为四个系列，分别是Log、MergeTree、Integration、Special。其中包含了两种特殊的表引擎Replicated、Distributed，功能上与其他表引擎正交，根据场景组合使用
* 本地表,分布式表;  单机表,复制表.
* 分区, 分片

## 阿里云提供
高可用集群与单副本集群,高可用集群每个分片至少有两个副本, 所以成本至少是两倍.
集群监控与报警
使用DMS进行数据管理
开源测试集导入以及性能测试
慢sql
正在运行的SQL管理

## 前端
阿里云quickBI
grafana, 使用grafana可使用类sql快速搭建图表,还可以以iframe嵌入到其他系统中.


## 阿里云功能限制
高可用集群必须用复制表引擎；
DDL操作需要使用ON CLUSTER default语句在所有server上执行。
只支持分布式表，也即多台ClickHouse server会自动组成分布式集群，不支持单机表；默认所有server都会自动组成名字为default的集群，DDL需要使用ON ClUSTER default语句在所有server上执行。
不支持用户自行配置remote cluster。
不支持File、URL table engine。
不支持file、url等table function。
不支持用户自定义profile。

## 表
从数据分布上，可以分为本地表、分布式表两种类型。
从存储引擎上，可以分为单机表、复制表两种类型。

![表](../开发文档/C综合需求/meida/clickhouse01.png)

参考 https://blog.csdn.net/nazeniwaresakini/article/details/105858390

### Partitioning分区
分区是在 建表 时通过 PARTITION BY expr 子句指定的.
新数据插入到表中时，这些数据会存储为按主键排序的新片段（块）。插入后 10-15 分钟，同一分区的各个片段会合并为一整个片段。
分区优点:
    * 可以充分利用多核cpu能力, 同时在多个分区中进行查询, 或者根据分区key, 只查询需要的分区.
    * 对partition进行TTL管理，淘汰过期的分区数据

### Sharding分片
* 分布式集群分片模式:
    1. random随机分片：写入数据会被随机分发到分布式集群中的某个节点上。
    2. constant固定分片：写入数据会被分发到固定一个节点上。
    3. column value分片：按照某一列的值进行hash分片。
    4. 自定义表达式分片：指定任意合法表达式，根据表达式被计算后的值进行hash分片。

* 分布式写入:
    * 直接写分布式表的优点自然是可以让ClickHouse控制数据到分片的路由，缺点就多一些：
        数据是先写到一个分布式表的实例中并缓存起来，再逐渐分发到各个分片上去，实际是双写了数据（写入放大），浪费资源；
        数据写入默认是异步的，短时间内可能造成不一致；
        目标表中会产生较多的小parts，使merge（即compaction）过程压力增大。
    * 直接写本地表是同步操作，更快，parts的大小也比较合适，但是就要求应用层额外实现sharding和路由逻辑，如轮询或者随机等。
    * 在生产环境中总是推荐写本地表、读分布式表

### Replicated复制
工作在表级别，而不是集群级别.
目前支持复制表的引擎是ReplicatedMergeTree引擎族，它与平时最常用的MergeTree引擎族是正交的

## mergetree表引擎细分
[引擎选择](https://help.aliyun.com/document_detail/156340.html?spm=a2c4g.11186623.6.610.47ce6bcft384OY)

数据分区、存储有序、主键索引、稀疏索引、数据TTL
主键并不用于去重,主要作用是加速查询
由于MergeTree采用类似LSM tree的结构，很多存储层处理逻辑直到Compaction期间才会发生

### ReplacingMergeTree
根据主键对数据进行去重

虽然ReplacingMergeTree提供了主键去重的能力，但是仍旧有以下限制：
在没有彻底optimize之前，可能无法达到主键去重的效果，比如部分数据已经被去重，而另外一部分数据仍旧有主键重复。
在分布式场景下，相同primary key的数据可能被sharding到不同节点上，不同shard间可能无法去重。
optimize是后台动作，无法预测具体执行时间点。
手动执行optimize在海量数据场景下要消耗大量时间，无法满足业务即时查询的需求。
因此ReplacingMergeTree更多被用于确保数据最终被去重，而无法保证查询过程中主键不重复。

### CollapsingMergeTree
ClickHouse实现了CollapsingMergeTree来消除ReplacingMergeTree的功能限制.
该引擎要求在建表语句中指定一个标记列Sign，后台Compaction时会将主键相同、Sign相反的行进行折叠，也即删除。
Sign=1的行称之为状态行，Sign=-1的行称之为取消行
不在同一节点上的数据无法折叠
为了获得正确结果，业务层需要改写SQL，将count()、sum(col)分别改写为sum(Sign)、sum(col * Sign)。
CollapsingMergeTree虽然解决了主键相同的数据即时删除的问题，但是状态持续变化且多线程并行写入情况下，状态行与取消行位置可能乱序，导致无法正常折叠。

### VersionedCollapsingMergeTree
为了解决CollapsingMergeTree乱序写入情况下无法正常折叠问题，VersionedCollapsingMergeTree表引擎在建表语句中新增了一列Version，用于在乱序情况下记录状态行与取消行的对应关系。主键相同，且Version相同、Sign相反的行，在Compaction时会被删除。

### SummingMergeTree
支持对主键列进行预先聚合.会将主键相同的多行进行sum求和，然后使用一行数据取而代之，从而大幅度降低存储空间占用，提升聚合计算性能
ClickHouse只在后台Compaction时才会进行数据的预先聚合，而compaction的执行时机无法预测，所以可能存在部分数据已经被预先聚合、部分数据尚未被聚合的情况。因此，在执行聚合计算时，SQL中仍需要使用GROUP BY子句。
在预先聚合时，ClickHouse会对主键列之外的其他所有列进行预聚合。如果这些列是可聚合的（比如数值类型），则直接sum；如果不可聚合（比如String类型），则随机选择一个值。
通常建议将SummingMergeTree与MergeTree配合使用，使用MergeTree来存储具体明细，使用SummingMergeTree来存储预先聚合的结果加速查询。


### AggregatingMergeTree
AggregatingMergeTree也是预先聚合引擎的一种，用于提升聚合计算的性能。与SummingMergeTree的区别在于：SummingMergeTree对非主键列进行sum聚合，而AggregatingMergeTree则可以指定各种聚合函数。

## mergetree
[mergetree](https://clickhouse.tech/docs/zh/engines/table-engines/mergetree-family/mergetree/)
ORDER BY
    排序,可以是一组列的元组或任意的表达式
PARTITION BY
    分区
PRIMARY KEY
    主键
SAMPLE BY 
    用于抽样的表达式
TTL
    定行存储的持续时间并定义数据片段在硬盘和卷上的移动逻辑的规则列表, 表和列可分别设置TTL

长的主键会对插入性能和内存消耗有负面影响，但主键中额外的列并不影响 SELECT 查询的性能。
可以使用 ORDER BY tuple() 语法创建没有主键的表。

使用具有多个块的设备进行数据存储, 为热数据使用sdd,为冷数据使用普通硬盘. 
default 存储策略意味着只使用一个卷，这个卷只包含一个在 <path> 中定义的磁盘。表创建后，它的存储策略就不能改变了。


## 问题
* 分布式表,不建议直接写入分布式表, 而是写入本地表, 那么写入本地表的负载均衡策略如何保持一致?
分片规则用于写入路由, 不用于读路由. 无论分片规则是什么, 总是读所有分片,无论数据如何分布, 查询总是可以正常工作.

* 现在是asin + createtime 作为primarykey和orderby, 是否会有性能问题?

* 当前以database=company, table=站点, 为了让不同站点的数据更均衡的分布在各个分片上. 是否有必要这样做?

* TTL数据归档方案, 冷热数据存储方案.
