EKS 上 ClickHouse 实测基准测验
- 在该基准测试中,针对 1 亿行表统计一小时
namespace + timestamp范围的查询(Q1)用时 4ms。主要原因是什么?- A) 结果已经存储在查询缓存中
- B) 通过 PARTITION BY (day) 和 ORDER BY (namespace, timestamp) 进行裁剪后,只读取了 1 亿行中的 16,385 行
- C) gp3 卷的 3,000 IOPS 已足够高
- D) LowCardinality 类型对比较操作进行了向量化
显示答案
答案:B) 通过 PARTITION BY (day) 和 ORDER BY (namespace, timestamp) 进行裁剪后,只读取了 1 亿行中的 16,385 行
说明: 当查询条件与分区键(按日)和排序键(namespace、timestamp)匹配时,ClickHouse 会使用主索引按 granule 粒度进行裁剪。在此基准测试中,查询统计了 59,916 行,但只读取 16,385 行(0.016%):完全位于主键范围内的 granule 会通过索引计数(ClickHouse 24.6+ 的一项优化),只有范围边缘处不完整的 granule 才会解压缩。ClickHouse 的大部分速度都来自这种“不读取数据”的设计。
- 每列压缩结果中,trace_id 的压缩比为 1.0×(不可压缩),而 namespace 的压缩比约为 201×。如何解释这种差异?
- A) 只有 trace_id 列缺少压缩编解码器
- B) namespace 字符串更短
- C) 此次运行中 trace_id 几乎没有重复模式,LZ4 基本无法压缩;而 namespace 基数较低且是第一个 ORDER BY 键,因此相同值会形成长连续区段
- D) 只有 namespace 使用单独的字典文件
显示答案
答案:C) 此次运行中 trace_id 几乎没有重复模式,LZ4 基本无法压缩;而 namespace 基数较低且是第一个 ORDER BY 键,因此相同值会形成长连续区段
说明: 此次 LZ4 结果约为原始 3.07 GiB → 压缩后 3.08 GiB。十六进制数据每字节仅存储 4 位信息,因此其他编解码器或二进制表示仍可能降低体积。namespace 只有 10 个不同值,且为首个排序键,因此相同值会连续存储:95.72 MiB 缩减为 488.63 KiB(201×)。这就是为什么日志架构中“如何存储 ID”是影响存储成本的最大因素。
- 哪项说法符合 LZ4(默认)与 ZSTD(3) 的实测对比?
- A) ZSTD(3) 在存储和扫描速度上都更好
- B) ZSTD(3) 的存储量减少了 47%,但受 CPU 限制的全表扫描比 LZ4 慢约 1.9×
- C) 两种编解码器之间的存储差异在 5% 以内
- D) LZ4 磁盘占用更小,但扫描更慢
显示答案
答案:B) ZSTD(3) 的存储量减少了 47%,但受 CPU 限制的全表扫描比 LZ4 慢约 1.9×
说明: 实测结果:LZ4 为 7.82 GiB(1.97×),ZSTD(3) 为 4.16 GiB(3.7×),存储量减少 47%;但温热状态下的 LIKE '%timeout%' 全表扫描从 2.63 秒变为 4.9 秒。可考虑让近期分区使用 LZ4,并通过 TTL 驱动 ZSTD 重新压缩,再根据实际数据和 CPU/I/O 平衡进行验证。
- 绕过页缓存时,
LIKE '%timeout%'全表扫描(Q3)耗时 31.5 秒。这一数字指向什么瓶颈?- A) ClickHouse 字符串搜索算法效率低
- B) 读取约 4 GiB 的压缩 message 列 ÷ 31.5 秒 ≈ 130 MiB/s,与卷/实例 EBS 吞吐量限制相符,但不足以确定唯一瓶颈
- C) m5.xlarge 的 4 个 vCPU 已饱和
- D) EBS CSI 驱动程序开销占主导
显示答案
答案:B) 读取约 4 GiB 的压缩 message 列 ÷ 31.5 秒 ≈ 130 MiB/s,与卷/实例 EBS 吞吐量限制相符,但不足以确定唯一瓶颈
说明: 同一查询从页缓存提供数据时耗时 2.63 秒(受 CPU 限制,约 3,800 万行/秒)。直接从磁盘读取时慢了 12×,这是实测证据,表明全表扫描性能可能取决于卷吞吐量设置,而不只是数据库本身;gp3 吞吐量最高可配置为 2,000 MiB/s,但需额外付费(达到该值还要求至少 8,000 IOPS)。不过在这台 m5.xlarge 上,也必须检查实例约 137 MiB/s 的基线吞吐量和突发状态。应结合节点和卷容量检查持续负载需求。
- 根据测量结果,添加 bloom filter skip index 后,trace_id 点查(Q5)发生了什么变化?
- A) 查询时间从 1.13 秒降至 0.036 秒(31×),读取行数从 1 亿降至 108 万(跳过 98.9%),索引大小约为表的 1.5%
- B) 查询时间不变,只降低了内存使用量
- C) 查询时间减半,索引占表大小的 30%
- D) 效果与更改 ORDER BY 键相同,而且存储量也下降
显示答案
答案:A) 查询时间从 1.13 秒降至 0.036 秒(31×),读取行数从 1 亿降至 108 万(跳过 98.9%),索引大小约为表的 1.5%
说明:bloom_filter(0.01) GRANULARITY 4 跳过索引在判断成员关系时没有假阴性:否定结果允许跳过整个组,肯定结果则可能是假阳性,需要额外读取。读取数据量从 3.82 GiB 降至 42.6 MiB;索引本身为 119.7 MiB(表的 1.5%),MATERIALIZE 耗时约 20 秒。
- 基准测试中的摄取量(约 94 万行/秒)必须附带什么说明?
- A) 此结果是在三个副本下测得
- B) 此结果通过服务器内的 INSERT…SELECT 测得,因此不能直接衡量外部网络/解析摄取吞吐量
- C) 数据只写入内存,从未写入磁盘
- D) 测量期间禁用了压缩
显示答案
答案:B) 此结果通过服务器内的 INSERT…SELECT 测得,因此不能直接衡量外部网络/解析摄取吞吐量
说明: 数据在服务器内部生成并直接插入,因此比外部客户端推送 TSV/Native 数据更有利。测量包含排序、压缩和磁盘写入,但也包含数据生成的 CPU 开销,所以该数值并不是外部摄取的数学上限。应单独测量预期的客户端、批处理方式、并发数和格式。
- Q4(每个 namespace 的持续时间 p50/p99)扫描全部 1 亿行,但温热状态下约 1 秒就完成。哪项特性使其成为可能?
- A) 结果已在物化视图中预先计算
- B) 列式存储:只读取 duration_ms(289 MiB)和 namespace(0.5 MiB),而不是整个表的 7.8 GiB
- C) quantile 函数只读取 8,192 行磁盘数据,不处理其余行
- D) 分区裁剪将扫描范围限制为一天的数据
显示答案
答案:B) 列式存储:只读取 duration_ms(289 MiB)和 namespace(0.5 MiB),而不是整个表的 7.8 GiB
说明: 面向行的数据库必须读取该查询涉及的整行;列式引擎只读取聚合所需的列。此次扫描覆盖了完整时间范围(没有分区裁剪,因此 D 错误),而 quantile 会处理所有输入行,同时最多使用 8,192 个样本的 reservoir 进行近似。抽样并不会省去输入列扫描。需要精确值时,应单独评估 quantileExact。
- 根据本指南的决策框架,什么情况下在 EKS 上自托管 ClickHouse(而不是使用托管服务)尤其合理?
- A) 没有平台团队,也没有数据库运维经验
- B) 标准托管服务已足够,且只有一个租户
- C) 托管方案受限或成本倍数很高,并且平台团队能够负责基于 operator 的运维,包括备份和恢复演练
- D) 数据量较小(低于 100 GiB)
显示答案
答案:C) 托管方案受限或成本倍数很高,并且平台团队能够负责基于 operator 的运维,包括备份和恢复演练
说明: 这是数据库章节概览中的托管与自托管决策框架,并非来自基准测试文章本身。ClickHouse 使用 Apache-2.0 许可证,且拥有成熟的社区 operator(Altinity),因此自托管是可行的选择;前提是有该 operator 和负责运行它的团队。反之,如果运维人员有限,或已有合适的托管方案,托管服务更合适;通过团队运维原始 StatefulSet 的方式,则必须自行实现相同的生命周期管理和恢复职责。