跳到主要内容
跳到主要内容

选择主键

我们在本页中互换使用“排序键(ordering key)”一词来指代“主键(primary key)”。严格来说,在 ClickHouse 中它们有所不同,但为了本文档的目的,读者可以互换使用这两个术语,其中排序键指的是在表中指定的 ORDER BY 列。

注意,ClickHouse 的主键与熟悉 OLTP 数据库(如 Postgres)中类似术语的用户 非常不同

在 ClickHouse 中选择有效的主键对于查询性能和存储效率至关重要。ClickHouse 将数据组织成分片,每个分片包含其自身的稀疏主索引。此索引通过减少扫描的数据量显著加快查询速度。此外,由于主键决定了磁盘上数据的物理顺序,它直接影响压缩效率。优化排序的数据可以更有效地压缩,从而通过减少 I/O 进一步提高性能。

  1. 在选择排序键时,优先考虑在查询过滤器(即 WHERE 子句)中频繁使用的列,尤其是那些排除大量行的列。
  2. 与表中其他数据高度相关的列也是有益的,因为连续存储可提高压缩率和 GROUP BYORDER BY 操作期间的内存效率。

可以应用一些简单的规则来帮助选择排序键。以下规则有时可能会出现冲突,因此请按顺序考虑这些规则。用户可以通过此过程识别多个键,通常 4-5 个即可

重要

排序键必须在表创建时定义,无法在之后添加。可以通过称为投影的功能在数据插入之后(或之前)向表中添加额外的排序键。请注意,这会导致数据重复。更多细节 请参见这里

示例

考虑以下 posts_unordered 表。该表每行对应 Stack Overflow 的一篇帖子。

该表没有主键 - 正如 ORDER BY tuple() 所指示的那样。

假设用户希望计算 2024 年之后提交的问题数量,这代表了他们最常见的访问模式。

请注意此查询读取的行数和字节数。没有主键,查询必须扫描整个数据集。

使用 EXPLAIN indexes=1 确认由于缺乏索引而导致的完整表扫描。

假设一个名为 posts_ordered 的表,包含相同数据,定义的 ORDER BY(PostTypeId, toDate(CreationDate)),即

PostTypeId 的基数为 8,是排序键中第一个条目的逻辑选择。认识到日期粒度过滤可能是足够的(它仍然可以受益于日期时间过滤),因此我们使用 toDate(CreationDate) 作为我们的键的第二个组件。这也会产生更小的索引,因为日期可以用 16 位表示,从而加快过滤速度。

以下动画展示了如何为 Stack Overflow 帖子表创建一个优化的稀疏主索引。索引不是针对单独的行,而是针对行块:

如果在具有此排序键的表上重复相同的查询:

此查询现在利用稀疏索引,显著减少了读取的数据量,并将执行时间提高了 4 倍 - 请注意读取的行数和字节的减少。

使用 EXPLAIN indexes=1 可以确认索引的使用。

此外,我们可以可视化稀疏索引如何修剪所有不能包含匹配项的行块:

备注

表中所有列将根据指定排序键的值进行排序,无论它们是否包含在键中。例如,如果将 CreationDate 用作键,则所有其他列中的值的顺序将与 CreationDate 列中的值的顺序相对应。可以指定多个排序键 - 这将与 SELECT 查询中的 ORDER BY 子句具有相同的语义进行排序。

关于选择主键的完整高级指南可以在 这里 找到。

有关排序键如何改善压缩并进一步优化存储的更深入见解,请探索有关 ClickHouse 中的压缩列压缩编解码器 的官方指南。