



























💡 记住:ClickHouse 是为 大批量、低频写入 设计的,不是消息队列。写入频率越低、批次越大,系统越稳定。
ClickHouse的存储结构为:/data/clickhouse/data/simulation/solarforecast_raw_data/{parts}
朱凯 著 ISBN:978-7-111-65490-2 本书纸版由机械工业出版社于2020年出版。本书基于 ClickHouse 19.17.4.11 版本讲解。
※,1.6.1 顺理成章的MySQL时期
原文:(针对mysql)市面上一块典型的7200转SATA磁盘的IOPS(每秒能处理的请求数)仅为100左右,也就是说每秒只能执行100次随机读取。假设一次随机读取返回10行数据,那么查询100000行记录则需要至少100秒,这种响应时间显然是不可接受的。
问题:假设一次随机读取返回10行数据是否合理?为什么不假设读取100行,1000行呢?
这是一个非常敏锐的提问!你抓住了原文论证中的一个关键简化假设。我们来深入分析:
✅ 结论先行:
“一次随机读取返回10行数据”这个假设在特定上下文中是合理的,但它是一个有意为之的、偏保守(甚至略显悲观)的估算,目的是突出传统磁盘在 随机 I/O 场景下的致命瓶颈。
它不是对数据库实际工作方式的精确描述,而是一种用于说明性能问题的教学性简化模型。
一、为什么说“10行”在技术上是可能的?
1. 磁盘 I/O 的基本单位是“扇区”或“块”
- 传统机械硬盘(HDD)的最小物理读取单位是 512 字节(旧)或 4KB(先进格式化,AF)。
- 操作系统和文件系统通常以 4KB 页(Page)为单位进行 I/O。
- 数据库(如 MySQL InnoDB)默认的数据页大小也是 16KB(可配置为 4KB、8KB、32KB、64KB)。
2. 一行数据有多大?
这决定了一页能存多少行。举例:
场景 单行大小估算 16KB 页可存行数 简单用户表(id + name) ~100 字节 ≈ 160 行 包含文本/JSON 的日志表 ~1KB ≈ 16 行 宽表(上百字段) ~2KB+ ≈ 8 行或更少 👉 所以,“10行/页”对应的是单行约 1.6KB 的中等偏大记录,这在实际业务中(如包含描述、地址、少量 JSON)完全合理。
✅ 因此,从存储密度角度看,“一次 I/O 读 10 行”并非随意捏造,而是基于典型数据模型的合理估计。
二、那为什么不假设读 100 行、1000 行呢?
原因有三,核心在于 “随机读取” vs “顺序读取” 的本质区别:
❌ 误区:认为“一次 I/O 可以任意多读”
- IOPS = Input/Output Operations Per Second,这里的 “Operation” 指的是一次寻址+读取动作。
- 7200 转 HDD 的瓶颈不在传输速度,而在“寻道时间”+“旋转延迟”:
- 平均寻道时间:~8.5 ms
- 平均旋转延迟:~4.17 ms(7200 RPM)
- 总随机访问延迟 ≈ 12.7 ms → 理论最大 IOPS ≈ 1000 / 12.7 ≈ 79(接近常说的 100)
🔑 关键点:无论你这次读 1 字节还是 1MB,只要它是随机位置,都要付出 ~12.7ms 的延迟成本!
📌 所以:
- 如果你要读 分散在不同位置的 1000 行,即使每行只有 100 字节,也需要 1000 次随机 I/O → 1000 / 100 = 10 秒。
- 但如果这 1000 行物理上连续存储(顺序读),那么只需 1 次寻址 + 连续读取 → 传输时间可能仅需几毫秒。
- 【自注】从上面两段可以得出:一次IO可以读取任意多的数据,关键在于需要的数据是否一次IO就可以读取到(即是否是连续存储的);对于数据无序的情况,一次IO只能获取到其中一部分数据,下一部分数据需要再来一次IO寻址+数据读取。
💡 原文的潜台词是:
“假设你要做的是全表扫描但数据无序(最坏情况),或者执行一个无法使用索引的查询,导致需要大量随机访问不同的数据页。”在这种场景下,每次 I/O 只能有效获取一页的数据(比如 10 行),而不是批量读取上千行。
三、数据库的实际行为:为什么通常比“10行/IOPS”好得多?
原文故意忽略了数据库的优化机制,以凸显 HDD 的极限瓶颈。现实中,数据库会通过以下方式避免陷入这种最坏情况:
1. 顺序 I/O 优先
- 全表扫描时,数据库会按物理存储顺序读取数据页 → 变成顺序读,吞吐可达 100~200 MB/s(远高于 100 IOPS 的限制)。
- 例如:16KB 页 × 1000 页 = 16MB,顺序读可能只需 0.1 秒。
2. 缓冲池(Buffer Pool)
- 热点数据常驻内存(如 MySQL 的
innodb_buffer_pool),根本不需要磁盘 I/O。3. 预读(Read-Ahead)
- 数据库预测你会读后续页面,提前批量加载到内存。
4. 索引覆盖 & 聚簇索引
- 利用 B+ 树索引,将随机访问转化为少量 I/O(如 3 次)即可定位大量数据。
✅ 所以,100,000 行查询在真实系统中通常不会花 100 秒——除非你的查询写得极差(如
SELECT *且无索引 + 数据全冷 + 强制随机读)。
四、原文为何要这样假设?—— 教学目的
作者的真正意图是:
- 量化 HDD 的随机 I/O 瓶颈:用具体数字(100 IOPS)说明为什么不能依赖 HDD 做高并发随机查询。
- 引出 SSD 的必要性:SSD 的随机 IOPS 可达 数万至数十万,彻底解决此问题。
- 强调架构设计的重要性:必须通过索引、缓存、数据局部性来避免随机 I/O。
如果假设“一次读 1000 行”,就会掩盖 HDD 在非顺序访问场景下的致命缺陷,削弱论证力度。
✅ 总结
问题 回答 “10行/次”合理吗? 合理,对应单行 ~1.6KB 的典型业务数据,在 16KB 页中可存约 10 行。 为什么不假设更多? 因为随机 I/O 的成本在“寻址”而非“传输量”。读 1 行和读 1000 行(若分散)都需要多次寻址。 现实会这么慢吗? 通常不会,因数据库会通过顺序读、缓存、索引等避免随机 I/O。但最坏情况下确实可能。 作者为何这样写? 教学简化,旨在突出传统 HDD 在 OLTP 场景下的性能天花板,推动技术选型(如用 SSD 或优化查询)。 📌 记住:性能分析中,“最坏情况假设”常用于揭示系统瓶颈,不代表日常表现。真正的工程优化,就是让系统永远不要走到这个最坏情况。
官方安装文档:https://clickhouse.com/docs/install
在192.168.67.27 Ubuntu20.04上使用apt安装
也可以从ClickHouse官方仓库下载deb包手动安装,注意页面最下面有个next按钮可以查找更多版本的deb包。需下载三个deb包:
- https://packages.clickhouse.com/deb/pool/main/c/clickhouse/clickhouse-common-static_26.2.3.2_amd64.deb
- https://packages.clickhouse.com/deb/pool/main/c/clickhouse/clickhouse-client_26.2.3.2_amd64.deb
- https://packages.clickhouse.com/deb/pool/main/c/clickhouse/clickhouse-server_26.2.3.2_amd64.deb
- apt install /path/to/*.deb OR dpkg -i install *.deb
1. 设置源
# Install prerequisite packages
sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
# Download the ClickHouse GPG key and store it in the keyring
curl -fsSL 'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' | sudo gpg --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg
# Get the system architecture
ARCH=$(dpkg --print-architecture)
# Add the ClickHouse repository to apt sources
echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg arch=${ARCH}] https://packages.clickhouse.com/deb stable main" | sudo tee /etc/apt/sources.list.d/clickhouse.list
# Update apt package lists
sudo apt-get update
2 查看可用版本
(base) [root@sungrow27 etc]$apt-cache madison clickhouse-server
clickhouse-server | 26.2.3.2 | https://packages.clickhouse.com/deb stable/main amd64 Packages
clickhouse-server | 26.2.2.9 | https://packages.clickhouse.com/deb stable/main amd64 Packages
clickhouse-server | 26.2.1.1139 | https://packages.clickhouse.com/deb stable/main amd64 Packages
...
3 安装最新版本。🔒 🔑 👮♂️ 安装过程中需要设置一个密码,本次设置为:Clickhouse@2026
(base) [root@sungrow27 etc]$apt install clickhouse-server=26.2.3.2 clickhouse-client=26.2.3.2
正在读取软件包列表... 完成
正在分析软件包的依赖关系树
正在读取状态信息... 完成
将会同时安装下列软件:
clickhouse-common-static
建议安装:
clickhouse-common-static-dbg
下列【新】软件包将被安装:
clickhouse-client clickhouse-common-static clickhouse-server
升级了 0 个软件包,新安装了 3 个软件包,要卸载 0 个软件包,有 71 个软件包未被升级。
需要下载 223 MB 的归档。
解压缩后会消耗 759 MB 的额外空间。
您希望继续执行吗? [Y/n] y
获取:1 https://packages.clickhouse.com/deb stable/main amd64 clickhouse-common-static amd64 26.2.3.2 [223 MB]
获取:2 https://packages.clickhouse.com/deb stable/main amd64 clickhouse-client amd64 26.2.3.2 [62.2 kB]
获取:3 https://packages.clickhouse.com/deb stable/main amd64 clickhouse-server amd64 26.2.3.2 [92.0 kB]
已下载 215 MB,耗时 33秒 (6,461 kB/s)
正在选中未选择的软件包 clickhouse-common-static。
dpkg: 警告: 无法找到软件包 squid-langpack 的文件名列表文件,现假定该软件包目前没有任何文件被安装在系统里。
(正在读取数据库 ... 系统当前共安装有 193762 个文件和目录。)
准备解压 .../clickhouse-common-static_26.2.3.2_amd64.deb ...
正在解压 clickhouse-common-static (26.2.3.2) ...
正在选中未选择的软件包 clickhouse-client。
准备解压 .../clickhouse-client_26.2.3.2_amd64.deb ...
正在解压 clickhouse-client (26.2.3.2) ...
正在选中未选择的软件包 clickhouse-server。
准备解压 .../clickhouse-server_26.2.3.2_amd64.deb ...
正在解压 clickhouse-server (26.2.3.2) ...
正在设置 clickhouse-common-static (26.2.3.2) ...
正在设置 clickhouse-server (26.2.3.2) ...
ClickHouse binary is already located at /usr/bin/clickhouse
Symlink /usr/bin/clickhouse-server already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-server to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-client already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-client to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-local already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-local to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-benchmark already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-benchmark to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-obfuscator already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-obfuscator to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-compressor already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-compressor to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-format already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-format to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-extract-from-config already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-extract-from-config to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-keeper already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-keeper to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-keeper-converter already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-keeper-converter to /usr/bin/clickhouse.
Symlink /usr/bin/clickhouse-chdig already exists but it points to /clickhouse. Will replace the old symlink to /usr/bin/clickhouse.
Creating symlink /usr/bin/clickhouse-chdig to /usr/bin/clickhouse.
Symlink /usr/bin/chdig already exists. Will keep it.
Symlink /usr/bin/ch already exists. Will keep it.
Symlink /usr/bin/chl already exists. Will keep it.
Symlink /usr/bin/chc already exists. Will keep it.
Creating clickhouse group if it does not exist.
groupadd -r clickhouse
groupadd:“clickhouse”组已存在
Creating clickhouse user if it does not exist.
useradd -r --shell /bin/false --home-dir /nonexistent -g clickhouse clickhouse
useradd:用户“clickhouse”已存在
Will set ulimits for clickhouse user in /etc/security/limits.d/clickhouse.conf.
Creating config directory /etc/clickhouse-server/config.d that is used for tweaks of main server configuration.
Creating config directory /etc/clickhouse-server/users.d that is used for tweaks of users configuration.
Config file /etc/clickhouse-server/config.xml already exists, will keep it and extract path info from it.
/etc/clickhouse-server/config.xml has /var/lib/clickhouse/ as data path.
/etc/clickhouse-server/config.xml has /var/log/clickhouse-server/ as log path.
Users config file /etc/clickhouse-server/users.xml already exists, will keep it and extract users info from it.
Log directory /var/log/clickhouse-server/ already exists.
Data directory /var/lib/clickhouse/ already exists.
Pid directory /var/run/clickhouse-server already exists.
chown -R clickhouse:clickhouse '/var/log/clickhouse-server/'
chown -R clickhouse:clickhouse '/var/run/clickhouse-server'
chown clickhouse:clickhouse '/var/lib/clickhouse/'
Set up the password for the default user:
Password for the default user is saved in file /etc/clickhouse-server/users.d/default-password.xml.
Setting capabilities for clickhouse binary. This is optional.
chown -R clickhouse:clickhouse '/etc/clickhouse-server'
ClickHouse has been successfully installed.
Start clickhouse-server with:
sudo clickhouse start
Start clickhouse-client with:
clickhouse-client --password
Synchronizing state of clickhouse-server.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable clickhouse-server
Created symlink /etc/systemd/system/multi-user.target.wants/clickhouse-server.service → /lib/systemd/system/clickhouse-server.service.
正在设置 clickhouse-client (26.2.3.2) ...
正在处理用于 systemd (245.4-4ubuntu3.22) 的触发器 ...
4 修改配置文件 /etc/clickhouse-server/config.xml
chown -R clickhouse:clickhouse /data1/clickhouse5. 启动clickhouse-server
(base) [root@sungrow27 clickhouse-server]$systemctl start clickhouse-server.service
(base) [root@sungrow27 clickhouse-server]$systemctl status clickhouse-server.service
● clickhouse-server.service - ClickHouse Server (analytic DBMS for big data)
Loaded: loaded (/lib/systemd/system/clickhouse-server.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2026-03-05 15:33:12 CST; 1s ago
Main PID: 2367228 (clickhouse-serv)
Tasks: 689 (limit: 154102)
Memory: 199.0M
CGroup: /system.slice/clickhouse-server.service
├─2367221 clickhouse-watchdog --config=/etc/clickhouse-server/config.xml --pid-file=/run/clickhouse-server/clickhouse-server.pid
└─2367228 /usr/bin/clickhouse-server --config=/etc/clickhouse-server/config.xml --pid-file=/run/clickhouse-server/clickhouse-server.pid
3月 05 15:33:11 sungrow27 systemd[1]: Starting ClickHouse Server (analytic DBMS for big data)...
3月 05 15:33:11 sungrow27 clickhouse-server[2367221]: Processing configuration file '/etc/clickhouse-server/config.xml'.
3月 05 15:33:11 sungrow27 clickhouse-server[2367221]: Logging trace to /var/log/clickhouse-server/clickhouse-server.log
3月 05 15:33:11 sungrow27 clickhouse-server[2367221]: Logging errors to /var/log/clickhouse-server/clickhouse-server.err.log
3月 05 15:33:11 sungrow27 systemd[1]: clickhouse-server.service: Supervising process 2367228 which is not our child. We'll most likely not notice when it exits.
3月 05 15:33:12 sungrow27 systemd[1]: Started ClickHouse Server (analytic DBMS for big data).
6 客户端连接
(base) [root@sungrow27 clickhouse-server]$clickhouse-client
ClickHouse client version 26.2.3.2 (official build).
Connecting to localhost:9000 as user default.
Password for user (default): [手动输入Clickhouse@2026]
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 26.2.3.
sungrow27 :)
※,3.2 客户端访问
ClickHouse的底层访问接口支持TCP和HTTP两种协议,其中,TCP协议拥有更好的性能,其默认端口为9000,主要用于集群间的内部通信及CLI客户端;而HTTP协议则拥有更好的兼容性,可以通过REST服务的形式被广泛用于JAVA、Python等编程语言的客户端,其默认端口为8123。通常而言,并不建议用户直接使用底层接口访问ClickHouse,更为推荐的方式是通过CLI和JDBC这些封装接口,因为它们更加简单易用。
ClickHouse的jar包有两类:
※,6.4.2 二级(跳数)索引的类型
★,关于ngrambf_v1(布隆过滤器)
问题:
你回答中提到:
对于表中的每一个 Granule(数据块),ClickHouse 会:
遍历该块中 message 列的所有字符串。
对每个字符串,提取所有的 N-Gram(N 由参数指定)。
将所有这些 N-Gram 全部添加到一个布隆过滤器(Bloom Filter)中。
将这个 BF 作为该 Granule 的索引元数据保存起来。
📦 最终,每个 Granule 对应一个小小的 BF。
我有点不理解,请问:ClickHouse遍历该块中 message 列的所有字符串,对每个字符串,提取所有的 N-Gram,将所有这些 N-Gram 全部添加到一个布隆过滤器(Bloom Filter)中,如果有一亿条数据,这个布隆过滤器为什么还是一个小小的BF?不应该很大吗?通义千问Qwen3-max回答:
这是一个极其关键且深刻的问题!你准确地抓住了布隆过滤器(Bloom Filter, BF)最反直觉、也最精妙的地方:
为什么能用“很小的空间”表示“海量元素”的存在性?
答案在于:布隆过滤器不存储元素本身,只存储元素的“指纹”(哈希值),并且允许多个元素共享同一个“指纹位”。
下面我将一步步拆解这个“魔法”。
1. 布隆过滤器的核心结构:一个位数组
想象一个非常长的开关面板,上面有
M个灯泡(位),初始状态全是 关(0)。
M是固定的,比如你创建索引时指定的512。- 这个面板就是整个布隆过滤器,它的大小就是
M位 =M/8字节。
- 例如
M=512→ 面板大小 = 64 字节。- 无论你要存 1 个元素还是 1 亿个元素,这个面板的物理大小永远不变!
2. 如何“添加”一个元素(N-Gram)?
假设我们要添加一个 N-Gram,比如
"hel"。
计算多个哈希值:
- 使用
H个不同的哈希函数(比如H=2)。hash1("hel") = 123hash2("hel") = 456将结果映射到位数组索引:
- 因为面板只有
M=512个位置,所以要对结果取模。index1 = 123 % 512 = 123index2 = 456 % 512 = 456打开对应的灯泡:
- 把第
123号和第456号灯泡 打开(设为 1)。✅ 现在,
"hel"就被“添加”到 BF 中了。注意,我们并没有存储"hel"这个字符串!
3. 添加一亿个 N-Gram 会发生什么?
现在,我们开始处理一个 Granule(比如 8192 行数据)。每行日志可能产生 10~100 个 N-Gram。那么一个 Granule 总共可能有 几万到几十万个 N-Gram。
当我们把这几十万个 N-Gram 逐个“添加”到同一个 BF(
M=512)中时:
- 每个 N-Gram 都会通过哈希函数,试图打开面板上的 2 个(
H=2)灯泡。- 关键点来了:不同的 N-Gram 极有可能会打开相同的灯泡!
- 例如,
"abc"的hash1结果可能是123。"xyz"的hash2结果也可能是123。- 它们都会去打开第
123号灯泡。最终结果:
- 面板上的灯泡会从全
0,逐渐变成很多1。- 但是,面板的总大小始终是 512 位(64字节)!
- 它不会因为元素增多而变大,它只是变得“更亮”(
1的比例更高)。📦 这就是为什么它“小小”的原因:它的容量是固定的,靠的是信息压缩和概率,而不是存储。
4. 查询时如何工作?(以及误报的来源)
现在,我们想查询 N-Gram
"out"是否可能存在。
计算哈希:
hash1("out") = 200hash2("out") = 300检查灯泡:
- 查看第
200号和300号灯泡是否都是1。- 情况 A:如果任何一个是
0(比如300是0)。
- 结论:
"out"绝对没有被添加过! ✅ 100% 准确- 情况 B:如果两个都是
1。
- 结论:
"out"可能存在。- 但是:这两个灯泡也可能是被其他 N-Gram(如
"pqr","stu")打开的!- 这就是“误报(False Positive)”的来源。❌
误报率可控:
- 如果你把面板做得更大(增大
M),或者用更多的哈希函数(增大H),就能降低不同元素“撞”到同一个灯泡的概率,从而降低误报率。- 这就是为什么创建索引时要调整
M和H参数。
5. 回到你的问题:一亿条数据 vs 一个小 BF
- 误解澄清:BF 不是对整张表建一个,而是对每一个 Granule(数据块) 建一个。
- 一个 Granule 默认只有 8192 行 数据。
- 即使这 8192 行每行产生 100 个 N-Gram,总共也就 80 万个 N-Gram。
- 一个
M=4096(512字节) 的 BF 来存储 80 万个 N-Gram 的指纹,虽然会有一定误报,但空间效率极高。总结一下尺寸:
项目 大小 一个 Granule 的原始数据 几 MB 到几十 MB 该 Granule 对应的 ngrambf_v1索引 (BF)几百字节 ( M=4096→ 512字节)💡 核心思想:BF 牺牲了一点点“精确性”(允许误报),换取了巨大的空间节省和极快的查询速度。在“跳过索引”的场景下,这是完美的权衡——宁可多读一点(误报),也绝不能漏掉(漏报)。
最终结论
布隆过滤器之所以“小”,是因为:
- 它不存数据,只存哈希指纹。
- 它的底层是一个固定大小的位数组,与插入元素的数量无关。
- 它利用哈希冲突来实现极致压缩,并通过数学方法将误报率控制在可接受范围内。
因此,即使一个 Granule 包含海量的 N-Gram,其对应的
ngrambf_v1索引依然只是一个 KB 级别的微小结构,却能高效地完成“可能性预筛”的任务。
※,9 数据查询
问:ClickHouse如何输出查看执行计划?
DeepSeek回答:ClickHouse 提供了多种查看查询执行计划的方法。以下是主要的几种方式:
1. EXPLAIN 语句(最常用)
查看逻辑执行计划
-- 查看语法树(逻辑计划) EXPLAIN SYNTAX SELECT * FROM your_table WHERE id = 1; -- 查看查询流水线(物理计划) EXPLAIN PIPELINE SELECT * FROM your_table WHERE id = 1; -- 查看执行计划(最详细) EXPLAIN PLAN SELECT * FROM your_table WHERE id = 1;查看带设置的执行计划
EXPLAIN SELECT * FROM your_table WHERE id = 1 SETTINGS allow_experimental_analyzer = 1; -- 使用新的分析器2. EXPLAIN ESTIMATE(估算统计信息)
-- 估算查询将处理的数据量 EXPLAIN ESTIMATE SELECT * FROM your_table WHERE date >= '2024-01-01';3. 查看查询执行的详细步骤
启用详细日志
-- 在查询时启用trace日志 SELECT * FROM your_table SETTINGS send_logs_level = 'trace', -- 查看详细执行日志 log_queries = 1; -- 记录查询日志或使用
clickhouse-client -h localhost --password Clickhouse@2026 --send_logs_level=trace <<< 'SELECT * FROM simulation.load_raw_data' > /dev/null(base) [root@sungrow27 20240509_1_1_0]$clickhouse-client --version ClickHouse client version 26.2.3.2 (official build). (base) [root@sungrow27 20240509_1_1_0]$clickhouse-client -h localhost --password Clickhouse@2026 --send_logs_level=trace <<< 'SELECT * FROM simulation.load_raw_data' > /dev/null [sungrow27] 2026.03.12 13:45:13.486734 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Debug> executeQuery: (from 127.0.0.1:56624) (query 1, line 1) SELECT * FROM simulation.load_raw_data (stage: Complete) [sungrow27] 2026.03.12 13:45:13.487487 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Trace> Planner: Query to stage Complete [sungrow27] 2026.03.12 13:45:13.487693 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Trace> Planner: Query from stage FetchColumns to stage Complete [sungrow27] 2026.03.12 13:45:13.488088 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Key condition: unknown [sungrow27] 2026.03.12 13:45:13.488806 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): MinMax index condition: unknown [sungrow27] 2026.03.12 13:45:13.488890 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Trace> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Filtering marks by primary and secondary keys [sungrow27] 2026.03.12 13:45:13.493081 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): PK index has dropped 0/282 granules, it took 0ms across 40 threads. [sungrow27] 2026.03.12 13:45:13.493267 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Selected 282/282 parts by partition key, 282 parts by primary key, 282/282 marks by primary key, 282 marks to read from 282 ranges [sungrow27] 2026.03.12 13:45:13.493675 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Trace> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Spreading mark ranges among streams (default reading) [sungrow27] 2026.03.12 13:45:13.501670 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Reading approx. 81216 rows with 40 streams [sungrow27] 2026.03.12 13:45:13.523991 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Debug> executeQuery: Read 81216 rows, 3.87 MiB in 0.037162 sec., 2185458.2638178784 rows/sec., 104.20 MiB/sec. [sungrow27] 2026.03.12 13:45:13.524191 [ 2367241 ] {ffb2f54d-ef5e-4e81-bef0-14907ac8c707} <Debug> MemoryTracker: Query peak memory usage: 4.89 MiB.(base) [root@sungrow27 20240509_1_1_0]$clickhouse-client -h localhost --password Clickhouse@2026 --send_logs_level=trace <<< "SELECT * FROM simulation.load_raw_data where data_time='2024-05-09 00:00:00'" > /dev/null [sungrow27] 2026.03.12 13:50:54.489445 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Debug> executeQuery: (from 127.0.0.1:50512) (query 1, line 1) SELECT * FROM simulation.load_raw_data where data_time='2024-05-09 00:00:00' (stage: Complete) [sungrow27] 2026.03.12 13:50:54.491304 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Trace> Planner: Query to stage Complete [sungrow27] 2026.03.12 13:50:54.491624 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Trace> Planner: Query from stage FetchColumns to stage Complete [sungrow27] 2026.03.12 13:50:54.493018 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Trace> QueryPlanOptimizePrewhere: The min valid primary key position for moving to the tail of PREWHERE is -1 [sungrow27] 2026.03.12 13:50:54.493075 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Trace> QueryPlanOptimizePrewhere: Moved 1 conditions to PREWHERE [sungrow27] 2026.03.12 13:50:54.493383 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Key condition: (column 1 in [1715184000, 1715184000]) [sungrow27] 2026.03.12 13:50:54.493837 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): MinMax index condition: (column 0 in [1715184000, 1715184000]) [sungrow27] 2026.03.12 13:50:54.493951 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Query condition cache has dropped 0/1 granules for PREWHERE condition equals(__table1.data_time, '2024-05-09 00:00:00'_String). [sungrow27] 2026.03.12 13:50:54.494021 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Query condition cache has dropped 0/1 granules for WHERE condition equals(data_time, '2024-05-09 00:00:00'_String). [sungrow27] 2026.03.12 13:50:54.494079 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Trace> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Filtering marks by primary and secondary keys [sungrow27] 2026.03.12 13:50:54.494164 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Trace> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Used generic exclusion search over index for part 20240509_1_283_4_284 with 1 steps [sungrow27] 2026.03.12 13:50:54.494231 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): PK index has dropped 0/1 granules, it took 0ms across 1 threads. [sungrow27] 2026.03.12 13:50:54.494311 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Debug> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Selected 1/282 parts by partition key, 1 parts by primary key, 1/1 marks by primary key, 1 marks to read from 1 ranges [sungrow27] 2026.03.12 13:50:54.494401 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Trace> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Spreading mark ranges among streams (default reading) [sungrow27] 2026.03.12 13:50:54.494609 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Trace> simulation.load_raw_data (fa3adb74-721c-48ef-8abd-8581fd90a9a2) (SelectExecutor): Reading 1 ranges in order from part 20240509_1_283_4_284, approx. 288 rows starting from 0 [sungrow27] 2026.03.12 13:50:54.497771 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Debug> executeQuery: Read 288 rows, 13.78 KiB in 0.008472 sec., 33994.33427762039 rows/sec., 1.59 MiB/sec. [sungrow27] 2026.03.12 13:50:54.497847 [ 2367241 ] {128ca226-cfcc-415e-b9f5-6954b095fa8a} <Debug> MemoryTracker: Query peak memory usage: 268.32 KiB. (base) [root@sungrow27 20240509_1_1_0]$查看查询的详细执行信息
执行查询后,从系统表查看:
-- 查看最近的查询执行详情 SELECT query, query_id, read_rows, read_bytes, result_rows, result_bytes, memory_usage FROM system.query_log WHERE type = 'QueryFinish' ORDER BY event_time DESC LIMIT 5;4. 使用特殊表函数
查看查询执行的步骤
-- 查看查询的执行步骤和耗时 SELECT * FROM clusterAllReplicas(default, system.query_log) WHERE query_id = 'your-query-id-here' AND type = 'QueryFinish';5. 在客户端中查看
在clickhouse-client中启用详细输出
# 使用详细模式启动客户端 clickhouse-client --verbose # 或者在查询时使用\G格式 clickhouse-client --query "EXPLAIN PLAN SELECT * FROM your_table" --format Vertical6. 查看查询的性能剖析
启用性能剖析
-- 启用查询剖析 SET allow_introspection_functions = 1; -- 查看查询的内存使用 EXPLAIN ESTIMATE SELECT * FROM your_table SETTINGS allow_introspection_functions = 1, log_comment = 'profile_query';7. 查看实际执行统计
执行查询后,查看实际的执行统计:
-- 查看表的部分统计 SELECT table, sum(rows) as total_rows, formatReadableSize(sum(bytes)) as total_size, formatReadableSize(sum(data_uncompressed_bytes)) as uncompressed_size FROM system.parts WHERE active GROUP BY table ORDER BY sum(rows) DESC;8. 使用 Grafana 或 ClickHouse 管理工具
如果你使用可视化工具,如:
Tabix
DBeaver(需要安装ClickHouse驱动)
DataGrip
Grafana 配合 ClickHouse 数据源
这些工具通常有可视化的执行计划查看器。
实际使用示例
示例1:查看完整执行计划
-- 查看完整的执行计划 EXPLAIN SELECT database, formatReadableSize(sum(data_uncompressed_bytes)) as size FROM system.columns GROUP BY database ORDER BY size DESC SETTINGS send_logs_level = 'debug', log_queries = 1;示例2:查看查询管道
-- 显示查询的物理执行管道 EXPLAIN PIPELINE SELECT toYear(date) as year, count() as cnt FROM hits WHERE CounterID = 34 GROUP BY year ORDER BY year;示例3:在日志中查看详细执行
-- 执行查询并查看详细日志 SELECT * FROM system.tables WHERE name LIKE 'query%' SETTINGS send_logs_level = 'trace', log_queries = 1;执行后查看系统日志:
-- 查看查询执行日志 SELECT * FROM system.query_log WHERE event_time > now() - interval 5 minute AND query LIKE '%system.tables%' ORDER BY event_time DESC LIMIT 1\G小贴士
对于复杂查询,建议使用
EXPLAIN PLAN和EXPLAIN PIPELINE结合查看性能瓶颈时,使用
send_logs_level = 'trace'获取详细日志长期监控查询性能,可以定期查询
system.query_log在生产环境中谨慎使用 trace 级别日志,因为它会产生大量日志数据
选择哪种方法取决于你的具体需求:
调试查询逻辑 →
EXPLAIN SYNTAX优化查询性能 →
EXPLAIN PIPELINE查看执行细节 → 启用 trace 日志
监控生产查询 → 查询
system.query_log
★,9.8 having子句
HAVING子句需要与GROUP BY同时出现,不能单独使用。它能够在聚合计算之后实现二次过滤数据。例如下面的语句是一条普通的聚合查询,会按照table分组并计数:
SELECT COUNT() FROM system.parts GROUP BY table
--执行计划
Expression
Expression
Aggregating
Concat
Expression
One
现在增加HAVING子句后再次执行上述操作,则数据在按照table聚合之后,进一步截掉了table='query_v3'的部分。
SELECT COUNT() FROM system.parts GROUP BY table HAVING table = 'query_v3'
--执行计划
Expression
Expression
Filter
Aggregating
Concat
Expression
One
观察两次查询的执行计划,可以发现HAVING的本质是在聚合之后增加了Filter过滤动作。
对于类似上述的查询需求,除了使用HAVING之外,通过嵌套的WHERE也能达到相同的目的,例如下面的语句:
SELECT COUNT() FROM (SELECT table FROM system.parts WHERE table = 'query_v3') GROUP BY table
--执行计划
Expression
Expression
Aggregating
Concat
Expression
Expression
Expression
Filter
One
分析上述查询的执行计划,相比使用HAVING,嵌套WHERE的执行计划效率更高。因为WHERE等同于使用了谓词下推,在聚合之前就进行了数据过滤,从而减少了后续聚合时需要处理的数据量。
既然如此,那是否意味着HAVING子句没有存在的意义了呢?其实不然,现在来看另外一种查询诉求。假设现在需要按照table分组聚合,并且返回均值bytes_on_disk大于10 000字节的数据表,在这种情形下需要使用HAVING子句:
SELECT table ,avg(bytes_on_disk) as avg_bytes
FROM system.parts GROUP BY table
HAVING avg_bytes > 10000
┌─table─────┬───avg_bytes───┐
│ hits_v1 │ 730190752 │
└─────────┴────────────┘
这是因为WHERE的执行优先级大于GROUP BY,所以如果需要按照聚合值进行过滤,就必须借助HAVING实现。
★,9.12 select子句
SELECT子句决定了一次查询语句最终返回哪些列字段或表达式。与直观的感受不同,虽然SELECT位于SQL语句的起始位置,但它却是在上述一众子句之后执行的。在其他子句执行之后,SELECT会将选取的字段或表达式作用于每行数据之上。
在选择列字段时,ClickHouse还为特定场景提供了一种基于正则查询的形式。例如执行下面的语句后,查询会返回名称以字母n开头和包含字母p的列字段:
SELECT COLUMNS('^n'), COLUMNS('p') FROM system.databases
┌─name────┬─data_path────┬─metadata_path───┐
│ default │ /data/default/ │ /metadata/default/ │
│ system │ /data/system/ │ /metadata/system/ │
★,9.13 DISTINCT子句
DISTINCT子句能够去除重复数据,使用场景广泛。有时候,人们会拿它与GROUP BY子句进行比较。假设数据表query_v5的数据如下所示:
┌─name─┬─v1─┐
│ a │ 1 │
│ c │ 2 │
│ b │ 3 │
│ NULL │ 4 │
│ d │ 5 │
│ a │ 6 │
│ a │ 7 │
│ NULL │ 8 │
└────┴───┘
则下面两条SQL查询的返回结果相同:
-- DISTINCT查询
SELECT DISTINCT name FROM query_v5
-- DISTINCT查询执行计划
Expression
Distinct
Expression
Log
-- GROUP BY查询
SELECT name FROM query_v5 GROUP BY name
-- GROUP BY查询执行计划
Expression
Expression
Aggregating
Concat
Expression
Log
其中,第一条SQL语句使用了DISTINCT子句,第二条SQL语句使用了GROUP BY子句。但是观察它们执行计划不难发现,DISTINCT子句的执行计划更简单。与此同时,DISTINCT也能够与GROUP BY同时使用,所以它们是互补而不是互斥的关系。
如果使用了LIMIT且没有ORDER BY子句,则DISTINCT在满足条件时能够迅速结束查询,这样可避免多余的处理逻辑;
而当DISTINCT与ORDER BY同时使用时,其执行的优先级是先DISTINCT后ORDER BY。
本章将会介绍ClickHouse的权限、熔断机制、数据备份和服务监控等知识。
user.xml配置文件默认位于/etc/clickhouse-server/user.d/路径下,ClickHouse使用它来定义用户相关的配置项,包括系统参数的设定、用户的定义、权限以及熔断机制等。
使用users标签可以配置自定义用户。如果打开user.xml配置文件,会发现已经默认配置了default用户,在此之前的所有示例中,一直使用的正是这个用户。定义一个新用户,必须包含以下几项属性。
1.username
username用于指定登录用户名,这是全局唯一属性。该属性比较简单,这里就不展开介绍了。
2.password
password用于设置登录密码,支持明文、SHA256加密和double_sha1加密三种形式,可以任选其中一种进行设置。现在分别介绍它们的使用方法。
(1)明文密码:在使用明文密码的时候,直接通过password标签定义,例如下面的代码:<password>123</password>;如果password为空,则表示免密码登录:<password></password>
(2)SHA256加密:在使用SHA256加密算法的时候,需要通过password_sha256_hex标签定义密码,例如下面的代码:<password_sha256_hex>a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3</password_sha256_hex>
可以执行下面的命令获得密码的加密串,例如对明文密码123进行加密:
# echo -n 123 | openssl dgst -sha256
(stdin)= a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3
(3)double_sha1加密:在使用double_sha1加密算法的时候,则需要通过password_double_sha1_hex标签定义密码,例如下面的代码:<password_double_sha1_hex>23ae809ddacaf96af0fd78ed04b6a265e05aa257</password_double_sha1_hex>
可以执行下面的命令获得密码的加密串,例如对明文密码123进行加密:
# echo -n 123 | openssl dgst -sha1 -binary | openssl dgst -sha1
(stdin)= 23ae809ddacaf96af0fd78ed04b6a265e05aa257
数据导出:如果数据的体量较小,可以通过dump的形式将数据导出为本地文件。例如执行下面的语句将test_backup的数据导出:
clickhouse-client --password Clickhouse@2026 --query "select * from simulation.load_raw_data" > load_raw_data_backup.csvclickhouse-client --password Clickhouse@2026 <<< "select * from simulation.load_raw_data" > load_raw_data_backup.csv数据导入:将备份数据再次导入,则可以执行下面的语句:
cat load_raw_data_backup.csv | clickhouse-client --query "INSERT INTO simulation.load_raw_data FORMAT TSV"上述这种dump形式的优势在于,可以利用SELECT查询并筛选数据,然后按需备份。
如果是备份整个表的数据,也可以直接复制它的整个目录文件,例如:
mkdir -p /chbase/backup/default/ & cp -r /chbase/data/default/test_backup /chbase/backup/default/ #新版ClickHouse(26.2.3.2)数据表目录变成了一个软连接,和这个例子不太一样。快照表实质上就是普通的数据表,它通常按照业务规定的备份频率创建,例如按天或者按周创建。所以首先需要建立一张与原表结构相同的数据表,然后再使用INSERT INTO SELECT句式,点对点地将数据从原表写入备份表。假设数据表test_backup需要按日进行备份,现在为它创建当天的备份表:CREATE TABLE test_backup_0206 AS test_backup
有了备份表之后,就可以点对点地备份数据了,例如:INSERT INTO TABLE test_backup_0206 SELECT * FROM test_backup
如果考虑到容灾问题,也可以将备份表放置在不同的ClickHouse节点上,此时需要将上述SQL语句改成远程查询的形式:
INSERT INTO TABLE test_backup_0206 SELECT * FROM remote('ch5.nauu.com:9000','default', 'test_backup', 'default')
基于数据分区的备份,ClickHouse目前提供了FREEZE与FETCH两种方式。
FREEZE的完整语法如下所示:ALTER TABLE tb_name FREEZE PARTITION partition_expr
分区在被备份之后,会被统一保存到/path_to_clickhouse_data_root_dir/shadow/N子目录下。其中,N是一个自增长的整数,它的含义是备份的次数(FREEZE执行过多少次),具体次数由shadow子目录下的increment.txt文件记录。而分区备份实质上是对原始目录文件进行硬链接操作,所以并不会导致额外的存储空间。
对于备份分区的还原操作,则需要借助ATTACH装载分区的方式来实现。这意味着如果要还原数据,首先需要主动将shadow子目录下的分区文件复制到相应数据表的detached目录下,然后再使用ATTACH语句装载。
FETCH只支持ReplicatedMergeTree系列的表引擎,它的完整语法如下所示:ALTER TABLE tb_name FETCH PARTITION partition_id FROM zk_path
其工作原理与ReplicatedMergeTree同步数据的原理类似,FETCH通过指定的zk_path找到ReplicatedMergeTree的所有副本实例,然后从中选择一个最合适的副本,并下载相应的分区数据。例如执行下面的语句:ALTER TABLE test_fetch FETCH PARTITION 2019 FROM '/clickhouse/tables/01/test_fetch'表示指定将test_fetch的2019分区下载到本地,并保存到对应数据表的detached目录下,目录如下所示:data/default/test_fetch/detached/2019_0_0_0
与FREEZE一样,对于备份分区的还原操作,也需要借助ATTACH装载分区来实现。
FREEZE和FETCH虽然都能实现对分区文件的备份,但是它们并不会备份数据表的元数据。所以说如果想做到万无一失的备份,还需要对数据表的元数据进行备份,它们是/path_to_clickhouse_data_root_dir/metadata目录下的[table].sql文件。目前这些元数据需要用户通过复制的形式单独备份。
基于原生功能对ClickHouse进行监控,可以从两方面着手——系统表和查询日志。
在众多的SYSTEM系统表中,主要由以下三张表支撑了对ClickHouse运行指标的查询,它们分别是metrics、events和asynchronous_metrics。
metrics表用于统计ClickHouse服务在运行时,当前正在执行的高层次的概要信息,包括正在执行的查询总次数、正在发生的合并操作总次数等。该系统表的查询方法如下所示
SELECT * FROM system.metrics LIMIT 5
┌─metric──────┬─value─┬─description─────────────────────┐
│ Query │ 1 │ Number of executing queries │
│ Merge │ 0 │ Number of executing background merges │
│ PartMutation │ 0 │ Number of mutations (ALTER DELETE/UPDATE) │
│ ReplicatedFetch │ 0 │ Number of data parts being fetched from replica │
│ ReplicatedSend │ 0 │ Number of data parts being sent to replicas
└──────────┴─────┴─────────────────────────────┘
events用于统计ClickHouse服务在运行过程中已经执行过的高层次的累积概要信息,包括总的查询次数、总的SELECT查询次数等,该系统表的查询方法如下所示:
SELECT event, value FROM system.events LIMIT 5
┌─event─────────────────────┬─value─┐
│ Query │ 165 │
│ SelectQuery │ 92 │
│ InsertQuery │ 14 │
│ FileOpen │ 3525 │
│ ReadBufferFromFileDescriptorRead │ 6311 │
└─────────────────────────┴─────┘
asynchronous_metrics用于统计ClickHouse服务运行过程时,当前正在后台异步运行的高层次的概要信息,包括当前分配的内存、执行队列中的任务数量等。该系统表的查询方法如下所示:
SELECT * FROM system.asynchronous_metrics LIMIT 5
┌─metric────────────────────────┬─value─┬─description────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
1. │ OSGuestTimeCPU1 │ 0 │ The ratio of time spent running a virtual CPU for guest operating systems under the control of the Linux kernel (See `man procfs`). This is a system-wide metric, it includes all the processes on the host machine, not just clickhouse-server. This metric is irrelevant for ClickHouse, but still exists for completeness. The value for a single CPU core will be in the interval [0..1]. The value for all CPU cores is calculated as a sum across them [0..num cores]. │
2. │ ReplicasMaxQueueSize │ 0 │ Maximum queue size (in the number of operations like get, merge) across Replicated tables. │
3. │ jemalloc.prof.active │ 1 │ An internal metric of the low-level memory allocator (jemalloc). See https://jemalloc.net/jemalloc.3.html │
4. │ NetworkSendErrors_veth5a6b48e │ 0 │ Number of times error (e.g. TCP retransmit) happened while sending via the network interface. This is a system-wide metric, it includes all the processes on the host machine, not just clickhouse-server. │
5. │ OSIOWaitTimeCPU3 │ 0 │ The ratio of time the CPU core was not running the code but when the OS kernel did not run any other process on this CPU as the processes were waiting for IO. This is a system-wide metric, it includes all the processes on the host machine, not just clickhouse-server. The value for a single CPU core will be in the interval [0..1]. The value for all CPU cores is calculated as a sum across them [0..num cores]. │
└───────────────────────────────┴───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
查询日志目前主要有6种类型,它们分别从不同角度记录了ClickHouse的操作行为。所有查询日志在默认配置下都是关闭状态(新版好像不是了),需要在config.xml配置中进行更改,接下来分别介绍它们的开启方法。在配置被开启之后,ClickHouse会为每种类型的查询日志自动生成相应的系统表以供查询。
query_log是最常用的查询日志,它记录了ClickHouse服务中所有已经执行的查询记录,它的全局定义方式如下所示:
<!-- config.xml -->
<clickhouse>
<query_log>
<database>system</database>
<table>query_log</table>
<partition_by>toYYYYMM(event_date)</partition_by>
<!—刷新周期-->
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</query_log>
</clickhouse>
如果只需要为某些用户单独开启query_log,也可以在user.xml的profile配置中按照下面的方式定义:<log_queries> 1</log_queries>。
system.query_log日志记录的信息十分完善,涵盖了查询语句、执行时间、执行用户返回的数据量和执行用户等。
query_thread_log记录了所有线程的执行查询的信息,它的全局定义方式如下所示:
<!-- config.xml -->
<clickhouse>
<query_thread_log>
<database>system</database>
<table>query_thread_log</table>
<partition_by>toYYYYMM(event_date)</partition_by>
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</query_thread_log>
</clickhouse>
同样,如果只需要为某些用户单独开启该功能,可以在user.xml的profile配置中按照下面的方式定义:<log_query_threads> 1</log_query_threads>。
system.query_thread_log日志记录的信息涵盖了线程名称、查询语句、执行时间和内存用量等。
part_log日志记录了MergeTree系列表引擎的分区操作日志,其全局定义方式如下所示:
<!-- config.xml -->
<clickhouse>
<part_log>
<database>system</database>
<table>part_log</table>
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</part_log>
</clickhouse>
system.part_log日志记录的信息涵盖了操纵类型、表名称、分区信息和执行时间等。
text_log日志记录了ClickHouse运行过程中产生的一系列打印日志,包括INFO、DEBUG和Trace,它的全局定义方式如下所示:
<!-- config.xml -->
<clickhouse>
<text_log>
<database>system</database>
<table>text_log</table>
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
</text_log>
</clickhouse>
system.text_log日志记录的信息涵盖了线程名称、日志对象、日志信息和执行时间等。
metric_log日志用于将system.metrics和system.events中的数据汇聚到一起,它的全局定义方式如下所示:
<!-- config.xml -->
<clickhouse>
<metric_log>
<database>system</database>
<table>metric_log</table>
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
<collect_interval_milliseconds>1000</collect_interval_milliseconds>
</metric_log>
</clickhouse>
其中,collect_interval_milliseconds表示收集metrics和events数据的时间周期。
除了上面介绍的系统表和查询日志外,ClickHouse还能够与众多的第三方监控系统集成,限于篇幅这里就不再展开了。
================结束标记=================
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。