# ClikHouse!
## 什么是ClickHouse?[ ](https://clickhouse.tech/docs/zh/#shi-yao-shi-clickhouse)
> ClickHouse随笔
ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。
### OLAP场景的关键特征[ ](https://clickhouse.tech/docs/zh/#olapchang-jing-de-guan-jian-te-zheng)
- 绝大多数是读请求
- 数据以相当大的批次(> 1000行)更新,而不是单行更新;或者根本没有更新。
- 已添加到数据库的数据不能修改。
- 对于读取,从数据库中提取相当多的行,但只提取列的一小部分。
- 宽表,即每个表包含着大量的列
- 查询相对较少(通常每台服务器每秒查询数百次或更少)
- 对于简单查询,允许延迟大约50毫秒
- 列中的数据相对较小:数字和短字符串(例如,每个URL 60个字节)
- 处理单个查询时需要高吞吐量(每台服务器每秒可达数十亿行)
- 事务不是必须的
- 对数据一致性要求低
- 每个查询有一个大表。除了他以外,其他的都很小。
- 查询结果明显小于源数据。换句话说,数据经过过滤或聚合,因此结果适合于单个服务器的RAM中
***
以上是ClickHouse官方文档的说明,CK中的数据是真正意义上的**列式存储**。同样是**支持类SQL语法**查询存储在**分布式集群**上的大数据的OLAP引擎,它的查询速度要比Hive查询快数倍。
> 这点在公司中已经使用过ClickHouse的我深有体会,百亿级的count也不过是ms级别,百亿级的稍复杂的查询也可以在数秒内返回查询结果,同样的查询在Hive上可能要按小时跑MR任务(还要跟同事抢夺集群资源)。
## 数据存储[ ](https://clickhouse.tech/docs/zh/engines/table-engines/mergetree-family/mergetree/#mergetree-data-storage)
表由按主键排序的数据片段(DATA PART)组成。
当数据被插入到表中时,会创建多个数据片段并按主键的字典序排序。例如,主键是 `(CounterID, Date)` 时,片段中数据首先按 `CounterID` 排序,具有相同 `CounterID` 的部分按 `Date` 排序。
不同分区的数据会被分成不同的片段,ClickHouse 在后台合并数据片段以便更高效存储。不同分区的数据片段不会进行合并。合并机制并不保证具有相同主键的行全都合并到同一个数据片段中。
数据片段可以以 `Wide` 或 `Compact` 格式存储。在 `Wide` 格式下,每一列都会在文件系统中存储为单独的文件,在 `Compact` 格式下所有列都存储在一个文件中。`Compact` 格式可以提高插入量少插入频率频繁时的性能。
数据存储格式由 `min_bytes_for_wide_part` 和 `min_rows_for_wide_part` 表引擎参数控制。如果数据片段中的字节数或行数少于相应的设置值,数据片段会以 `Compact` 格式存储,否则会以 `Wide` 格式存储。
每个数据片段被逻辑的分割成颗粒(granules)。颗粒是 ClickHouse 中进行数据查询时的最小不可分割数据集。ClickHouse 不会对行或值进行拆分,所以每个颗粒总是包含整数个行。每个颗粒的第一行通过该行的主键值进行标记,ClickHouse 会为每个数据片段创建一个索引文件来存储这些标记。对于每列,无论它是否包含在主键当中,ClickHouse 都会存储类似标记。这些标记让你可以在列文件中直接找到数据。
颗粒的大小通过表引擎参数 `index_granularity` 和 `index_granularity_bytes` 控制。取决于行的大小,颗粒的行数的在 `[1, index_granularity]` 范围中。如果单行的大小超过了 `index_granularity_bytes` 设置的值,那么一个颗粒的大小会超过 `index_granularity_bytes`。在这种情况下,颗粒的大小等于该行的大小。
## 主键与排序键
首先摆出官方示例如下,该实例配置为:
```sql
ORDER BY (CounterID, Date)
```
```yaml
全部数据 : [-------------------------------------------------------------------------]
CounterID: [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll]
Date: [1111111222222233331233211111222222333211111112122222223111112223311122333]
标记: | | | | | | | | | | |
a,1 a,2 a,3 b,3 e,2 e,3 g,1 h,2 i,1 i,3 l,3
标记号: 0 1 2 3 4 5 6 7 8 9 10
```
主键与排序键不同的示例,该实例配置为:
```sql
ORDER BY (CounterID, Date, price), PRIMARY KEY (CounterId, Date)
```
```yaml
全部数据 : [-------------------------------------------------------------------------]
CounterID: [aaaaaaaaaaaaaaaaaabbbbcdeeeeeeeeeeeeefgggggggghhhhhhhhhiiiiiiiiikllllllll]
Date: [1111111222222233331233211111222222333211111112122222223111112223311122333]
price: [1112333111122212331112111122111222113112333332222222331223332233311213233]
标记: | | | | | | | | | | |
a,1 a,2 a,3 b,3 e,2 e,3 g,1 h,2 i,1 i,3 l,3
标记号: 0 1 2 3 4 5 6 7 8 9 10
```
官方示例中(CounterID, Date)为排序键,同时也是主键。在某些情况下可以指定与排序键不同的主键,主键是查询时候数据库scan数据的范围,排序键时插入时数据的存储顺序,可以看到,主键须是排序键的**前缀**(官网也有提到)。
- 在MergeTree引擎下,主键与排序键没有不同。
- 在[CollapsingMergeTree](https://clickhouse.tech/docs/zh/engines/table-engines/mergetree-family/collapsingmergetree/#table_engine-collapsingmergetree) 和 [SummingMergeTree](https://clickhouse.tech/docs/zh/engines/table-engines/mergetree-family/summingmergetree/) 引擎里进行数据合并时会提供额外的处理逻辑。在这种情况下,指定与主键不同的 *排序键* 是有意义的。
> 可以没有主键,创建表时指定order by tuple()数据会直接以插入顺序存储。
#### 实际使用与问题
ClickHouse特性
- SIMD
- 列式存储
- LZ4压缩
- 丰富函数库 Geo,bitmap, 精准去重
- 运维麻烦
- join慢
问题
- 实际集群出现过高并发导致的死锁问题,Clickhouse并发查询慢,可能导致死锁连接不上CK节点。解决:前端页面加查询缓存,查询并发数由400降为100;
ClickHouse随笔