此博文是 加速大型 ClickHouse 数据加载
系列的一部分
前往赛道
在我们关于加速大型数据加载的三篇博文系列的第二部分中,我们将把第一篇博文中的理论付诸实践,演示如何使用相同的硬件,以比默认设置快 3 倍的速度运行大型数十亿行插入。我们概述了一个公式,您可以使用该公式来确定最适合您的摄取用例的块大小/并行度比率。最后,我们将通过利用 ClickHouse Cloud 的新型 SharedMergeTree 表引擎与简单的水平集群扩展相结合来打破速度限制。
请注意,我们在此仅演示了一种调整场景,其中数据由 ClickHouse 服务器本身拉取。您可以在此处阅读有关客户端将数据推送到 ClickHouse 的设置。
挑战
将 ClickHouse 用于大型数据加载就像驾驶高性能一级方程式赛车 🏎 一样。可以获得大量的原始马力,并且您可以为大型数据加载达到最高速度。但是,要实现最大的摄取性能,您必须根据 (3) 可用马力的具体数量(CPU 核心和 RAM)选择 (1) 足够高的档位(插入块大小)和 (2) 适当的加速级别(插入并行度)。
理想情况下,我们希望以最高档位和全速加速驾驶我们的赛车
- 最高档位:我们配置的插入块大小越大,ClickHouse 必须创建的分区就越少,并且需要的磁盘文件 i/o 和后台合并就越少。
- 全速加速:我们配置的并行插入线程数越高,数据处理速度就越快。
但是,这两个性能因素之间存在冲突的权衡(以及与后台分区合并的权衡)。ClickHouse 服务器的可用主内存量是有限的。较大的块使用更多主内存,这限制了我们可以利用的并行插入线程数。相反,较高数量的并行插入线程需要更多主内存,因为插入线程数决定了并发创建在内存中的插入块数。这限制了插入块的可能大小。此外,插入线程和后台合并线程之间可能存在资源争用。配置的插入线程数高 (1) 会创建更多需要合并的分区,并且 (2) 会占用后台合并线程的 CPU 核心和内存空间。
我们的挑战是找到一个最佳平衡点,即足够大的块大小和足够多的并行插入线程,以达到最佳的插入速度。
实验
在以下部分中,我们将测试这些块大小/并行度设置,并评估它们对摄取速度的影响。
测试数据
我们实验使用的数据来自 PyPi 数据集(托管在 GCS 存储桶中)。它由 653.3 亿行组成,原始未压缩大小约为 14 TiB。磁盘上压缩后的数据大小(存储在 ClickHouse 表中时)约为 1.2 TiB。
测试表和插入查询
对于这些测试,我们将以尽可能快的速度将此数据摄取到 ClickHouse Cloud 服务中的表中,该服务由 3 台 ClickHouse 服务器组成,每台服务器具有 59 个 CPU 核心和 236 GiB 的 RAM。我们将结合 s3Cluster 集成表函数(与 gcs 兼容)使用 INSERT INTO SELECT 查询。
我们将首先使用 插入块大小和 插入线程的默认设置来摄取我们的大型数据集,然后将使用不同的数字重复此加载,以评估对加载时间的影响。
用于内省性能指标的查询
我们正在使用以下 SQL 查询通过 系统表来内省在我们的测试结果中可视化的插入性能指标
换挡
为了说明插入块大小如何影响创建(以及需要合并)的分区数量,我们在下面可视化了各种插入块大小(以行数为单位)与单个数据块的(未压缩)平均内存大小及其相应的(压缩)单个数据分区的平均磁盘大小。这与写入存储的平均分区总数叠加在一起。请注意,我们给出了平均值,因为正如在第一篇博文中提到的那样,创建的块和分区很少精确地包含配置的行数或字节数,因为 ClickHouse 是流式传输和按行-块方式处理数据的。因此,这些设置指定了最小阈值。
另请注意,我们从下一节中不同摄取测试运行的结果中获取了这些数字。
您可以看到,我们配置的插入块大小越大,存储测试数据(~1.2 TiB 压缩表数据)需要创建(和合并)的分区就越少。例如,使用 100 万行的插入块大小,将在磁盘上创建 65,000 个初始分区(平均压缩大小为 20 MiB)。而使用 8000 万行的插入块大小,仅创建 817 个初始分区(平均压缩大小为 1.5 GiB)。
标准速度
以下图表可视化了默认插入块大小为约 100 万行但不同插入线程数的摄取运行结果。我们显示了达到的峰值内存使用量以及分区数达到 3000(建议的用于高效查询的数量)的所需时间: 使用 ClickHouse Cloud 的默认设置 4 个摄取线程(OSS 中的默认设置是没有并行摄取线程),每台服务器消耗的主内存峰值为 7 GiB。从摄取开始到最初创建的 65,000 个分区合并为健康的少于 3,000 个分区的数量,需要 4 个多小时。凭借其默认设置,ClickHouse 试图为支持来自并发用户的并发任务提供良好的资源使用平衡。但是,一种尺寸并不适合所有人。我们在这里的目标是调整仅供我们用于一个目的的专用服务:实现插入数据集的最高速度。
如果我们使利用的并行插入线程数与 59 个可用 CPU 核心的数量相匹配,则与较低的线程设置相比,达到 3k 个活动分区的时间会增加。
使用 16 个插入线程可实现最短时间,为与数据插入同时运行的后台分区合并线程留出了充足的 59 个 CPU 核心。
请注意,上图中的蓝色水平条表示每台服务器的可用内存(236 GiB RAM)。我们可以看到,分别有很大的空间来增加插入块大小和内存使用量(上图中的蓝色垂直条)。
目前,我们正在以全速(最多 59 个插入线程)在非常低的档位(每个初始分区仅 100 万行)踩下加速踏板,从而导致整体摄取速度较低。我们现在将在下一节中找到更好的档位和加速组合。
最高速度
我们尝试通过运行针对这些插入速度因素的不同系统组合的摄取来确定插入块大小和插入线程数的完美组合。下图可视化了这些摄取测试运行的结果,其中我们将插入块大小翻倍了几次(从100 万到500 万到1000 万到2000 万到4000 万,直到8000 万)
对于每个块大小,我们将插入线程数翻倍(从 4 开始),直到峰值内存使用量(蓝色垂直条)几乎达到每台服务器的可用内存(蓝色水平条)。黄色圆圈表示从摄取开始到所有最初创建的分区合并为健康的少于 3000 个分区的数量的时间。
当使用 32 个并行插入线程和 1000 万行的插入块大小摄取我们数据集的 653.3 亿行时,我们达到了最高速度。这是我们服务器大小(59 个 CPU 核心和 236 GiB RAM)的最佳平衡点。这导致了适量的6500 个初始分区,这些分区可以由空闲的 27 个 CPU 核心在后台快速合并为少于 3000 个分区(32 个核心用于并行读取和摄取数据)。使用 16 个或更少的插入线程,插入并行度太低。使用 59 个插入线程,1000 万行的块大小不适合 RAM。
500 万行的块大小适用于 59 个插入线程。但是,它仍然会创建13,000 个初始分区,而没有为高效的后台合并留下任何专用 CPU 核心,从而导致资源争用和达到 3000 个活动分区的长时间。使用 32 个和 16 个插入线程,数据处理速度较慢,但合并速度更快,因为有更多专用 CPU 核心可用于后台合并。4 个和 8 个插入线程在 500 万行的块大小下没有创建足够的插入并行度。
2000 万行及以上的插入块大小会导致少量初始分区,但高内存开销将插入限制为少量插入线程,从而创建不足的插入并行度。
使用我们确定的最佳平衡点设置,我们将摄取速度提高了近 3 倍。我们使用了完全相同的底层硬件,但我们更有效地利用它来进行数据摄取,而不会通过以半力(32 个插入线程)踩下加速踏板并以尽可能高的档位(1000 万行块大小)创建过多的初始分区。
一级方程式
我们知道,通过多次测试运行来确定大型数十亿行插入的最佳设置是不切实际的。因此,我们为您提供此方便的公式,用于大致计算大型数据加载的最高速度设置
•
max_insert_threads
:选择约一半的可用 CPU 核心用于插入线程(为后台合并留下足够的专用核心)
• peak_memory_usage_in_bytes
:选择预期的峰值内存使用量;所有可用 RAM(如果是隔离摄取)或一半或更少(为其他并发任务留出空间)
然后min_insert_block_size_bytes
= peak_memory_usage_in_bytes
/ (~3 * max_insert_threads
)
使用此公式,您可以将 min_insert_block_size_rows
设置为 0(以禁用基于行的阈值),同时将 max_insert_threads
设置为选定的值,并将 min_insert_block_size_bytes
设置为上述公式的计算结果。
使用 ClickHouse Cloud 打破速度限制
使摄取速度更快的下一个措施是为我们的引擎投入更多马力,并通过添加额外的服务器来增加可用 CPU 核心的数量。
结合其SharedMergeTree 表引擎,ClickHouse Cloud 允许您自由地(并且快速地!)更改现有服务器的大小(CPU 和 RAM)或添加额外的服务器。我们对我们的服务进行了后者,并使用不同数量的 ClickHouse 服务器(每个服务器具有 59 个 CPU 核心和 236 GB RAM)摄取了我们的数据集。此图表显示了结果: 对于每个摄取测试运行,我们使用了在上面部分中确定的最佳调整设置(32 个并行插入线程和 1000 万行的插入块大小)。
我们可以看到,摄取吞吐量与 CPU 核心数和 ClickHouse 服务器数分别呈完美的线性关系。这使我们能够以您想要的任何速度运行大型数据插入。
它可以像您想要的速度一样快
ClickHouse Cloud 允许您分别通过额外的 CPU 核心和服务器线性扩展摄取持续时间。因此,您可以根据需要快速运行大型摄取。下图可视化了这一点: 单个 ClickHouse Cloud 服务器(具有 59 个 CPU 核心和 236 GB RAM)以每秒约 400 万行的吞吐量在约 240 分钟内摄取了我们 650 亿行的数据集。根据我们在上面证明线性可扩展性的摄取测试运行,我们预测了通过额外的服务器可以如何提高吞吐量和摄取时间。
总结
在我们由三部分组成的博文系列的第二部分中,我们给出了指导并演示了如何调整主要的插入性能因素,以大幅加快大型数十亿行插入的速度。在相同的硬件上,最高摄取速度比默认设置快近 3 倍。此外,我们利用 ClickHouse Cloud 的无缝集群扩展使摄取速度更快,并说明了如何通过扩展节点以您想要的任何速度运行大型数据插入。
我们希望您学到了一些加速大型 ClickHouse 数据加载的新方法。
在本系列的下一篇也是最后一篇博文中,我们将演示如何长期增量且可靠地加载大型数据集。
敬请期待!