问题描述
1. 背景
需要对clickhouse里的日志表进行导出,原有的API接口是通过REST API从clickhouse查询并通过HTTP传输,虽然给clickhouse的HTTP接口启用了压缩,但效率和资源利用仍然很低。要等待很长时间,页面可能会超时。原有的API值保留了很少的数据,不存在这个问题。但使用clickhouse后,数据保存的非常大,所以请求时间就很长了。
2. 目标
所以接下来的目标就是很明确了,因为时间长,要使用异步。因为数据量大,所以要更换更高效的导出方式。显然直接用数据库本身的导出方式,直接导出文件更高效。因为要防止用户反复请求统一个参数,因此任务要有管理。首先就是去重,防止重复下发同样的导出任务,第二个就是要限制并发或同时下载的任务数,防止吃太多CPU资源。因为clickhouse的查询通常都会消耗大量的硬盘、CPU、内存资源,尤其是CPU。
目标:异步,利用数据库的导出功能,任务要有数量和状态管理。
3. 方案
根据以往的经验,celery就可以做异步任务。所以直接选定了celery,clickhouse-client的导出。而任务要有状态管理,可以在redis里管理状态。
方案:celery做异步,clickhouse-client直接导出压缩文件。对任务在redis管理。
接下来,就是如何实现。首先从celery执行一个简单任务开始。
一、clickhouse表的导出
clickhouse可以直接导出到压缩文件(见引用),格式也可以支持多种多样。我们需要的压缩的csv格式,因此只需要提供csv.gz的文件名后缀即可。
SELECT *
FROM nyc_taxi
INTO OUTFILE 'taxi_rides.tsv.gz'
根据不同表、不同起止时间查询,可以得出SQL模板:
select * from {table_name} where {time_field} >= {start_time} and {time_field} < {end_time}
INTO OUTFILE '{filename}'
format CSVWithNames
SETTINGS max_threads = {max_threads}
这里有两个问题需要解决:
1. clickhouse的HTTP客户端不能使用导出的INTO OUTFILE语句。
- This functionality is available in the command-line client and clickhouse-local. Thus a query sent via HTTP interface will fail.
所以只能封装一个使用clickhouse-client的命令调用导出,celery的worker也需要和clickhouse运行在一起。
def execute_query_local(query, database):
# 启动子进程执行命令:
# clickhouse-client --query query
ret = subprocess.check_call(["clickhouse-client","-d", database, "--query", query], shell=False)
return ret
2. 导出数据的CPU消耗很大,因此要限制一下线程数,以降低消耗。我使用了10个线程。时间长是可以忍受的,但崩溃是无法忍受的。
SETTINGS max_threads = 10
二、celery异步任务
1. 最小可用
首先,一个celery的最小可用任务,这没有什么难度。可以借助ch

最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



