ClickHouse client 分析库客户端工具手册
从一条“只查昨天”的 SQL 把集群打满说起
故障现场往往很朴素:开发者想确认订单状态,在终端里连上 ClickHouse,复制了一条看似带时间条件的 SQL。几秒后终端仍在滚动读取行数,服务端监控却显示扫描字节和内存持续增长。真正的问题可能不是 SQL 写错,而是连错数据库、时间列没有参与数据跳过、查询没有资源上限,甚至导出的几千万行正在写满开发机磁盘。
clickhouse-client 是 Native 协议客户端。它把 SQL 和会话设置编码后发给服务端,持续接收进度、数据块、日志和异常;执行计划、数据读取、内存限制与权限判断都发生在服务端。客户端界面再轻,也不会把一次全表扫描变成廉价操作。因此第一次连接的目标不是“看到提示符”,而是同时证明四件事:连到了预期端点、TLS 身份可信、当前账号权限正确、查询有可观察且可终止的成本边界。
下面用 ch-dev.example.test、数据库 app 和只读用户 app_readonly 表示团队提供的开发端点。不要把示例域名替换成生产地址后直接试写操作。
安装后先辨认自己运行的是哪一个入口
ClickHouse 官方发布物是单一 clickhouse 可执行文件,其中包含 client 子命令;发行包还可能提供 clickhouse-client 入口。客户端不需要 JVM、Python 或单独安装的 Node.js。官方的 ClickHouse Client 文档 和 安装入口 应作为下载来源,团队环境则应固定四段版本号并校验制品来源,不把滚动的 latest 当作可复现基线。
Linux 或 macOS 上做一次个人开发验证,可以下载官方二进制:
curl https://clickhouse.com/ | sh
./clickhouse client --versionDebian / Ubuntu 团队机器更适合配置官方仓库后由包管理器安装:
sudo apt-get update
sudo apt-get install clickhouse-client
clickhouse-client --version新 CLI clickhousectl 能管理本地版本、服务和 ClickHouse Cloud 资源,安装入口是 curl https://clickhouse.com/cli | sh。它不是 clickhouse-client 的新名字:只需要连库查 SQL 时使用 clickhouse client;需要给本地 ClickHouse 项目固定版本和生命周期时再引入 clickhousectl。Cloud API key 管理的是云资源控制面,也不要与数据库用户名、密码混为一组凭证。
安装验收要同时记录客户端版本和服务端版本。客户端能启动,只证明本机二进制可执行:
clickhouse-client --version
clickhouse-client --host ch-dev.example.test --port 9440 --secure \
--user app_readonly --database app \
--query "SELECT version(), currentUser(), currentDatabase(), timezone() FORMAT Vertical"密码参数故意留空,客户端会交互询问。预期输出能看到服务端版本、app_readonly、app 和服务端时区。若输出中的用户或数据库不符,先停止后续查询;“能连上另一个库”不是成功。
一条连接命令里的每个字段都在改变风险
clickhouse-client \
--host ch-dev.example.test \
--port 9440 \
--secure \
--user app_readonly \
--database app \
--connect_timeout 5 \
--receive_timeout 30--host 决定 DNS、路由和证书主机名校验对象。通过负载均衡器或私网 DNS 连接时,记录逻辑服务名,不要把某个临时节点 IP 固化进项目脚本。--port 是 Native 协议端口,常见非 TLS 端口为 9000,TLS 端口为 9440;它们不是 HTTP 的 8123/8443。把 HTTP 端口交给 Native 客户端,常见证据是握手失败、连接被重置或读到无法解析的数据包。
--secure 开启 TLS。客户端在 9440 和 ClickHouse Cloud 域名上通常会自动启用,但显式写出更利于审计。私有 CA 场景应在客户端配置里设置 openSSL.client.caConfig,不要用接受无效证书的开关换取“先连上”。证书报主机名不匹配时,应修正访问域名、SNI 或证书 SAN,而不是关闭验证。
--user 与 --database 决定服务端身份和默认名称解析上下文。--connect_timeout 控制建立连接最多等多久,--receive_timeout 控制等待服务端数据的网络超时;后者不是查询总时长上限。查询执行上限应由 max_execution_time 等服务端设置约束,不能指望断开终端自动取消每个分布式子查询。
密码写在 --password xxx、连接 URI 或 shell 历史中,都会进入进程列表、终端录屏或审计采集。交互输入适合人工操作;自动化则从密钥管理器注入 CLICKHOUSE_PASSWORD,执行结束立即清理环境。配置优先级也必须可预测:命令行的 --user、--password、--host 或连接串会覆盖对应环境变量。
把连接模板和秘密拆开
ClickHouse Client 会按顺序寻找显式 --config、当前目录配置、XDG 配置目录、用户目录和系统目录。项目仓库只保存无密模板,例如 docs/tools/clickhouse-client.example.yaml:
connections_credentials:
connection:
- name: app-dev-readonly
hostname: ch-dev.example.test
port: 9440
secure: true
user: app_readonly
database: app
openSSL:
client:
caConfig: /etc/company-ca/clickhouse-ca.pem
history_file: /tmp/clickhouse-client-app-history
history_max_entries: 200使用命名连接时,调用者不必反复手写端口和 TLS:
CLICKHOUSE_PASSWORD="$(secret-tool lookup service clickhouse account app_readonly)" \
clickhouse-client --config "$HOME/.config/clickhouse/config.yaml" \
--connection app-dev-readonly \
--query "SELECT currentUser(), currentDatabase()"字段的治理含义比“少敲几个参数”更重要。hostname、port、secure 和 CA 路径属于可审查的连接契约;user 可以公开为角色名;密码只存在于个人密钥库或 CI secret。若将密码写入 YAML,文件至少应在仓库外、权限限制为当前用户,并进入凭证轮换清单。
官方客户端默认把交互历史写到 ~/.clickhouse-client-history,默认最大条目数很高。SQL 里常出现订单号、邮箱、手机号、临时 token 和内部表名,因此生产排障应使用受控 history_file,会话后按事件留存要求销毁,而不是把默认历史无限积累在个人目录。
正向实验:从身份确认走到可审计的小样本
先准备一个不会创建对象的查询文件 smoke.sql:
SELECT
version() AS server_version,
currentUser() AS login_user,
currentDatabase() AS current_db,
timezone() AS server_timezone
FORMAT Vertical;
SELECT
database,
name,
engine
FROM system.tables
WHERE database = currentDatabase()
ORDER BY name
LIMIT 20
FORMAT PrettyCompact;给本次排障一个可搜索的查询 ID,并限制服务端执行成本:
clickhouse-client --connection app-dev-readonly \
--query_id "ticket-CH-READ-001" \
--max_execution_time 10 \
--max_rows_to_read 1000000 \
--max_bytes_before_external_group_by 268435456 \
--queries-file smoke.sql正常证据不是固定的行数,而是两段查询都完成,身份字段符合预期,进度中的读取行数停止增长,退出码为 0。max_rows_to_read 在命中的读取阶段触发时会失败而不是悄悄截断,但分布式查询中远端读取由各远端节点检查,不能把入口会话里的一个数字误当成整个集群的精确总量上限;需要逐叶子节点约束时还要结合 max_rows_to_read_leaf 和服务端 profile 验证。LIMIT 20 只限制返回行数,不保证底层只读 20 行。
接着对业务表做可复制的小样本查询。先用 EXPLAIN indexes = 1 检查分区键、主键或跳数索引是否有机会排除数据,再执行同一过滤条件:
EXPLAIN indexes = 1
SELECT order_id, status, created_at
FROM app.orders
WHERE created_at >= now() - INTERVAL 1 HOUR
AND created_at < now()
ORDER BY created_at DESC
LIMIT 20;
SELECT order_id, status, created_at
FROM app.orders
WHERE created_at >= now() - INTERVAL 1 HOUR
AND created_at < now()
ORDER BY created_at DESC
LIMIT 20
FORMAT JSONEachRow;预期结果中,解释输出应出现与表结构相符的 Parts、Granules 或索引过滤信息;业务查询每行一个 JSON 对象,最多 20 行。若解释输出显示几乎所有 parts/granules 都会被读取,减小 LIMIT 仍然救不了扫描成本,应回到排序键、分区和谓词类型检查,而不是继续在客户端重试。
反向实验:让错误稳定留下证据
第一组反例故意把 Native 客户端连到 HTTP 端口:
clickhouse-client --host ch-dev.example.test --port 8123 \
--user app_readonly --connect_timeout 3 --query "SELECT 1"预期是非零退出码,并出现握手、意外数据包、连接关闭或超时类错误。此时 Test-NetConnection/nc 显示端口可达也不能证明协议正确。证据链应记录目标主机、端口、是否 TLS、客户端错误和服务端入口类型。
第二组反例验证只读权限真的在服务端生效:
CREATE TABLE app.permission_probe
(
id UInt8
)
ENGINE = Memory;用 app_readonly 执行后,预期收到权限不足或只读设置异常,且 SHOW TABLES FROM app LIKE 'permission_probe' 返回空。若表创建成功,不要自行删除后当作无事发生;先记录 query_id,通知库 owner 清理,并收回该账号的 DDL 权限。客户端配置里的“readonly”标签、脚本文件名或 UI 颜色都不是授权证据。
第三组反例用保护阈值暴露无界扫描:
clickhouse-client --connection app-dev-readonly \
--query_id "ticket-CH-SCAN-GUARD" \
--max_rows_to_read 1000 \
--query "SELECT count() FROM app.orders WHERE toString(created_at) != ''"对大表而言,预期是读取超过阈值后失败。toString(created_at) 还可能破坏原本可用的索引条件,这正好说明“返回一个 count”不等于查询便宜。修复方向是使用原始类型上的范围谓词,并由服务端 profile 固化资源上限。
用 query_id 从终端追到服务端
客户端为每条交互查询显示 query_id,批处理应主动设置能关联工单或流水线的 ID,但不要把邮箱、手机号、token 放进 ID。查询结束后,具备相应系统表权限的 owner 可以查看:
SELECT
type,
query_id,
user,
query_duration_ms,
read_rows,
read_bytes,
result_rows,
memory_usage,
exception_code,
exception
FROM system.query_log
WHERE query_id = 'ticket-CH-READ-001'
ORDER BY event_time_microseconds
FORMAT Vertical;成功查询通常产生 QueryStart 与 QueryFinish 事件;执行中失败会出现 ExceptionWhileProcessing,启动前失败可能只有 ExceptionBeforeStart。日志写入是异步的,立即查不到时可稍候再查;SYSTEM FLUSH LOGS 需要额外权限,不应赋给日常只读用户。system.query_log 保存查询文本、资源统计和异常,但不保存结果集,因此它既是性能证据,也是需要访问控制与保留策略的敏感日志。
ClickHouse Cloud 的该系统表在每个节点本地保存。查全服务副本要由有权限的 owner 使用 clusterAllReplicas('default', system.query_log) 聚合,单节点查不到不能直接判定查询未执行。分布式查询还要区分 query_id 与 initial_query_id,前者标识当前执行,后者把子查询归回入口请求。
正在运行的查询可先从 system.processes 找到,再由有 KILL QUERY 权限的受控角色终止:
SELECT query_id, user, elapsed, read_rows, read_bytes, memory_usage, query
FROM system.processes
WHERE query_id = 'ticket-CH-SCAN-GUARD';日常账号不应拥有任意终止他人查询的权限。自动化取消还要验证终止后 system.processes 不再出现该 ID,且服务器侧内存、线程和临时文件回到基线。
导出不是“把屏幕保存下来”
格式决定下游如何解释类型、空值、转义和表头。机器消费优先使用 JSONEachRow、CSVWithNames 或 Native,不要解析 PrettyCompact。先导出一个受限样本:
umask 077
clickhouse-client --connection app-dev-readonly \
--query_id "ticket-CH-EXPORT-SAMPLE" \
--max_result_rows 1000 \
--result_overflow_mode throw \
--query "
SELECT order_id, status, created_at
FROM app.orders
WHERE created_at >= now() - INTERVAL 1 HOUR
ORDER BY created_at DESC
LIMIT 1000
FORMAT CSVWithNames
" > ./orders-sample.csv
wc -l ./orders-sample.csv
head -n 2 ./orders-sample.csv预期文件权限只允许当前用户读取,第一行是列名,总行数不超过表头加 1000 条数据。shell 重定向发生在客户端机器;在工作站、跳板机或 CI runner 上执行,就会在那台机器产生数据副本。ClickHouse 的 INTO OUTFILE 同样把 SELECT 结果写到客户端侧文件,并支持按扩展名推断压缩格式;它不是让服务端节点在本地目录创建文件。使用前仍要确认执行客户端所在主机、目标绝对路径、覆盖或追加语义和磁盘配额。
受控导入只在隔离的开发库进行。先让数据库 owner 创建一次性表并授予精确 INSERT 权限,再检查文件头:
clickhouse-client --connection app-dev-writer \
--query_id "ticket-CH-IMPORT-SAMPLE" \
--query "INSERT INTO sandbox.orders_sample FORMAT CSVWithNames" \
< ./orders-sample.csv导入后的验收至少包含行数、空值、时间类型和重复键检查。失败时保留客户端退出码、异常码、行号附近的脱敏样本与 query_id;不要不断重跑一个非幂等导入。CSVWithNames 的列名不能替代服务端类型转换,列顺序、时区和空值表示仍要与目标表契约一致。
接入项目时让失败成为稳定接口
项目可以保存一个只读健康脚本,但连接秘密不进入仓库:
#!/usr/bin/env bash
set -euo pipefail
: "${CH_HOST:?missing CH_HOST}"
: "${CH_USER:=app_readonly}"
: "${CH_DATABASE:=app}"
: "${CH_CLIENT_CONFIG:?missing CH_CLIENT_CONFIG}"
query_id="ci-${CI_PIPELINE_ID:-local}-${CI_JOB_ID:-smoke}"
clickhouse-client \
--config "$CH_CLIENT_CONFIG" \
--host "$CH_HOST" \
--port "${CH_PORT:-9440}" \
--secure \
--user "$CH_USER" \
--database "$CH_DATABASE" \
--query_id "$query_id" \
--max_execution_time 10 \
--max_rows_to_read 100000 \
--query "
SELECT currentUser(), currentDatabase();
SELECT count() FROM system.tables WHERE database = currentDatabase();
"CI secret 注入 CLICKHOUSE_PASSWORD,CA 由受控文件挂载。流水线只打印用户、数据库、退出码和 query ID,不打印连接 URI、密码或业务结果。成功阈值是查询在预算内退出且身份正确;失败自动附上 query ID,让服务端 owner 能查日志,而不是把全量 SQL 与数据粘进工单。
服务端侧要用角色、settings profile 和 quota 固化约束。GRANT SELECT ON app.* 决定对象权限;readonly、max_execution_time、max_memory_usage、并发和 quota 决定会话能消耗多少资源。客户端传入的较小限制可用于自我保护,但不能允许客户端放大服务端上限。生产只读角色还应限制对系统表、字典、URL/file 表函数和跨集群能力的访问,避免“不能改业务表”却能导出更多数据。
清理和回滚要沿着数据副本反向走
一次临时排障结束后,按产生顺序的反方向处理:先终止仍运行的查询,再撤销临时授权和一次性用户,然后销毁导出文件、受控 history 与环境变量,最后由 owner 删除隔离库中的探针表。示例:
unset CLICKHOUSE_PASSWORD CLICKHOUSE_USER CLICKHOUSE_HOST
rm -f -- ./orders-sample.csv /tmp/clickhouse-client-app-history删除前先用绝对路径或 Resolve-Path/realpath 确认目标属于本次工作目录;不要对共享导出目录做递归清理。凭证若进入命令行、终端历史、日志或聊天,清文件不等于回滚,必须立即轮换凭证并检查审计记录。
配置回滚采用版本化无密模板:恢复上一版 host、端口、CA 和连接名,重新执行身份查询。不要通过关闭 TLS、放宽角色或切换 default 用户“恢复可用”。版本升级时保留旧客户端直到 smoke query、格式回归和证书链验证完成;若新客户端与旧服务端出现能力或设置差异,回滚客户端并记录服务端版本,而不是压掉版本不匹配警告。
架构师真正关心的是扫描、内存和数据出域
客户端成本很小,成本中心在服务端扫描字节、解压 CPU、聚合内存、网络结果集和客户端落盘。一次查询返回 20 行,仍可能扫描数十亿行;一次压缩良好的 Native 导出,解压后也可能写满 runner。容量治理因此要同时观察 read_rows、read_bytes、memory_usage、result_bytes、查询并发、外部聚合临时盘和客户端可用磁盘,而不是只统计 SQL 次数。
LIMIT 管结果,不管完整扫描;max_result_rows 管返回,不一定管读取,而且按数据块检查时最终结果可能略高于阈值;max_rows_to_read 与 max_bytes_to_read 在读取侧拒绝超额,但分布式查询还要验证远端和叶子节点的检查位置;max_execution_time 控时,但超时后的取消传播仍需观察。团队阈值应来自基线、SLO 和容量预算,不照抄示例数字。长期趋势要证明高峰并发下排队年龄受控、失败查询释放内存和临时文件、导出目录不会单调增长。
凭证治理至少区分三类主体:人工只读用户、受控导入用户、自动化服务账号。三者分别设置对象权限、资源 profile、quota、来源网络与有效期,不共享密码。高敏表可在服务端提供脱敏视图,只授权视图而非原表;字段白名单放在 SQL 模板中,截图和 CSV 进入数据分类、保留与销毁流程。
什么时候该换入口
临时诊断、可复制 SQL、批量格式转换和 query ID 追踪,clickhouse-client 最直接。团队需要图形化浏览、可视化或共享查询时,可以选 SQL Console 或受控 BI,但仍要继承服务端角色与资源限制。应用代码需要连接池、重试、参数绑定和可观测性时,应使用官方语言驱动;不要让应用通过 shell 启动客户端。
需要通过 HTTP 网关、标准代理或无 Native 端口网络访问时,使用 HTTP 接口比硬穿 9000/9440 更合理。需要在本地直接分析文件且不接服务端时,clickhouse local 更轻。需要管理本地版本或 ClickHouse Cloud 资源时使用 clickhousectl;Cloud API key 可以限定为只读或读写范围,但它属于组织和云资源控制面,不能默认继承数据库只读账号的边界,必须单独审批、收窄权限并轮换。
选型的停止条件很清楚:只要团队能证明连接身份、TLS、对象权限、扫描上限、query ID 证据、导出落点和清理责任,客户端就完成了它的工作。分片副本、MergeTree 设计、备份恢复和服务端容量属于数据库架构决策,不能靠给客户端再加一个参数补救。
