ClickHouse/docs/ja/guides/developer/deduplicating-inserts-on-retries.md

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

398 lines
13 KiB
Markdown
Raw Normal View History

2024-11-18 02:58:58 +00:00
---
slug: /ja/guides/developer/deduplicating-inserts-on-retries
title: 挿入操作の再試行時のデータ重複防止
description: 挿入操作を再試行する際の重複データ防止策
keywords: [重複排除, デデュプリケート, 挿入再試行, 挿入]
---
挿入操作は、タイムアウトなどのエラーにより失敗することがあります。挿入が失敗した場合、データが成功裏に挿入されたかどうかは不明です。本ガイドでは、挿入再試行時に同じデータが複数回挿入されないように、データ重複防止機能を有効にする方法を説明します。
挿入が再試行されると、ClickHouseはデータが既に成功裏に挿入されたかどうかを判断しようとします。挿入されたデータが重複としてマークされる場合、ClickHouseはそれを対象のテーブルに挿入しません。しかし、ユーザーはあたかもデータが正常に挿入されたかのように成功操作ステータスを受け取ります。
## 再試行時の挿入重複防止の有効化
### テーブルに対する挿入重複防止
**`*MergeTree`エンジンのみが挿入時の重複防止をサポートしています。**
`*ReplicatedMergeTree`エンジンに対しては、挿入重複防止はデフォルトで有効になっており、`replicated_deduplication_window`および`replicated_deduplication_window_seconds`設定によって制御されます。非レプリケートされた`*MergeTree`エンジンに対しては、重複防止は`non_replicated_deduplication_window`設定によって制御されます。
上記の設定は、テーブルの重複防止ログのパラメータを決定します。この重複防止ログは有限の数の`block_id`を保持し、これにより重複防止の仕組みが決まります(以下参照)。
### クエリレベルでの挿入重複防止
設定`insert_deduplicate=1`を用いると、クエリレベルでの重複防止が有効になります。`insert_deduplicate=0`でデータを挿入した場合、そのデータは`insert_deduplicate=1`で挿入を再試行しても重複防止されません。これは、`block_id`が`insert_deduplicate=0`での挿入時に記録されないためです。
## 挿入重複防止の仕組み
データがClickHouseに挿入されるとき、行数とバイト数に基づいてデータがブロックに分割されます。
`*MergeTree`エンジンを使用したテーブルでは、各ブロックにはそのブロック内のデータのハッシュである一意の`block_id`が割り当てられます。この`block_id`は挿入操作の一意キーとして使用されます。同じ`block_id`が重複防止ログで見つかった場合、そのブロックは重複と見なされ、テーブルには挿入されません。
このアプローチは異なるデータを含む挿入の場合にうまく機能します。しかし、同じデータを複数回意図的に挿入する場合は、`insert_deduplication_token`設定を使用して重複防止プロセスを制御する必要があります。この設定を用いると、各挿入に対して一意のトークンを指定でき、ClickHouseはこのトークンを利用してデータが重複かどうかを判断します。
`INSERT ... VALUES` クエリの場合、挿入データのブロック分割は決定的であり、設定によって決定されます。したがって、初回操作と同じ設定値で挿入を再試行することをお勧めします。
`INSERT ... SELECT` クエリの場合、クエリの`SELECT`部分が各操作で同じ順序で同じデータを返すことが重要です。実際の使用においてこれは難しいことに留意してください。再試行で安定したデータ順序を確保するために、クエリの`SELECT`部分で正確な`ORDER BY`セクションを定義してください。再試行間に選択されたテーブルが更新される可能性がある事にも注意が必要です。結果データが変更され、重複防止が行われない可能性があります。また、大量のデータを挿入する場合、挿入後のブロック数が重複防止ログウィンドウをオーバーフローすることがあり、ClickHouseがブロックを重複防止しない可能性があります。
## マテリアライズドビューを使用した挿入重複防止
テーブルにマテリアライズドビューがある場合、そのビューの変換定義にしたがって挿入されたデータもビューの対象に挿入されます。変換されたデータも再試行時に重複を排除されます。ClickHouseはマテリアライズドビューに対しても、ターゲットテーブルに挿入されたデータと同様に重複を排除します。
このプロセスは以下のソーステーブル用の設定を使用して制御できます:
- `replicated_deduplication_window`
- `replicated_deduplication_window_seconds`
- `non_replicated_deduplication_window`
また、ユーザープロファイル設定`deduplicate_blocks_in_dependent_materialized_views`を使用することもできます。
マテリアライズドビュー下のテーブルへブロックを挿入する際、ClickHouseはソーステーブルの`block_id`と追加の識別子を組み合わせた文字列をハッシュして`block_id`を計算します。これにより、マテリアライズドビュー内での正確な重複防止が保証され、元の挿入によってデータが区別され、マテリアライズドビューの対象テーブルに到達する前に加えられる変換にかかわらずデータが識別されます。
## 例
### マテリアライズドビューの変換後の同一ブロック
マテリアライズドビュー内での変換中に生成された同一ブロックは異なる挿入データに基づいているため、重複排除されません。
こちらは例です:
```sql
CREATE TABLE dst
(
`key` Int64,
`value` String
)
ENGINE = MergeTree
ORDER BY tuple()
SETTINGS non_replicated_deduplication_window=1000;
CREATE MATERIALIZED VIEW mv_dst
(
`key` Int64,
`value` String
)
ENGINE = MergeTree
ORDER BY tuple()
SETTINGS non_replicated_deduplication_window=1000
AS SELECT
0 AS key,
value AS value
FROM dst;
```
```sql
SET max_block_size=1;
SET min_insert_block_size_rows=0;
SET min_insert_block_size_bytes=0;
```
上記の設定により、テーブルから一行のみを持つ一連のブロックを選択できます。これらの小さいブロックは圧縮されず、テーブルに挿入されるまでは同一のままです。
```sql
SET deduplicate_blocks_in_dependent_materialized_views=1;
```
マテリアライズドビューでの重複排除を有効にする必要があります:
```sql
INSERT INTO dst SELECT
number + 1 AS key,
IF(key = 0, 'A', 'B') AS value
FROM numbers(2);
SELECT
*,
_part
FROM dst
ORDER by all;
```
ここでは、`dst`テーブルに2つのパートが挿入されたことが分かります。選択からの2ブロック — 挿入時の2パート。パートは異なるデータを含んでいます。
```sql
SELECT
*,
_part
FROM mv_dst
ORDER by all;
```
ここでは、`mv_dst`テーブルに2つのパートが挿入されたことが分かります。それらのパートは同じデータを含んでいますが、重複排除されていません。
```sql
INSERT INTO dst SELECT
number + 1 AS key,
IF(key = 0, 'A', 'B') AS value
FROM numbers(2);
SELECT
*,
_part
FROM dst
ORDER by all;
SELECT
*,
_part
FROM mv_dst
ORDER by all;
```
ここでは、挿入を再試行したとき、すべてのデータが重複排除されることが確認できます。重複排除は`dst`テーブルと`mv_dst`テーブルの両方で機能します。
## 挿入時の同一ブロック
```sql
CREATE TABLE dst
(
`key` Int64,
`value` String
)
ENGINE = MergeTree
ORDER BY tuple()
SETTINGS non_replicated_deduplication_window=1000;
SET max_block_size=1;
SET min_insert_block_size_rows=0;
SET min_insert_block_size_bytes=0;
```
挿入:
```sql
INSERT INTO dst SELECT
0 AS key,
'A' AS value
FROM numbers(2);
SELECT
'from dst',
*,
_part
FROM dst
ORDER by all;
```
上記の設定で、選択結果から2つのブロックが生成され、その結果、テーブル`dst`への挿入には2つのブロックがあるはずです。しかし、`dst`テーブルには一つのブロックのみが挿入されたことが確認できます。これは、2番目のブロックが重複排除されたためです。挿入されたデータと、重複排除のキーである`block_id`は挿入されたデータのハッシュとして計算されています。この動作は期待されたものではありません。このようなケースは稀ですが、理論的には可能です。このようなケースを正しく処理するために、ユーザーは`insert_deduplication_token`を提供する必要があります。以下の例でそれを修正しましょう:
## `insert_deduplication_token`を使用した挿入時の同一ブロック
```sql
CREATE TABLE dst
(
`key` Int64,
`value` String
)
ENGINE = MergeTree
ORDER BY tuple()
SETTINGS non_replicated_deduplication_window=1000;
SET max_block_size=1;
SET min_insert_block_size_rows=0;
SET min_insert_block_size_bytes=0;
```
挿入:
```sql
INSERT INTO dst SELECT
0 AS key,
'A' AS value
FROM numbers(2)
SETTINGS insert_deduplication_token='some_user_token';
SELECT
'from dst',
*,
_part
FROM dst
ORDER by all;
```
2つの同一ブロックが期待通りに挿入されました。
```sql
select 'second attempt';
INSERT INTO dst SELECT
0 AS key,
'A' AS value
FROM numbers(2)
SETTINGS insert_deduplication_token='some_user_token';
SELECT
'from dst',
*,
_part
FROM dst
ORDER by all;
```
再試行挿入は期待通りに重複排除されます。
```sql
select 'third attempt';
INSERT INTO dst SELECT
1 AS key,
'b' AS value
FROM numbers(2)
SETTINGS insert_deduplication_token='some_user_token';
SELECT
'from dst',
*,
_part
FROM dst
ORDER by all;
```
この挿入も異なるデータを含んでいるにもかかわらず重複排除されています。`insert_deduplication_token`の優先度が高いことに注意してください:`insert_deduplication_token`が提供されると、ClickHouseはデータのハッシュサムを使用しません。
## 異なる挿入操作がマテリアライズドビューの基になるテーブルで同じデータを生成する場合
```sql
CREATE TABLE dst
(
`key` Int64,
`value` String
)
ENGINE = MergeTree
ORDER BY tuple()
SETTINGS non_replicated_deduplication_window=1000;
CREATE MATERIALIZED VIEW mv_dst
(
`key` Int64,
`value` String
)
ENGINE = MergeTree
ORDER BY tuple()
SETTINGS non_replicated_deduplication_window=1000
AS SELECT
0 AS key,
value AS value
FROM dst;
SET deduplicate_blocks_in_dependent_materialized_views=1;
select 'first attempt';
INSERT INTO dst VALUES (1, 'A');
SELECT
'from dst',
*,
_part
FROM dst
ORDER by all;
SELECT
'from mv_dst',
*,
_part
FROM mv_dst
ORDER by all;
select 'second attempt';
INSERT INTO dst VALUES (2, 'A');
SELECT
'from dst',
*,
_part
FROM dst
ORDER by all;
SELECT
'from mv_dst',
*,
_part
FROM mv_dst
ORDER by all;
```
毎回異なるデータを挿入しています。しかし、`mv_dst`テーブルには同じデータが挿入されています。データは元のデータが異なっていたため、重複排除されません。
## 異なるマテリアライズドビューが同一のデータを基になるテーブルに挿入する場合
```sql
CREATE TABLE dst
(
`key` Int64,
`value` String
)
ENGINE = MergeTree
ORDER BY tuple()
SETTINGS non_replicated_deduplication_window=1000;
CREATE TABLE mv_dst
(
`key` Int64,
`value` String
)
ENGINE = MergeTree
ORDER BY tuple()
SETTINGS non_replicated_deduplication_window=1000;
CREATE MATERIALIZED VIEW mv_first
TO mv_dst
AS SELECT
0 AS key,
value AS value
FROM dst;
CREATE MATERIALIZED VIEW mv_second
TO mv_dst
AS SELECT
0 AS key,
value AS value
FROM dst;
SET deduplicate_blocks_in_dependent_materialized_views=1;
select 'first attempt';
INSERT INTO dst VALUES (1, 'A');
SELECT
'from dst',
*,
_part
FROM dst
ORDER by all;
SELECT
'from mv_dst',
*,
_part
FROM mv_dst
ORDER by all;
```
テーブル`mv_dst`に2つの等しいブロックが挿入されました期待通り
```sql
select 'second attempt';
INSERT INTO dst VALUES (1, 'A');
SELECT
'from dst',
*,
_part
FROM dst
ORDER by all;
SELECT
'from mv_dst',
*,
_part
FROM mv_dst
ORDER by all;
```
この再試行操作は、`dst`テーブルと`mv_dst`テーブルの両方で重複排除されます。