Hologres V4.0.0-4.0.8 で論理パーティションテーブルをクエリする際に、「duplicate key value violates unique constraint」エラーやデータ欠損が発生するのはなぜですか?
V4.0 では、論理パーティションテーブルのマルチパーティションフラッシュ最適化に、クラスターインデックスの実装に関するバグが存在します。このバグにより、AliORC ファイル (列指向テーブルのデータファイル) 内のインデックスが破損する可能性があります。これにより、主に 2 つの問題が発生する可能性があります:
データ欠損:不正なクラスターインデックスが原因で、クエリが既存のデータを見つけられない場合があります。
プライマリキーの重複:データ更新中に、Hologres が既存のレコードを特定できず、代わりに新しいレコードが挿入されることがあります。これにより、プライマリキー制約に違反し、データが重複します。
このバグは、以下の 3 つの条件がすべて満たされた場合にのみ発生します:
ご利用の Hologres インスタンスのバージョンが 4.0.0 から 4.0.8 の間である。
テーブルが列指向ストレージまたは行列混在ストレージの論理パーティションテーブルである。
テーブルにクラスタリングキーが明示的に定義されている。
ソリューション
インスタンスのアップグレード
インスタンスを V4.0.9 以降にアップグレードしてください。
V4.0.9 では、クエリが不正なインデックスを使用しようとすると、クエリは失敗し、以下のエラーメッセージが返されます:
clustered_index size [x] incorrect.
インデックスに問題があるテーブルの確認
各データベースで以下の SQL 文を実行し、インデックスが破損しているテーブルを検出します。
WITH logical_partition_tables AS ( SELECT DISTINCT p1.table_namespace, p1.table_name FROM hologres.hg_table_properties p1 WHERE p1.property_key = 'logical_partition_columns' AND EXISTS ( SELECT 1 FROM hologres.hg_table_properties p2 WHERE p2.table_namespace = p1.table_namespace AND p2.table_name = p1.table_name AND p2.property_key = 'orientation' AND p2.property_value IN ('column', 'row,column', 'column,row') ) ), tables_with_pk AS ( SELECT l.table_namespace, l.table_name, p.property_value AS pk_expr FROM logical_partition_tables l JOIN hologres.hg_table_properties p ON l.table_namespace = p.table_namespace AND l.table_name = p.table_name WHERE p.property_key = 'primary_key' ), partition_cols AS ( SELECT l.table_namespace, l.table_name, p.property_value AS part_expr FROM logical_partition_tables l JOIN hologres.hg_table_properties p ON l.table_namespace = p.table_namespace AND l.table_name = p.table_name WHERE p.property_key = 'logical_partition_columns' ), all_partitions AS ( SELECT t.table_namespace, t.table_name, t.pk_expr, pc.part_expr, part.partition FROM tables_with_pk t JOIN partition_cols pc ON t.table_namespace = pc.table_namespace AND t.table_name = pc.table_name CROSS JOIN LATERAL ( SELECT partition FROM hologres.hg_list_logical_partition( (t.table_namespace || '.' || t.table_name)::TEXT ) ) AS part ), split_kv AS ( SELECT table_namespace, table_name, pk_expr, partition, TRIM(SPLIT_PART(kv, '=', 1)) AS col_name, TRIM(SPLIT_PART(kv, '=', 2)) AS col_val FROM all_partitions, UNNEST(STRING_TO_ARRAY(partition, '/')) AS kv ), final_where AS ( SELECT table_namespace, table_name, pk_expr, partition, STRING_AGG( FORMAT('%I = %L', col_name, col_val), ' AND ' ORDER BY col_name ) AS where_clause FROM split_kv GROUP BY table_namespace, table_name, pk_expr, partition ) SELECT FORMAT( '-- Check PK duplicates in %I.%I, partition: %s' || E'\n' || 'SELECT %s, COUNT(1) FROM %I.%I WHERE %s GROUP BY %s HAVING COUNT(1) > 1;', table_namespace, table_name, partition, pk_expr, table_namespace, table_name, where_clause, pk_expr ) AS detection_sql FROM final_where ORDER BY table_namespace, table_name, partition;上記の SQL コマンドを実行すると、現在のデータベース内のテーブルに不正なインデックスがあるかどうかを確認するための、他の SQL 文が返されます。
結果のサンプル:

SQL 文を実行します。正常に実行された場合、インデックスは正しいです。
clustere_index size mismatchエラーが発生した場合、インデックスは破損しています。エラーメッセージで特定されたテーブルについては、完全なコンパクションを実行して、不正なインデックスファイルを修正します:SELECT hologres.hg_full_compact_table('schema.table_name','max_file_size_mb=1, reclaim_deleted_data_space = false');コンパクション後、インデックスは正しく再構築されます。影響を受けたテーブルに対して再度上記のチェック文を実行し、修正を確認できます。
プライマリキーが重複しているテーブルの確認
論理パーティションテーブルでは、プライマリキーが重複することがあります。この問題は、
duplicate key value violates unique constraintエラーとして、または同じプライマリキーに対して複数のレコードが存在する形で現れることがあります。以下のコードを実行してください:WITH logical_partition_tables AS ( SELECT DISTINCT p1.table_namespace, p1.table_name FROM hologres.hg_table_properties p1 WHERE p1.property_key = 'logical_partition_columns' AND EXISTS ( SELECT 1 FROM hologres.hg_table_properties p2 WHERE p2.table_namespace = p1.table_namespace AND p2.table_name = p1.table_name AND p2.property_key = 'orientation' AND p2.property_value IN ('column', 'row,column') ) ), tables_with_pk AS ( SELECT l.table_namespace, l.table_name, p.property_value AS pk_expr FROM logical_partition_tables l JOIN hologres.hg_table_properties p ON l.table_namespace = p.table_namespace AND l.table_name = p.table_name WHERE p.property_key = 'primary_key' ), partition_cols AS ( SELECT l.table_namespace, l.table_name, p.property_value AS part_expr FROM logical_partition_tables l JOIN hologres.hg_table_properties p ON l.table_namespace = p.table_namespace AND l.table_name = p.table_name WHERE p.property_key = 'logical_partition_columns' ), all_partitions AS ( SELECT t.table_namespace, t.table_name, t.pk_expr, pc.part_expr, part.partition FROM tables_with_pk t JOIN partition_cols pc ON t.table_namespace = pc.table_namespace AND t.table_name = pc.table_name CROSS JOIN LATERAL ( SELECT partition FROM hologres.hg_list_logical_partition( (t.table_namespace || '.' || t.table_name)::TEXT ) ) AS part ), split_kv AS ( SELECT table_namespace, table_name, pk_expr, partition, TRIM(SPLIT_PART(kv, '=', 1)) AS col_name, TRIM(SPLIT_PART(kv, '=', 2)) AS col_val FROM all_partitions, UNNEST(STRING_TO_ARRAY(partition, '/')) AS kv ), final_where AS ( SELECT table_namespace, table_name, pk_expr, partition, STRING_AGG( FORMAT('%I = %L', col_name, col_val), ' AND ' ORDER BY col_name ) AS where_clause FROM split_kv GROUP BY table_namespace, table_name, pk_expr, partition ) SELECT FORMAT( '-- Check PK duplicates in %I.%I, partition: %s' || E'\n' || 'SELECT %s, COUNT(1) FROM %I.%I WHERE %s GROUP BY %s HAVING COUNT(1) > 1;', table_namespace, table_name, partition, pk_expr, table_namespace, table_name, where_clause, pk_expr ) AS detection_sql FROM final_where ORDER BY table_namespace, table_name, partition;これらのコマンドを実行すると、現在のデータベース内のテーブルにプライマリキーの重複があるかどうかを確認するための、他の SQL 文が返されます。
結果のサンプル:

返された SQL コマンドを実行してデータが返された場合、プライマリキーの重複が存在します。フルコンパクションを実行し、重複したプライマリキーを削除してください。
-- ステップ 1 select hologres.hg_full_compact_table('schema.table_name','max_file_size_mb=1'); -- ステップ 2 call public.hg_remove_duplicated_pk('schema.table_name','max_file_size_mb=1'); -- 注:hg_remove_duplicated_pk の実行時に「Query exceed memory limit」エラーが返された場合、データ量が大きすぎて処理できません。この場合、パーティションごとに重複を削除します。前のステップの SQL コメントには、重複データを含むパーティションが示されています。 call public.hg_remove_duplicated_pk('schema.table_name_1', 'dt_int=1'); call public.hg_remove_duplicated_pk('schema.table_name_2', 'dt_text=''A''');操作が完了すると、重複データは削除されます。対応するテーブルに対して再度上記のチェック文を実行し、修正を確認できます。
潜在的な問題があるテーブルの確認
以前のチェックで問題なく通過した場合でも、クラスタリングキーがないテーブルに対して追加のチェックを行うことを選択できます。このようなテーブルはプライマリキーの重複が発生しやすく、書き戻し後のリアルタイムクエリで `cluster index` size mismatch エラーが発生する可能性があります。これらの潜在的なリスクに対処するため、以下の SQL コマンドを使用して影響を受ける可能性のあるテーブルを特定し、それらに対してフルコンパクションを実行してください。
SELECT FORMAT( 'SELECT hologres.hg_full_compact_table(%L, ''max_file_size_mb=1''); -- Table: %I.%I (has logical partition but NO clustering_key)', table_namespace || '.' || table_name, table_namespace, table_name ) AS compact_sql FROM ( SELECT DISTINCT p1.table_namespace, p1.table_name FROM hologres.hg_table_properties p1 WHERE p1.property_key = 'logical_partition_columns' AND EXISTS ( SELECT 1 FROM hologres.hg_table_properties p2 WHERE p2.table_namespace = p1.table_namespace AND p2.table_name = p1.table_name AND p2.property_key = 'orientation' AND p2.property_value IN ('column', 'row,column') ) ) AS tables_missing_ck ORDER BY table_namespace, table_name;上記の SQL 文は、フルコンパクションコマンドを生成します。
結果のサンプル:

オフピーク時にコマンドを実行してください。