clickhouse 使用笔记
clickhouse 安装
安装的时候,最开始是在 Mac 系统上安装的,后来在 Windows 上又重新使用 Docker 和 WSL 进行复制。
在 Windows 上使用 Docker 有一些小问题需要注意。
Mac 系统
准备使用 orbstack 安装 clickhouse 的 docker 镜像,安装命令如下:
docker run -d --name some-clickhouse-server --ulimit nofile=262144:262144 clickhouse/clickhouse-server
连接客户端的命令为:
docker run -it --rm --link some-clickhouse-server:clickhouse-server --entrypoint clickhouse-client clickhouse/clickhouse-server --host clickhouse-server
# OR
docker exec -it some-clickhouse-server clickhouse-client
启停容器:
docker stop some-clickhouse-server
docker rm some-clickhouse-server
登录 clickhouse
clickhouse-client -u default -h localhost
Windows 系统
安装 docker desktop ,然后配置对应的国内镜像:
{
...,
"registry-mirrors": [
"https://registry.docker-cn.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
]
}
拉取对应的镜像后,需要要启用镜像容器时,在启动时需要填写对应有 Optional settings ,其中 Ports 需要指定对应的端口的映射方案。即 8123:8123 9000:9000 9009:9009 。
数据库终端
数据库客户端使用 dbeaver ,对应的驱动名为: com.clickhouse.jdbc.ClickHouseDriver
配置好 URL 模板后,使用 orbstack 地址进行 http 访问。
注意使用 localhost 的话需要调整对应的端口号。http 使用 8123 端口,本地 server 使用 9000 端口。
数据库的批量写入
使用 Python 下载 akshare 的数据保存到 clickhouse 时,查询了一上,一般建议是使用 pandahouse
或者 clickhouse_driver
。翻看了一下对应的项目源码,已经很久没做更新了。在阅读 clickhouse 的官方网站时,发现建议使用 clickhouse_connect
进行数据库的操作。
这里对数据时行计算后,将相同数量的数据写入到表中。
ph.to_clickhouse(df, tablename, index=False, connection=connection)
441 ms ± 65.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
client.insert_df(table_name, df, DB)
56.8 ms ± 3.53 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
大概写了一下数据库的写入,测试一下时间, client.insert()
大概在 60ms 左右,而使用 pandahouse.to_clickhouse()
则需要 500ms ,数量一共大概在 7703 rows x 39 columns 。
最终决定使用 clickhouse_connect 实现数据的写入和清理操作。
数据库查询
因为写入过程还需要再做一下测试,使用 client.query_df()
对数据表进行查询,语句如下:
sql = "SELECT * FROM db.table"
parameters = {}
mm = client.query_df(sql)
查询 63W Rows × 45 columns 数据的时间如下:
2.17 s ± 329 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
表引擎 ReplacingMergeTree
作用
MergeTree 支持主键,但主键是用来缩小查询范围的,不具备唯一性约束,而 ReplacingMergeTree 相对 MergeTree 增加了去重功能,它仅会在合并分区时,删除重复的数据,且其在写入时,不会因为插入相同主键的数据报错。
ver 参数
通过为表引擎增加 ver 参数来添加去重算法,如果没有设置该参数,合并时保留分组内的最后一条数据。如果指定了该参数,则保留 ver 字段聚会最大的那一行。
CREATE TABLE TEST
{
id String,
verstion DateTime
}
ENGINE = ReplacingMergeTree(version)
PARTITION BY toYYYYMM(version)
PRIMARY KEY ID
ORDER BY (ID)
插入重复数据后,可以使用 optimize table test FINAL;
命令强制者分区合并。
特点
- 使用 ORDER BY 排序键,作为判断数据是否重复的唯一数据
- 只有在合并分区时,才会触发数据的去重逻辑
- 删除重复数据,是以数据分区为单位,同一个数据分区的重复数据才会被删除,不同数据分区的重复数据仍会保留
- 去重策略:
- 若指定了 ver 参数,则会保留重复数据中, ver 字段最大的那一行
- 若未指定 ver 参数,则会保留重复数据中对最末的那一行数据。
物化视图
物化视图是对应一份持久化的存储,可以是物理表的一份数据子集拷贝,也可以是多表 JOIN 或都预聚合的一个结果或子集。 Clickhouse 的物化视图实现更像是触发器,如果 View 中预告定义了聚合函数,那么聚合函数仅适用于新插入的数据。对源表数据的更改都不会更改物化视图。
对于大型表,提前计算统计结果更快也更节省资源。可以将结果放在单独的物化视图中。
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT ...
- POPULATE
- 将表中现有数据存储到视图中,否则只会写入创建视图之后的数据。
Projection 功能
利用一组列的组合,可以按照与万年青不同的排序存储,并且支持聚合函数的查询。其特点如下:
- part-level 存储
- 相比普通视图是一张独立的表, Projection 物化的数据就保存在万年青的分区目录中,支持明细数据的普通 Projection 和预聚合 Projection
- 无感使用,自动命中
- 可以对一张 MergeTree 创建多个 Projection ,当执行 select 语句时,能根据查询范围,自动匹配最优的 Projection 提供查询回事,没命中则查询底表
- 数据同源
- 数据保存在原表分区,所以数据的更新、合并都是同源的,也就不会出现不致的情况
创建 Projection 的语句如下:
alter table table_name add PROJECTION projection_name
(
select x,y
order by z
)
所有数据需要在创建 Projection 之后,才会自动物化,对于历史数据,需要手动触发物化。
alter table table_name MATERIALIZE PROJECTION projection_name;
执行相同查询时,可以利用参数增加效率优化:
set allow_experimental_projection_optimization = 1;
除了查询明细数据的优化外,还可以做聚合查询:
alter table table_name add PROJECTION projection_name
(
select
id,
count(*),
avg()
group by id
)
需要删除时则使用 alter table table_name drop projection projection_name
进行删除。
PROJECTION 匹配原则
- 设置了 SET allow_experimental_projection_optimization = 1
- 返回的数据行小于基表总数
- 查询覆盖的分区 part 超过一半
- Where 必须是 PROJECTION 定义中 GROUP BY 的子集
- GROUP BY 必须是 PROJECTION 定义中 GROUP BY 的子集
- SELECT 必须是 PROJECTION 定义中 SELECT 的子集
- 匹配多个 PROJECTION 的时候,选取读取 part 最少的
如果你不知道查询是否匹配了 PROJECTION ,有两种方式可以校验:
使用 explain
查询执行计划
explain
select id from table_name where cond=cond1
显示查询计划中, with 0 projection projection_name
即表示命令 PROJECTION 。