- 提升查询性能,尤其是在配合
JOIN使用时 - 在不拖慢摄取过程的情况下,实时富化摄取的数据
使用字典加速 JOIN
JOIN:LEFT ANY 类型。在这种情况下,连接键需要与底层键值存储的键属性相匹配。
如果满足这一条件,ClickHouse 就可以利用字典执行 Direct Join。这是 ClickHouse 最快的 JOIN 算法,适用于右侧表所使用的底层表引擎支持低延迟键值请求的场景。ClickHouse 有三种表引擎具备这一能力:Join (本质上是一个预先计算好的哈希表) 、EmbeddedRocksDB 和 Dictionary。这里我们将介绍基于字典的方法,不过这三种引擎的实现机制是相同的。
直接 JOIN 算法要求右侧表以字典为底层支撑,这样,待连接的数据就已经以内存中的低延迟键值数据结构形式存在。
示例
posts 和 votes 表执行 JOIN:
在虽然这个查询速度很快,但要获得良好性能,仍然依赖于我们谨慎地编写JOIN右侧使用更小的数据集:这个查询看起来似乎比实际需要的更冗长,因为外层查询和子查询中都对PostId进行了过滤。这是一种性能优化,可确保查询响应速度足够快。为了获得最佳性能,务必确保JOIN的右侧是较小的数据集,而且越小越好。有关优化 JOIN 性能以及了解可用算法的技巧,我们推荐阅读这一系列博客文章。
JOIN。理想情况下,我们只需先将 posts 过滤为包含 “SQL” 的记录,然后再查看这部分博客子集的 UpVote 和 DownVote 计数,以计算相应指标。
使用字典
votes 表的大小:
在下面的示例中,字典的数据源自 ClickHouse 表。虽然这是字典最常见的数据来源,但也支持多种来源,包括文件、http 以及数据库 (如 Postgres) 。正如我们将展示的,字典可以自动刷新,因此非常适合用来确保经常变动的小型数据集可用于直接 JOIN。我们的字典需要一个用于执行查找的主键。从概念上讲,这与事务型数据库中的主键完全相同,并且应当唯一。上面的查询需要基于连接键
PostId 进行查找。相应地,字典中应填充 votes 表里每个 PostId 的赞成票和反对票总数。下面是获取该字典数据的查询:
在自管理 OSS 中,上述命令需要在所有节点上执行。在 ClickHouse Cloud 中,字典会自动复制到所有节点。上述操作是在一台配备 64GB RAM 的 ClickHouse Cloud 节点上执行的,加载耗时 36 秒。要确认字典占用的内存:
dictGet 函数,即可获取特定 PostId 的赞成票和反对票。下面我们来获取帖子 11227902 的值:
查询时富集
索引时富集
Location 永远不会变化 (实际上会变化) ——具体来说,是 users 表中的 Location 列。假设我们希望按位置对 posts 表执行分析查询。该表中包含一个 UserId。
字典提供了从用户 id 到位置的映射,底层由 users 表提供支持:
我们排除了为了在 posts 表的写入时利用该字典,我们需要修改 schema:Id < 0的用户,因此可以使用Hashed字典类型。Id < 0的用户属于系统用户。
Location 被声明为 MATERIALIZED 列。这意味着该值可以在 INSERT 查询中提供,但始终会被计算。
ClickHouse 也支持 DEFAULT 列 (即可以插入该值;如果未提供,也会自动计算) 。
要填充该表,我们可以像平常一样使用从 S3 执行的 INSERT INTO SELECT:
字典高级主题
刷新字典
LIFETIME 为 MIN 600 MAX 900。LIFETIME 是字典的更新间隔,此处的取值会使字典每隔 600 到 900 秒以随机间隔定期重新加载。之所以需要这种随机间隔,是为了在大量服务器更新时分散字典源上的负载。在更新期间,仍然可以查询字典的旧版本,只有初始加载会阻塞查询。请注意,设置 (LIFETIME(0)) 会阻止字典更新。
可以使用 SYSTEM RELOAD DICTIONARY 命令强制重新加载字典。
对于 ClickHouse 和 Postgres 等数据库源,你可以设置一个查询,使字典仅在确实发生变化时才更新 (由该查询的响应决定) ,而不是按固定周期更新。更多详细信息请参见此处。