> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-8c05c8a2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 有効期限（TTL）の管理

> ClickStackでの有効期限（TTL）の管理

export const Image = ({img, alt, size}) => {
  return <Frame>
      <img src={img} alt={alt} />
    </Frame>;
};

<div id="ttl-clickstack">
  ## ClickStack における TTL
</div>

Time-to-Live (TTL) は、ClickStack における効率的なデータ保持と管理を支える重要な機能です。特に、大量のデータが継続的に生成される環境では欠かせません。TTL を使用すると、古いデータの有効期限切れと削除を自動化できるため、手動で介入しなくてもストレージを最適に活用し、パフォーマンスを維持できます。この機能は、データベースをスリムに保ち、ストレージコストを削減し、最も関連性が高く新しいデータに絞ってクエリを実行できるようにするうえで不可欠です。さらに、データライフサイクルを体系的に管理することでデータ保持ポリシーへの準拠にも役立ち、結果としてオブザーバビリティソリューション全体の持続可能性とスケーラビリティを高めます。

**デフォルトでは、ClickStack はデータを 3 日間保持します。これを変更するには、["Modifying TTL"](#modifying-ttl) を参照してください。**

TTL は ClickHouse ではテーブルレベルで制御されます。たとえば、ログのデフォルトスキーマを以下に示します。collector がテーブルを作成する際、`${TABLES_TTL}` は設定された保持期間 (変更していない場合は 3 日) に置き換えられます。

```sql theme={null}
CREATE TABLE IF NOT EXISTS ${DATABASE}.otel_logs
(
  `Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
  `TraceId` String CODEC(ZSTD(1)),
  `SpanId` String CODEC(ZSTD(1)),
  `TraceFlags` UInt8,
  `SeverityText` LowCardinality(String) CODEC(ZSTD(1)),
  `SeverityNumber` UInt8,
  `ServiceName` LowCardinality(String) CODEC(ZSTD(1)),
  `Body` String CODEC(ZSTD(1)),
  `ResourceSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
  `ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
  `ScopeSchemaUrl` LowCardinality(String) CODEC(ZSTD(1)),
  `ScopeName` String CODEC(ZSTD(1)),
  `ScopeVersion` LowCardinality(String) CODEC(ZSTD(1)),
  `ScopeAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
  `LogAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
  `EventName` String CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.cluster.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.cluster.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.container.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.container.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.deployment.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.deployment.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.namespace.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.namespace.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.node.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.node.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.pod.name` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.pod.name'] CODEC(ZSTD(1)),
  `__hdx_materialized_k8s.pod.uid` LowCardinality(String) MATERIALIZED ResourceAttributes['k8s.pod.uid'] CODEC(ZSTD(1)),
  `__hdx_materialized_deployment.environment.name` LowCardinality(String) MATERIALIZED ResourceAttributes['deployment.environment.name'] CODEC(ZSTD(1)),
  `ResourceAttributeItems` Array(String) ALIAS arrayMap((arr) -> concat(arr.1, '=', arr.2), ResourceAttributes::Array(Tuple(String, String))),
  `ScopeAttributeItems` Array(String) ALIAS arrayMap((arr) -> concat(arr.1, '=', arr.2), ScopeAttributes::Array(Tuple(String, String))),
  `LogAttributeItems` Array(String) ALIAS arrayMap((arr) -> concat(arr.1, '=', arr.2), LogAttributes::Array(Tuple(String, String))),
  INDEX idx_trace_id TraceId TYPE text(tokenizer = 'array'),
  INDEX idx_res_attr_key mapKeys(ResourceAttributes) TYPE text(tokenizer = 'array'),
  INDEX idx_res_attr_items ResourceAttributeItems TYPE text(tokenizer = 'array'),
  INDEX idx_scope_attr_key mapKeys(ScopeAttributes) TYPE text(tokenizer = 'array'),
  INDEX idx_scope_attr_items ScopeAttributeItems TYPE text(tokenizer = 'array'),
  INDEX idx_log_attr_key mapKeys(LogAttributes) TYPE text(tokenizer = 'array'),
  INDEX idx_log_attr_items LogAttributeItems TYPE text(tokenizer = 'array'),
  INDEX idx_lower_body lower(Body) TYPE text(tokenizer = 'splitByNonAlpha')
)
ENGINE = MergeTree
PARTITION BY toDate(Timestamp)
ORDER BY (toStartOfFiveMinutes(Timestamp), ServiceName, Timestamp)
TTL toDateTime(Timestamp) + ${TABLES_TTL}
SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, enable_block_number_column = 1, enable_block_offset_column = 1;
```

ClickHouse のパーティション化では、カラムまたは SQL 式に基づいて、データをディスク上で論理的に分離できます。データを論理的に分離することで、各パーティションを独立して操作できるようになり、たとえば TTL ポリシーに従って期限切れになった際に削除できます。

上記の例にあるように、パーティション化はテーブルを最初に定義する際に `PARTITION BY` 句で指定します。この句には任意のカラムに対する SQL 式を含めることができ、その結果によって行が送られるパーティションが決まります。これにより、データはディスク上で各パーティションと論理的に関連付けられ (共通のフォルダ名プレフィックスを介して)、その後は個別にクエリできます。上記の例では、デフォルトの `otel_logs` スキーマは `toDate(Timestamp)` 式を使って日単位でパーティション化します。行が ClickHouse に挿入されると、この式が各行に対して評価され、対応するパーティションが存在すればそこに振り分けられます (その日で最初の行であれば、パーティションが作成されます)。パーティション化とそのほかの用途の詳細については、["Table Partitions"](/ja/concepts/core-concepts/partitions) を参照してください。

<Image img="https://mintcdn.com/private-7c7dfe99-mintlify-8c05c8a2/6-CMW43ytOARd9iS/images/use-cases/observability/observability-14.png?fit=max&auto=format&n=6-CMW43ytOARd9iS&q=85&s=da6cc23c3d51eb2bc3c0f9d7bb951bf2" alt="Partitions" size="lg" width="1600" height="1077" data-path="images/use-cases/observability/observability-14.png" />

テーブルのスキーマには、`TTL toDateTime(Timestamp) + ${TABLES_TTL}` と設定 `ttl_only_drop_parts = 1` も含まれています。前者の句により、データは設定された TTL (デフォルトでは 3 日) を超えると削除されます。設定 `ttl_only_drop_parts = 1` は、行を部分的に削除しようとするのではなく、すべてのデータが期限切れになったデータパーツのみを削除するようにします。さらに、パーティション化によって別々の日のデータが決して「マージ」されないため、データを効率的に削除できます。

<Warning>
  **`ttl_only_drop_parts`**

  設定 [`ttl_only_drop_parts=1`](/ja/reference/settings/merge-tree-settings#ttl_only_drop_parts) は常に使用することを推奨します。この設定を有効にすると、ClickHouse は、その中のすべての行が期限切れになった時点でパーツ全体を削除します。TTL で期限切れになった行を部分的にクリーンアップする代わりにパーツ全体を削除することで (`ttl_only_drop_parts=0` の場合は、リソース負荷の高い mutation によって実行されます) 、`merge_with_ttl_timeout` を短く設定でき、システム性能への影響も抑えられます。TTL の期限切れを実行する単位 (たとえば日) と同じ単位でデータをパーティション化していれば、パーツには自然にその期間のデータだけが含まれるようになります。これにより、`ttl_only_drop_parts=1` を効率的に適用できます。
</Warning>

デフォルトでは、有効期限 (TTL) が切れたデータは、ClickHouse が[データパーツをマージする](/ja/reference/engines/table-engines/mergetree-family/mergetree#mergetree-data-storage)ときに削除されます。ClickHouse はデータの期限切れを検出すると、予定外のマージを実行します。

<Info>
  **TTL スケジュール**

  前述のとおり、TTL は即座には適用されず、スケジュールに従って適用されます。MergeTree テーブル設定 `merge_with_ttl_timeout` は、削除 TTL を伴うマージを再実行するまでの最小遅延時間 (秒) を設定します。デフォルト値は 14400 秒 (4 時間) です。ただし、これはあくまで最小遅延時間であり、TTL マージがトリガーされるまでにはさらに時間がかかる場合があります。値が低すぎると、リソースを大量に消費する予定外のマージが多数実行される可能性があります。TTL の期限切れは、コマンド `ALTER TABLE my_table MATERIALIZE TTL` を使って強制できます。
</Info>

<div id="modifying-ttl">
  ## 有効期限 (TTL) の変更
</div>

有効期限 (TTL) を変更するには、次のいずれかの方法を使用します。

1. **テーブルのスキーマを変更する (推奨) **。この方法では、たとえば [clickhouse-client](/ja/concepts/features/interfaces/cli) または [Cloud SQL Console](/ja/products/cloud/features/sql-console-features/sql-console) を使って ClickHouse インスタンスに接続する必要があります。たとえば、次の DDL を使用して `otel_logs` テーブルの 有効期限 (TTL) を変更できます。

```sql theme={null}
ALTER TABLE default.otel_logs
MODIFY TTL TimestampTime + toIntervalDay(7);
```

2. **OTel collectorを変更します**。ClickStack OpenTelemetry collector は、ClickHouse にテーブルが存在しない場合、自動的に作成します。これは ClickHouse エクスポーターによって実現されており、このエクスポーターではデフォルトの 有効期限 (TTL) 式を制御するための `ttl` パラメーターも公開されています。例:

```yaml theme={null}
exporters:
 clickhouse:
   endpoint: tcp://localhost:9000?dial_timeout=10s&compress=lz4&async_insert=1
   ttl: 72h
```

<div id="column-level-ttl">
  ### カラムレベルの有効期限 (TTL)
</div>

上記の例では、テーブルレベルでデータを期限切れにしています。カラムレベルでデータを期限切れにすることもできます。データの経過に応じて、調査での利用価値が保持に伴うリソースのオーバーヘッドに見合わないカラムを削除する目的で、これを利用できます。たとえば、insert time にまだ抽出されていない新しい動的メタデータが追加される可能性に備えて、`Body` カラムは保持しておくことを推奨します。たとえば、新しい Kubernetes ラベルなどです。一定期間、たとえば 1 か月が経過すると、この追加メタデータが有用でないことが明らかになる場合があり、その場合 `Body` カラムを保持する価値は低くなります。

以下では、`Body` カラムを 30 日後に削除する方法を示します。

```sql theme={null}
CREATE TABLE otel_logs_v2
(
        `Body` String TTL Timestamp + INTERVAL 30 DAY,
        `Timestamp` DateTime,
 ...
)
ENGINE = MergeTree
ORDER BY (ServiceName, Timestamp)
```

<Note>
  カラムレベルの有効期限 (TTL) を指定するには、独自のスキーマを定義する必要があります。これは OTel collector では指定できません。
</Note>
