イベント重複除外
1)重複除外の理由
重複は、リトレイ、ネットワークタイムアウト、フェイルオーバー、履歴データのリプレイにより発生します。制御されていない場合:- 不変量が侵害されます(二重デビット、繰り返し電子メール/SMS、「二度作成」注文)。
- コストの増加(再書き込み/再処理)
- 歪んだ分析です。
重複排除の目的は、許容される輸送の繰り返しと、しばしば同一性とともに、1回限りの観測効果を提供することです。
2)重複排除(階層)を配置する場所)
1.Edge/APIゲートウェイ-'Idempotency-Keu '/body+signatureによって明示的な重複を遮断します。
2.ブローカー/ストリーム-キー/シーケンスによる論理的重複除外、ミスで結合(コストのために頻繁に発生しない)。
3.Event receiver (consumer)-main location:受信トレイ/キーテーブル/キャッシュ。
4.シンク(DB/キャッシュ)-一意のキー/UPSERT/バージョン/圧縮。
5.ETL/分析-タイムウィンドウと列ベッドのキーによる期限。
ルール:できるだけ早く、しかし考慮に入れて偽陽性のコストと再生の必要性。
3)重複除外キー
3.1ナチュラル(優先)
'payment_id'、 'order_id'、 'saga_id#step'、 'aggregate_id#seq'
安定性と意味を保証します。
3.2コンポジット
'(tenant_id、 type、 external_id、 version)'\'(user_id、 event_ts_truncated、 payload_hash)'。
3.3フィンガープリント
フィールドの決定論的サブセットのハッシュ(order/registersを正規化する)、オプションで'HMAC (secret、 payload)'。
3.4シーケンス/バージョン
集計あたりの単調な'seq'(楽観的ブロッキング/バージョニング)。
アンチパターン:ビジネスエンティティとの接続なしの「ランダムUUID」は不可能です。
4)時間の窓および順序
重複除外ウィンドウ-イベントが再び来ることができる期間(通常24-72時間;財政のために-より長い)。
アウトオブオーダー:遅刻しましょう。ストリーミングフレームワーク-イベントタイム+透かし。
スライド/修正ウィンドウのデッドアップ: "最後のN分でキーを見ましたか?».
Sequence-aware: 'seq'が最後≤処理された場合-double/repeat。
5)データ構造と実装
5.1正確な会計
Redis SET/STRING+TTL: 'SETNXキー 1 EX 86400'→「はじめて-私たちは処理しています、そうでなければ-SKIP」。
LRU/LFUキャッシュ(in-proc):高速ですが、揮発性→最初の障壁としてのみ優れています。
SQL固有のインデックス+UPSERT:「挿入または更新」(idempotentエフェクト)。
5.2おおよその構造(確率的)
Bloom/Cuckooフィルター:安価なメモリ、誤検出が可能です。これは、金融/注文ではなく、明白な「騒々しい」ドロップ(テレメトリーなど)に適しています。
Count-Minスケッチ:「ホット」テイクから保護するための周波数の推定。
5.3ストリーミング状態
Kafka Streams/Flink: TTLのキー付きステートストア、ウィンドウ内のキーによるデダップ。チェックポイント/リストア。
Watermark+allowed lateness:遅延イベントウィンドウを管理します。
6)トランザクションパターン
6.1受信トレイ(着信テーブル)
'message_id '/keyを保存し、結果を副作用にします:pseudo
BEGIN;
ins = INSERT INTO inbox(id, received_at) ON CONFLICT DO NOTHING;
IF ins_not_inserted THEN RETURN cached_result;
result = handle(event);
UPSERT sink with result; -- idempotent sync
UPDATE inbox SET status='done', result_hash=... WHERE id=...;
COMMIT;
リプレイは録音を表示し、エフェクトを繰り返しません。
6.2アウトボックス
1つのトランザクションでのビジネス記録とイベント→パブリッシャーはブローカーに送信します。消費者から二重を排除しませんが、「穴」を除外します。
6.3ユニークなインデックス/UPSERT
sql
INSERT INTO payments(id, status, amount)
VALUES ($1, $2, $3)
ON CONFLICT (id) DO NOTHING; -- "create once"
または制御されたバージョンのアップグレード:
sql
UPDATE orders
SET status = $new, version = version + 1
WHERE id=$id AND version = $expected; -- optimistic blocking
6.4集計のバージョン管理
イベントが'イベントの場合に適用されます。version=aggregate。バージョン+1'。それ以外の場合-二重/繰り返し/競合。
7)デッドアップとブローカー/ストリーム
7.1カフカ
Idempotent Producerはエントリーダブルを削減します。
トランザクションを使用すると、オフセット+出力レコードをアトミックにコミットできます。
Compaction: post-factum dedup/coalescing(支払いのためではありません)-キーごとに最後の値を格納します。
消費者側:ウィンドウキーの状態ストア/Redis/DB。
7.2 NATS/JetStream
Ack/redelivery→少なくとも1回。消費者のデダップ(受信トレイ/Redis)。
JetStreamシーケンス/コンシューマー作業により、繰り返しを識別しやすくなります。
7.3キュー(ウサギ/SQS)
可視性タイムアウト+繰り返し配信→キー+デッドストアが必要です。
'MessageGroupId'/'DeduplicationId'を持つSQS FIFOは役に立ちますが、TTLウィンドウはプロバイダに限定されています。
8)貯蔵および分析装置
8.1 ClickHouse/BigQuery
ウィンドウでデダップする:'ORDER BY key、 ts'および'argMax'/'anyLast'条件付き。
ClickHouse:sql
SELECT key,
anyLast(value) AS v
FROM t
WHERE ts >= now() - INTERVAL 1 DAY
GROUP BY key;
または、「unique」イベントの実体化レイヤー(キー/バージョンでマージ)。
8.2ログ/テレメトリー
ingest→save network/diskでapproximate-dump (Bloom)としましょう。
9)再処理、再生およびbackfill
Dedupキーはリプレイ(TTL ≥リプレイウィンドウ)を生き残らなければなりません。
バックフィルの場合は、バージョン('key#source=batch2025')でキースペースを使用するか、オンラインウィンドウに干渉しないように「リーク」を分離します。
結果アーティファクト(hash/version)を保存すると、リプレイの「fast-skip」が高速化されます。
10)指標と観測可能性
'dedup_hit_total'/'dedup_hit_rate'-重複の割合がキャッチされます。
'dedup_fp_rate'は確率フィルタである。
'window_size_seconds'実際(テレメトリーによる遅着)。
'inbox_conflict_total'、 'upsert_conflict_total'
'replayed_events_total'、 'skipped_by_inbox_total'
テナント/キー/タイプによるプロフィール:最も重要なのはどこですか?
'message_id'、 'idempotency_key'、 'seq'、 'window_id'、 'action=process' skip'。
11)セキュリティとプライバシー
PIIをキーに入れないでください。ハッシュ/エイリアスを使用します。
指紋に署名するには-衝突/偽造を避けるためにHMAC(秘密、canonical_payload)。
キーの保存時間をコンプライアンス(GDPR保持)で調整します。
12)性能および費用
In-proc LRU ≪ Redisは、動作ごとにレイテンシ/コストでSQLを≪します。
Redis:安価で高速ですが、キーとTTLのボリュームを考慮してください。shardy by 'tenant/hash'。
SQL: p99で高価ですが、強力な保証とオーディエンスを提供します。
確率フィルタ:非常に安価ですが、FPは可能です。「extra SKIP」が重要でない場合に使用します。
13)アンチパターン
"私たちにはカフカが正確に一度あります-キーは必要ありません。"必要とされた-あざ/ビジネス層で。
キー→リプレイ/ディレイの短すぎるTTLは2倍になります。
グローバル単一のdedup→ホットスポットおよびSPOF;テナント/キーによって鋭くない。
メモリでのみデダップ-プロセスの損失=テイクの波。
お金/注文のために咲く-偽陽性は正当な操作を奪うでしょう。
一貫性のないペイロードの正規化-意味が同じメッセージの異なるハッシュ。
アウトオブオーダー-レイトイベントを無視すると、誤って重複がマークされます。
14)実装チェックリスト
- 自然なキー(または複合/指紋)を定義します。
- dedupウィンドウと'lateness'ポリシーを設定します。
- レベルを選択:エッジ、消費者、シンク;粉砕のために提供して下さい。
- Inbox/UPSERTを実装する。フローの場合-キー付き状態+TTL。
- おおよそのバリアが必要な場合-Bloom/Cuckoo(重要でないドメインのみ)。
- リプレイ互換性を構成します(TTL ≥リプレイ/バックフィルウィンドウ)。
- メトリクス'dedup_hit_rate'、競合とウィンドウラグ;テナントごとのダッシュボード。
- ゲームデー:タイムアウト/リトレイ、リプレイ、アウトオブオーダー、キャッシュドロップ。
- ペイロードの正規化とキーバージョン化を文書化します。
- ホットキーと長いウィンドウでロードテストを実行します。
15)サンプル構成/コード
15.1 Redis SETNX+TTL(バリア)
lua
-- KEYS[1] = "dedup:{tenant}:{key}"
-- ARGV[1] = ttl_seconds local ok = redis. call("SET", KEYS[1], "1", "NX", "EX", ARGV[1])
if ok then return "PROCESS"
else return "SKIP"
end
15.2 PostgreSQL受信トレイ
sql
CREATE TABLE inbox (
id text PRIMARY KEY,
received_at timestamptz default now(),
status text default 'received',
result_hash text
);
-- In the handler: INSERT... ON CONFLICT DO NOTHING -> check, then UPSERT in blue.
15.3カフカストリーム
java var deduped = input
.selectKey((k,v) -> v.idempotencyKey())
.groupByKey()
.windowedBy(TimeWindows. ofSizeWithNoGrace(Duration. ofHours(24)))
.reduce((oldV,newV) -> oldV) // first wins
.toStream()
.map((wKey,val) -> KeyValue. pair(wKey. key(), val));
15.4 Flink(キー付き状態+TTL、擬似)
java
ValueState<Boolean> seen;
env. enableCheckpointing(10000);
onEvent(e):
if (!seen.value()) { process(e); seen. update(true); }
15.5 NGINX/APIゲートウェイ(エッジ上のIdempotency-Key)
nginx map $http_idempotency_key $idkey { default ""; }
Proxy the key to the backend; backend solves deadup (Inbox/Redis).
16) FAQ
Q:何を選ぶために:deadupか純粋なidempotenceか?
A:通常両方:deadupは速い「フィルター」です(節約)、idempotenceは正しい効果の保証です。
Q:どのTTLを置くべきですか?
A:最大≥可能な再配達時間+在庫。通常24-72時間;財政および延期された仕事のため-日/週。
Q:レイトイベントはどのように対応していますか?
A: 'allowed lateness'とアラーム'late_event'を設定します。後で-別のブランチを介して(再計算/スキップ)。
Q:テレメトリーストリーム全体を重複除外できますか?
A:はい、端のおおよそのフィルター(Bloom)、しかしFPを考慮し、重大なビジネス効果に適用しません。
Q:バックフィルの邪魔になるデッドアップ?
A:別のキースペース('key#batch2025')またはbackfillの持続期間のための障壁を無効にして下さい;TTLキーはオンラインウィンドウのみをカバーする必要があります。
17)合計
重複排除は、適切なキー、ウィンドウと状態構造+トランザクションパターン(Inbox/Outbox/UPSERT)、注文と遅延イベントのマインドフルな処理です。最も安い場所に障壁を配置し、あざの中でidempotenceを確保し、'dedup_hit_rate'を測定し、リプレイ/テストに失敗する-このようにして、レイテンシーとコストの不要なテールなしで「効果的に1回」を得ることができます。