こんにちは、ハブル。
誰かがシステムを悪用した場合
ClickHouse は、上記の問題をうまく解決します。たとえば、ウィスパーから 2TiB のデータを転送すると、300GiB に収まります。この比較については詳しく説明しません。このトピックに関する記事はたくさんあります。さらに、最近まで、ClickHouse ストレージはすべてが完璧だったわけではありません。
消費スペースの問題
一見すると、すべてがうまく機能するはずです。続く retention
)、次に、Graphite-Web 用に選択したバックエンドの推奨に従ってテーブルを作成します。
どちらかを理解するには、挿入がどのように機能するか、および * ファミリーのエンジンのテーブル内のデータのその後のライフパスを知る必要があります。マージツリー ClickHouse (チャートは以下から引用)
- 挿入されました
блок
データ。私たちの場合、到着したのはメトリクスでした。
- このような各ブロックは、ディスクに書き込まれる前にキーに従ってソートされます。
ORDER BY
テーブル作成時に指定します。 - 並べ替えた後、
кусок
(part
) データがディスクに書き込まれます。
- サーバーはそのような部分が多くならないようにバックグラウンドで監視し、バックグラウンドで起動します。
слияния
(merge
、以下マージします)。
- データがアクティブに流れなくなると、サーバーは自動的にマージの実行を停止します。
партицию
(partition
)、ただし、次のコマンドを使用してプロセスを手動で開始できます。OPTIMIZE
. - パーティションに XNUMX つの部分しか残っていない場合、通常のコマンドを使用してマージを実行することはできません。
OPTIMIZE ... FINAL
したがって、最初のメトリクスが到着します。そして、それらはある程度のスペースを占有します。その後のイベントは、多くの要因に応じて多少異なる場合があります。
- パーティション化キーは、非常に小さい (XNUMX 日) ことも、非常に大きい (数か月) こともできます。
- 保持設定は、アクティブ パーティション (メトリックが記録される場所) 内のいくつかの重要なデータ集約しきい値に適合する場合もあれば、適合しない場合もあります。
- データが大量にある場合、バックグラウンドでのマージにより (最適でないパーティション化キーを選択した場合) 最も初期のチャンクがすでに巨大になっている可能性があり、新しい小さなチャンクとマージされません。
そしてそれはいつも同じ終わり方をします。 ClickHouse のメトリクスが占めるスペースは、次の場合にのみ増加します。
- 適用しないでください
OPTIMIZE ... FINAL
手動または - 遅かれ早かれバックグラウンドでマージが開始されるよう、すべてのパーティションに継続的にデータを挿入しないでください。
XNUMX 番目の方法は実装が最も簡単と思われるため、間違っており、最初に試行されました。
私は過去 4 年間、毎日ダミーのメトリクスを送信し、XNUMX 時間ごとに cron を実行する、かなり単純な Python スクリプトを作成しました。
ClickHouse DBMS の操作全体は、このシステムが遅かれ早かれすべてのバックグラウンド作業を実行するという事実に基づいているため、いつになるかはわかりませんが、古い巨大な部分が結合し始める瞬間を待つことができませんでした。新しい小さいもの。強制的な最適化を自動化する方法を探す必要があることが明らかになりました。
ClickHouse システム テーブルの情報
テーブル構造を見てみましょう
- データベース名 (
database
); - テーブル名 (
table
); - パーティション名とID (
partition
&partition_id
); - 作品が作成されたとき(
modification_time
); - ピース内の最小および最大の日付 (分割は日ごとに行われます) (
min_date
&max_date
);
テーブルもあります
- データベース名 (
Tables.database
); - テーブル名 (
Tables.table
); - 次の集計を適用する必要があるメトリックの経過時間 (
age
);
だから:
- チャンクのテーブルと集計ルールのテーブルがあります。
- それらの交差部分を結合して、すべてのテーブル *GraphiteMergeTree を取得します。
- 次のようなすべてのパーティションを探しています。
- XNUMX枚以上
- または、次の集計ルールを適用する時期が来た、および
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
。最終的な実装では、アクティブな記録でパーティションに触れる必要がないという事実も考慮されています。
これはまさにこのプロジェクトが行うことです
ClickHouse を使用してサーバー上でプログラムを実行すると、デーモン モードで動作し始めます。 XNUMX 時間に XNUMX 回リクエストが実行され、XNUMX 日以上経過した最適化可能な新しいパーティションが出現したかどうかがチェックされます。
私たちの当面の計画は、少なくとも deb パッケージを提供し、可能であれば rpm も提供することです。
代わりに、結論の
過去 9 か月以上にわたり、私は社内にいた
リクエストの作成には数リットルのビールと管理者の日数が費やされました。
出所: habr.com