ClickHouse + Graphite:如何显着减少磁盘空间消耗

ClickHouse + Graphite:如何显着减少磁盘空间消耗

你好,哈布尔。

如果有人利用该系统 石墨网 并遇到存储性能问题 耳语 (IO、磁盘空间消耗),那么ClickHouse被替代的几率应该趋于XNUMX。 此声明意味着第三方实现已被用作接收指标的守护进程,例如 复写机 или 碳纤维.

ClickHouse很好地解决了所描述的问题。 例如,从 Whisper 传输 2TiB 数据后,它们可容纳 300GiB。 我不会详细讨论这种比较;关于这个主题的文章有很多。 此外,直到最近,我们的 ClickHouse 存储并不是一切都很完美。

空间消耗问题

乍一看,一切都应该运行良好。 下列的 文件资料,为指标存储方案创建一个配置(进一步 retention),然后根据为graphite-web选择的后端的推荐创建一个表: 碳点击屋+石墨点击屋 или 图形库,取决于使用哪个堆栈。 然后……定时炸弹爆炸了。

为了了解哪一个,您需要了解插入是如何工作的以及*系列引擎表中数据的进一步生命路径合并树 ClickHouse(图表取自 简报 阿列克谢·扎特莱平):

  • 插入 блок 数据。 在我们的例子中,是到达的指标。
    ClickHouse + Graphite:如何显着减少磁盘空间消耗
  • 每个这样的块在写入磁盘之前都会根据键进行排序。 ORDER BY创建表时指定。
  • 排序后, кусок (part)数据写入磁盘。
    ClickHouse + Graphite:如何显着减少磁盘空间消耗
  • 服务器在后台监控,这样这样的碎片就不会很多,并启动后台 слияния (merge,以下合并)。
    ClickHouse + Graphite:如何显着减少磁盘空间消耗
    ClickHouse + Graphite:如何显着减少磁盘空间消耗
  • 一旦数据停止主动流入服务器,服务器就会停止自行运行合并 партицию (partition),但您可以使用以下命令手动启动该进程 OPTIMIZE.
  • 如果分区中只剩下一块,那么您将无法使用通常的命令运行合并;您必须使用 OPTIMIZE ... FINAL

因此,第一个指标出现了。 它们占用了一些空间。 随后的事件可能会因多种因素而有所不同:

  • 分区键可以非常小(一天)或非常大(几个月)。
  • 保留配置可能适合活动分区(其中记录指标)内的几个重要数据聚合阈值,也可能不适合。
  • 如果有大量数据,那么最早的块(由于后台合并可能已经很大了(如果您选择非最佳分区键))将不会与新的小块合并。

而且结局总是一样的。 ClickHouse 中指标占用的空间仅在以下情况下增加:

  • 请勿应用 OPTIMIZE ... FINAL 手动或
  • 不要持续将数据插入所有分区,这样后台合并迟早会开始

第二种方法似乎是最容易实现的,因此它是不正确的,因此首先尝试过。
我编写了一个相当简单的 python 脚本,该脚本在过去 4 年里每天发送虚拟指标,并每小时运行一次 cron。
由于ClickHouse DBMS的整个操作是建立在这个系统迟早会完成所有后台工作的基础上的,但不知道什么时候,我无法等待旧的大块开始合并的那一刻新的小。 很明显,我们需要寻找一种自动执行强制优化的方法。

ClickHouse + Graphite:如何显着减少磁盘空间消耗

ClickHouse系统表中的信息

我们看一下表结构 系统零件。 这是有关 ClickHouse 服务器上所有表的每个部分的综合信息。 除其他外,包含以下列:

  • 数据库名称(database);
  • 表名(table);
  • 分区名称和 ID (partition & partition_id);
  • 当这件作品被创作时(modification_time);
  • 片段中的最小和最大日期(按天进行分区)(min_date & max_date);

还有一张桌子 系统.graphite_retentions,具有以下有趣的领域:

  • 数据库名称(Tables.database);
  • 表名(Tables.table);
  • 应应用下一次聚合的指标年龄(age);

所以:

  1. 我们有一个块表和一个聚合规则表。
  2. 我们组合它们的交集并得到所有表 *GraphiteMergeTree。
  3. 我们正在寻找其中的所有分区:
    • 多于一件
    • 或者已经到了应用下一个聚合规则的时候了,并且 modification_time 比这一刻更老。

履行

这个请求

SELECT
    concat(p.database, '.', p.table) AS table,
    p.partition_id AS partition_id,
    p.partition AS partition,
    -- Самое "старое" правило, которое может быть применено для
    -- партиции, но не в будущем, см (*)
    max(g.age) AS age,
    -- Количество кусков в партиции
    countDistinct(p.name) AS parts,
    -- За самую старшую метрику в партиции принимается 00:00:00 следующего дня
    toDateTime(max(p.max_date + 1)) AS max_time,
    -- Когда партиция должна быть оптимизированна
    max_time + age AS rollup_time,
    -- Когда самый старый кусок в партиции был обновлён
    min(p.modification_time) AS modified_at
FROM system.parts AS p
INNER JOIN
(
    -- Все правила для всех таблиц *GraphiteMergeTree
    SELECT
        Tables.database AS database,
        Tables.table AS table,
        age
    FROM system.graphite_retentions
    ARRAY JOIN Tables
    GROUP BY
        database,
        table,
        age
) AS g ON
    (p.table = g.table)
    AND (p.database = g.database)
WHERE
    -- Только активные куски
    p.active
    -- (*) И только строки, где правила аггрегации уже должны быть применены
    AND ((toDateTime(p.max_date + 1) + g.age) < now())
GROUP BY
    table,
    partition
HAVING
    -- Только партиции, которые младше момента оптимизации
    (modified_at < rollup_time)
    -- Или с несколькими кусками
    OR (parts > 1)
ORDER BY
    table ASC,
    partition ASC,
    age ASC

返回每个 *GraphiteMergeTree 表分区,其合并应释放磁盘空间。 剩下要做的唯一一件事就是通过一个请求来检查所有这些 OPTIMIZE ... FINAL。 最终的实现还考虑到不需要接触活动记录的分区这一事实。

这正是该项目所做的 石墨通道优化器。 Yandex.Market 的前同事在生产中进行了尝试,工作结果如下所示。

ClickHouse + Graphite:如何显着减少磁盘空间消耗

如果您在带有 ClickHouse 的服务器上运行该程序,它将简单地开始以守护进程模式工作。 每小时执行一次请求,检查是否出现了超过三天的可以优化的新分区。

我们近期的计划是至少提供 deb 软件包,如果可能的话还提供 rpm。

取而代之的是结论

在过去的 9 个多月里,我一直在我的公司里 InnoGames公司 花了很多时间修补 ClickHouse 和石墨网络的交叉点。 这是一次很好的体验,导致我们从 Whisper 快速过渡到 ClickHouse 作为指标存储。 我希望这篇文章是关于我们对该堆栈的各个部分所做的改进以及将来将要做的事情的系列文章的开始。

开发该请求花费了几升啤酒和管理时间,以及 v0恶魔,为此我想向他表达我的谢意。 也为了评论这篇文章。

github 上的项目页面

来源: habr.com

添加评论