在了解如何查询我们的 Wikipedia 统计数据集后,接下来我们将重点介绍如何优化其在 ClickHouse 中的存储效率。 本节将演示一些实用技巧,帮助您在保持查询性能的同时降低存储需求。
类型优化
优化存储效率的常见方法是使用合适的数据类型。
以 project 和 subproject 列为例。这些列的类型是 String,但唯一值的数量相对较少:
SELECT
uniq(project),
uniq(subproject)
FROM wikistat;┌─uniq(project)─┬─uniq(subproject)─┐
│ 1332 │ 130 │
└───────────────┴──────────────────┘这意味着我们可以使用采用字典编码的 LowCardinality() 数据类型。这样一来,ClickHouse 存储的是内部值 ID,而不是原始字符串值,从而节省大量空间:
ALTER TABLE wikistat
MODIFY COLUMN `project` LowCardinality(String),
MODIFY COLUMN `subproject` LowCardinality(String)我们还为 hits 列使用了 UInt64 类型。它占用 8 字节,但最大值相对较小:
SELECT max(hits)
FROM wikistat;┌─max(hits)─┐
│ 449017 │
└───────────┘鉴于该值,我们可以改用 UInt32,它只占 4 字节,且可存储的最大值约为 40 亿:
ALTER TABLE wikistat
MODIFY COLUMN `hits` UInt32;这会使该列在内存中的大小至少减半。请注意,由于压缩,磁盘上的大小将保持不变。但也要小心:选择的数据类型不要太小!
专用编解码器
处理顺序数据 (例如时间序列) 时,我们还可以借助特殊编解码器进一步提升存储效率。 其基本思路是存储值与值之间的变化量,而不是值本身的绝对值;这样在处理变化缓慢的数据时,所需空间会少得多:
ALTER TABLE wikistat
MODIFY COLUMN `time` CODEC(Delta, ZSTD);我们已对 time 列使用了 Delta codec,这很适合时间序列数据。
合适的排序键也可以节省磁盘空间。
由于我们通常会按路径进行过滤,因此我们会将 path 添加到排序键中。
这需要重新创建表。
下面我们可以看到初始表和优化后表的 CREATE 命令:
CREATE TABLE wikistat
(
`time` DateTime,
`project` String,
`subproject` String,
`path` String,
`hits` UInt64
)
ENGINE = MergeTree
ORDER BY (time);CREATE TABLE optimized_wikistat
(
`time` DateTime CODEC(Delta(4), ZSTD(1)),
`project` LowCardinality(String),
`subproject` LowCardinality(String),
`path` String,
`hits` UInt32
)
ENGINE = MergeTree
ORDER BY (path, time);再来看看每个表中的数据占用了多少空间:
SELECT
table,
formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed,
formatReadableSize(sum(data_compressed_bytes)) AS compressed,
count() AS parts
FROM system.parts
WHERE table LIKE '%wikistat%'
GROUP BY ALL;┌─table──────────────┬─uncompressed─┬─compressed─┬─parts─┐
│ wikistat │ 35.28 GiB │ 12.03 GiB │ 1 │
│ optimized_wikistat │ 30.31 GiB │ 2.84 GiB │ 1 │
└────────────────────┴──────────────┴────────────┴───────┘优化后的表在压缩后占用的空间仅为原来的不到 1/4。