- 技術 💻
整合性とは|意味とデータ整合性の種類・崩れる例と守り方
結論
整合性とは、矛盾がなく整っていることです。ITでは、データ同士が食い違わない状態を指します。データベースは制約とトランザクションで守ります。
この記事でわかること
- 一般の意味・例文と、データ整合性の4種類
- 整合性が崩れる5つの例と、防いだ後のエラー原文
- 点検用SQL5本と、結果整合性の2秒の遅れの実測
データの整合性は、守る相手ごとに仕組みが分かれます。1行の値は制約、複数の更新はトランザクション、同時の更新はロック、複数のコピーは整合性モデルで守ります。
flowchart LR
A[データの整合性<br>矛盾が無い状態] --> B[1行の値<br>制約]
A --> C[複数の更新<br>トランザクション]
A --> D[同時の更新<br>ロック・version]
A --> E[複数のコピー<br>強い・結果整合性]
整合性が崩れる形と、防ぐ仕組みの早見表です。どれもこの記事で PostgreSQL 16 を使って再現しています。
| 崩れ方(よくある順) | 例 | 防ぐ仕組み |
|---|---|---|
| 二重登録 | 同じ会員が2行ある | 主キー・UNIQUE |
| ありえない値 | 数量が -3、状態が piad |
CHECK・NOT NULL |
| 親のない子 | 存在しない会員の注文 | 外部キー |
| 途中で失敗 | 出金だけ反映されて入金が無い | トランザクション |
| 同時の更新 | 2人が買ったのに在庫が1しか減らない | 楽観ロック・FOR UPDATE |
| コピーの遅れ | レプリカが更新前の値を返す | 強い整合性の読み取り |
確認環境:Ubuntu 24.04・PostgreSQL 16.15(psql 16.15)・GNU coreutils 9.4(sha256sum)。制約のないテーブルと制約のあるテーブルを作り、二重登録・親のない子・ありえない値・途中失敗・同時更新を psql で実際に起こしました。同時更新は psql を2つ同時に動かし、結果整合性はストリーミングレプリケーションのレプリカ(反映を2秒遅らせる設定)で確かめています。用語の定義・エラーコードは PostgreSQL 16 の公式ドキュメント、データチェックサムの既定値の変更は PostgreSQL 18 のリリースノート、結果整合性の説明は Amazon DynamoDB・Amazon S3 の公式ドキュメント、CAP 定理は Eric Brewer 氏の記事、情報セキュリティの「完全性」は JIPDEC の ISMS の資料、一般の語義はコトバンク(精選版 日本国語大辞典)で確かめました(確認日 2026年10月22日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「整合性とは|意味とデータ整合性の種類・崩れる例と守り方」
整合性とは(一般の意味・読み方・使い方)
この節では、ふだんの言葉としての「整合性」の意味と、正しい使い方が分かります。
整合性(せいごうせい)は、物事の間に矛盾がなく、きちんと合っている性質のことです。 精選版 日本国語大辞典は、英語 consistency の訳語として、論理学で「矛盾するものを同時に定理とすることがない」状態・性質(無矛盾性)と説明しています。ふだんは「計画と予算の整合性」のように、2つ以上のものが食い違っていないかを言うときに使います。
| 言い方 | 意味 | 例文 |
|---|---|---|
| 整合性がある・とれている | 食い違いが無い | 見積書と請求書の金額は整合性がとれている |
| 整合性がない・欠く | 食い違いがある | 説明と画面の動きに整合性がない |
| 整合性をとる | 食い違いを無くすよう合わせる | 仕様書と設計書の整合性をとる |
| 整合性を図る・保つ | 合うように努める・合った状態を続ける | 発言と行動の整合性を図る |
| 整合性を確認する・チェックする | 食い違いが無いか調べる | 移行後のデータの整合性を確認する |
「合う」と重ねない
「整合」に「合う」の意味が入っているため、「合う」「整える」と重ねると意味が重なります
類語・対義語・英語
| 種類 | 言葉 | 使い分けの目安 |
|---|---|---|
| 類語 | 一貫性 | 始めから終わりまで方針が変わらないこと。時間の流れで見る |
| 類語 | 辻褄が合う・筋が通る・矛盾がない | 話し言葉での言い換え |
| 対義語 | 矛盾・食い違い・不一致 | 整合性がない状態 |
| 英語 | consistency | 食い違いが無いこと(論理・データの一致) |
| 英語 | integrity | 正しく欠けていないこと(データが壊れていない・改ざんされていない) |
IT の文書では、consistency と integrity のどちらも「整合性」と訳されることがあります。 次の節から、IT での意味を見ていきます。
ITでの整合性とは(データ整合性)
この節では、IT・データベースの話で「整合性」が何を指すかが分かります。
IT で「整合性」と言うときは、多くの場合「データ整合性」、つまりデータの中身同士・データと決まりごとの間に食い違いが無い状態を指します。 例えば次の状態は、どれも整合性が崩れています。
- 同じ会員が2行あり、売上が2倍に数えられる
- 注文が、存在しない会員を指している
- 口座 A からは 3,000 円減ったのに、口座 B には増えていない
- 2人が同時に1個ずつ買ったのに、在庫が1しか減っていない
PostgreSQL の用語集は、ACID の「Consistency(一貫性)」を「データベースのデータが、常に整合性制約(integrity constraints)に従っている性質」と定義しています。整合性制約とは、主キー・外部キー・CHECK などの「データが守るべき決まり」をデータベースに書いたものです。 データベースは、この決まりに反する書き込みをエラーで止めます。
PostgreSQL のエラーコードでも、制約違反はまとめて「クラス 23 — Integrity Constraint Violation(整合性制約違反)」に分類されています。
| エラーコード | 名前 | 起きること |
|---|---|---|
| 23505 | unique_violation | 主キー・UNIQUE の重複 |
| 23503 | foreign_key_violation | 親の無い子・子のある親の削除 |
| 23514 | check_violation | CHECK の条件に合わない値 |
| 23502 | not_null_violation | 必須の列が空(NULL) |
| 23P01 | exclusion_violation | EXCLUDE(重なり禁止)の違反 |
整合性・一貫性・完全性・正確性の違い
この節では、似た言葉との違いが1つの表で分かります。
| 言葉 | 英語 | 見ているもの | 例 |
|---|---|---|---|
| 整合性(データ整合性) | integrity・consistency | データ同士・決まりとの食い違いが無いか | 注文の会員IDが会員表に存在する |
| 一貫性(ACID の C) | consistency | トランザクションの前後で制約を守っているか | 振替の前後で残高がマイナスにならない |
| 一貫性(分散システム) | consistency | どのコピーを読んでも同じ値か | レプリカも最新の値を返す |
| 完全性(情報セキュリティ) | integrity | 正確で欠けていないか、改ざんされていないか | 送ったファイルのハッシュ値が一致する |
| 正確性 | accuracy | 現実の事実と合っているか | 登録された住所が本当の住所 |
整合性で分かること
データの中に矛盾が無い
決まり(制約)を守っている
整合性だけでは分からないこと
値が現実と合っているか(正確性)
誰が見られるか(機密性・アクセス制御)
整合性がとれていても、正しいとは限りません。 住所を打ち間違えても、形式が決まりに合っていればデータベースは受け付けます。一方、整合性が崩れたデータは、どこかが必ず間違っています。
情報セキュリティの「完全性」との関係
情報セキュリティの3要素(機密性・完全性・可用性)の「完全性」も、英語では integrity です。JIPDEC の ISMS の資料では、完全性は「正確さ及び完全さの特性」と定義されています。データが壊れていない・勝手に書き換えられていないことを指し、後の「物理的な整合性」の節で扱うチェックサムがこれを確かめる道具です。
EU の GDPR(一般データ保護規則)も、第5条1項(f) で個人データの処理の原則の1つを「integrity and confidentiality(完全性及び機密性)」としています。許可されていない処理や、偶発的な消失・破壊・損傷から守ることを求めています。
データ品質・データセキュリティとの違い
| 言葉 | 範囲 | 整合性との関係 |
|---|---|---|
| データ品質 | 正確さ・網羅・鮮度・使いやすさまで含む広い考え方 | 整合性はデータ品質の前提の1つ |
| データセキュリティ | 誰が読める・変えられるかを制御する | 権限のある人の操作ミスで崩れる整合性は防げない |
| データ整合性 | 矛盾が無く、壊れていない | 制約・トランザクション・チェックサムで守る |
データ整合性の種類(物理・実体・参照・ドメイン・業務ルール)
この節では、データ整合性がどんな種類に分かれ、それぞれ何で守るかが分かります。
データ整合性は、大きく「物理的な整合性」と「論理的な整合性」に分かれます。論理的な整合性は、リレーショナルデータベースでは次の4つに分けて説明されることが多いです。
1実体整合性(エンティティ整合性)
1行を1つのものとして必ず見分けられること。主キーが重複せず、空(NULL)でない状態です。守る仕組みは PRIMARY KEY・UNIQUE。
2参照整合性
別の表を指す値が、必ず実在する行を指していること。親の無い子を作らず、子が残る親を消さない状態です。守る仕組みは FOREIGN KEY(外部キー)。
3ドメイン整合性
列の値が、決められた型・範囲・候補の中に入っていること。数量は1以上、状態は4種類のどれか、などです。守る仕組みは型・NOT NULL・CHECK。
4業務ルールの整合性(ユーザー定義)
業務の決まりに合っていること。注文の合計と明細の合計が一致する、同じ会議室の予約が重ならない、などです。EXCLUDE・トリガー・アプリの処理・点検用SQLで守ります。
| 種類 | 守ること | 崩れた例 | 守る仕組み |
|---|---|---|---|
| 物理的な整合性 | 保存・転送の間に壊れない | ディスク故障でページが壊れる | チェックサム・バックアップ・冗長化 |
| 実体整合性 | 行を一意に見分けられる | 同じ会員IDが2行 | 主キー・UNIQUE |
| 参照整合性 | 参照先が実在する | 会員999の注文があるのに会員999がいない | 外部キー |
| ドメイン整合性 | 値が決められた範囲 | 数量 -3・状態 piad |
型・NOT NULL・CHECK |
| 業務ルール | 業務の決まりに合う | 合計 5,000 円なのに明細は 4,000 円 | EXCLUDE・トリガー・点検SQL |
テーブルの分け方(正規化)が悪いと、同じ事実を何か所にも持つことになり、更新し忘れで食い違いが生まれます。関連記事「データベースの正規化をわかりやすく|第1〜第3正規形の例」で扱っています。
整合性が崩れる例をPostgreSQL 16で再現
この節では、制約もトランザクションも使わないと、データが実際にどう崩れるかが分かります。
例1〜3:二重登録・親のない子・ありえない値
制約を1つも付けないテーブルを作り、まずいデータを入れてみます。
CREATE TABLE bad_customers (id int, email text, name text);
CREATE TABLE bad_orders (id int, customer_id int, quantity int, status text);
INSERT INTO bad_customers VALUES (1, 'sato@example.com', '佐藤');
INSERT INTO bad_customers VALUES (1, 'sato@example.com', '佐藤'); -- 二重登録
INSERT INTO bad_orders VALUES (10, 1, 2, 'paid');
INSERT INTO bad_orders VALUES (11, 999, 1, 'paid'); -- いない会員
INSERT INTO bad_orders VALUES (12, 1, -3, 'piad'); -- ありえない値
すべて INSERT 0 1 で受け付けられました。 データベースは何も止めません。
id | email | name
----+------------------+------
1 | sato@example.com | 佐藤
1 | sato@example.com | 佐藤
(2 rows)
id | customer_id | quantity | status
----+-------------+----------+--------
10 | 1 | 2 | paid
11 | 999 | 1 | paid
12 | 1 | -3 | piad
(3 rows)
このまま会員ごとの注文数を集計すると、結果がおかしくなります。
SELECT c.name, count(*) AS 注文行数, sum(o.quantity) AS 合計数量
FROM bad_customers c JOIN bad_orders o ON o.customer_id = c.id GROUP BY c.name;
name | 注文行数 | 合計数量
------+----------+----------
佐藤 | 4 | -2
(1 row)
佐藤さんの注文は2件なのに、会員が2行あるため 4件 に数えられ、マイナスの数量が混ざって合計が -2 になりました。会員999の注文は、JOIN で消えて集計から漏れています。
例4:途中で失敗した振替
口座 A から B へ 3,000 円を移します。出金と入金の2つの UPDATE を、トランザクションを使わずに順に実行し、2つ目を失敗させます(ここではテーブル名の打ち間違い。アプリの例外や接続切れでも同じことが起きます)。
CREATE TABLE bad_accounts (id int, owner text, balance int);
INSERT INTO bad_accounts VALUES (1, 'A', 10000), (2, 'B', 0);
UPDATE bad_accounts SET balance = balance - 3000 WHERE id = 1;
UPDATE bad_acounts SET balance = balance + 3000 WHERE id = 2;
UPDATE 1
ERROR: relation "bad_acounts" does not exist
LINE 1: UPDATE bad_acounts SET balance = balance + 3000 WHERE id = 2...
^
id | owner | balance
----+-------+---------
1 | A | 7000
2 | B | 0
(2 rows)
合計
------
7000
(1 row)
合計 10,000 円だったお金が 7,000 円になり、3,000 円が消えました。 psql は既定で1文ごとに確定(自動コミット)するため、1つ目の UPDATE だけが残っています。
例5:同時の更新で在庫が合わない(ロストアップデート)
在庫10個の商品を、2人がほぼ同時に1個ずつ買う処理です。アプリでよくある「読む → 計算する → 書く」の形を、psql を2つ同時に動かして再現しました。\gset は、SELECT の結果を psql の変数に入れる命令です。
-- lu.sql(A と B の2つを同時に実行)
SELECT stock FROM bad_items WHERE id = 1 \gset
\echo :who 'が読んだ在庫 =' :stock
SELECT pg_sleep(1) \gset -- アプリの計算や画面操作の時間
UPDATE bad_items SET stock = :stock - 1 WHERE id = 1;
A が読んだ在庫 = 10
B が読んだ在庫 = 10
UPDATE 1
UPDATE 1
stock
-------
9
(1 row)
2個売れたのに、在庫は 9 です。 A も B も「10 から 1 引いて 9」を書いたため、A の更新が B に上書きされて消えました。これをロストアップデート(更新の消失)と呼びます。どちらの UPDATE もエラーにならないため、気づきにくい崩れ方です。
flowchart TD
A1[A が在庫10を読む] --> W1[A が 9 を書く]
B1[B が在庫10を読む] --> W2[B が 9 を書く]
W1 --> R[在庫は9<br>本当は8]
W2 --> R
制約で防ぐ(主キー・UNIQUE・外部キー・CHECK・NOT NULL)
この節では、例1〜3を制約で止めたときのエラー原文と、その読み方が分かります。
同じ内容のテーブルを、制約を付けて作り直します。
CREATE TABLE customers (
id bigint PRIMARY KEY,
email text NOT NULL UNIQUE,
name text NOT NULL
);
CREATE TABLE orders (
id bigint PRIMARY KEY,
customer_id bigint NOT NULL REFERENCES customers(id),
quantity int NOT NULL CHECK (quantity > 0),
status text NOT NULL CHECK (status IN ('new', 'paid', 'shipped', 'cancelled'))
);
INSERT INTO customers VALUES (1, 'sato@example.com', '佐藤');
INSERT INTO orders VALUES (10, 1, 2, 'paid');
まずいデータを入れようとすると、1つずつエラーで止まりました。
=> INSERT INTO customers VALUES (1, 'sato2@example.com', '佐藤');
ERROR: duplicate key value violates unique constraint "customers_pkey"
DETAIL: Key (id)=(1) already exists.
=> INSERT INTO customers VALUES (2, 'sato@example.com', '佐藤');
ERROR: duplicate key value violates unique constraint "customers_email_key"
DETAIL: Key (email)=(sato@example.com) already exists.
=> INSERT INTO customers VALUES (3, NULL, '鈴木');
ERROR: null value in column "email" of relation "customers" violates not-null constraint
DETAIL: Failing row contains (3, null, 鈴木).
=> INSERT INTO orders VALUES (11, 999, 1, 'paid');
ERROR: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"
DETAIL: Key (customer_id)=(999) is not present in table "customers".
=> INSERT INTO orders VALUES (12, 1, -3, 'paid');
ERROR: new row for relation "orders" violates check constraint "orders_quantity_check"
DETAIL: Failing row contains (12, 1, -3, paid).
=> INSERT INTO orders VALUES (13, 1, 1, 'piad');
ERROR: new row for relation "orders" violates check constraint "orders_status_check"
DETAIL: Failing row contains (13, 1, 1, piad).
注文が残っている会員を消そうとしても、外部キーが止めます。
=> DELETE FROM customers WHERE id = 1;
ERROR: update or delete on table "customers" violates foreign key constraint "orders_customer_id_fkey" on table "orders"
DETAIL: Key (id)=(1) is still referenced from table "orders".
数量 -3 の注文を入れたとき
制約は、どのアプリ・どの手作業から書き込んでも同じように効く
| 制約 | 守る整合性 | 止めるもの | 書き方 |
|---|---|---|---|
PRIMARY KEY |
実体 | 行IDの重複と NULL | id bigint PRIMARY KEY |
UNIQUE |
実体 | 重複(メールアドレスなど) | email text UNIQUE |
NOT NULL |
ドメイン | 空の値 | name text NOT NULL |
CHECK |
ドメイン | 範囲外・候補外の値 | CHECK (quantity > 0) |
FOREIGN KEY |
参照 | 親の無い子・子のある親の削除 | REFERENCES customers(id) |
EXCLUDE |
業務ルール | 期間の重なりなど | EXCLUDE USING gist (...) |
制約の追加・削除の書き方は、関連記事「SQL制約一覧|主キー・UNIQUE・CHECK・NOT NULLの追加と削除」で扱っています。
UNIQUE は NULL を何行でも通す
PostgreSQL のドキュメントには、UNIQUE では「2つの NULL は等しいとはみなさない」とあります。実際に試すと、NULL は2行とも入りました。NULL も1つに限りたいときは、PostgreSQL 15 から使える NULLS NOT DISTINCT を付けます。
=> CREATE TABLE u1 (code text UNIQUE);
=> INSERT INTO u1 VALUES (NULL), (NULL);
INSERT 0 2
=> CREATE TABLE u2 (code text UNIQUE NULLS NOT DISTINCT);
=> INSERT INTO u2 VALUES (NULL), (NULL);
ERROR: 23505: duplicate key value violates unique constraint "u2_code_key"
DETAIL: Key (code)=(null) already exists.
親を消したいとき:ON DELETE の選び方
子が残っている親を消すときの動きは、外部キーの ON DELETE で決めます。ドキュメントにある5つの動きです。
| 指定 | 親を消したときの動き | 向いている例 |
|---|---|---|
NO ACTION(既定) |
子が残っていればエラー(検査をトランザクションの後ろへ遅らせられる) | 注文が残る会員 |
RESTRICT |
子が残っていればエラー(遅らせられない) | 同上 |
CASCADE |
子も一緒に消す | 投稿とそのコメント |
SET NULL |
子の参照列を NULL にする | 投稿と、残しておきたい「いいね」 |
SET DEFAULT |
子の参照列を既定値にする | 既定の担当者に付け替える |
CASCADE と SET NULL を実際に試した結果です。投稿を1件消すと、コメント2件は消え、「いいね」の参照先は NULL になりました。
DELETE 1
comments_rows | likes_post_id
---------------+---------------
0 |
(1 row)
外部キーを宣言しても、子の側の列に索引(インデックス)は自動では作られません。 ドキュメントは、親の削除・更新のたびに子のテーブルを探すため、子の列に索引を作るとよいことが多い、としています。
業務ルールを制約にする:予約の重なりを EXCLUDE で止める
「同じ会議室の予約は時間が重ならない」は、CHECK では書けません。PostgreSQL の CHECK は、検査している行以外のデータを参照できないためです。複数の行にまたがる決まりは、EXCLUDE(排他制約)で書けます。
CREATE EXTENSION IF NOT EXISTS btree_gist;
CREATE TABLE reservations (
room int NOT NULL,
during tstzrange NOT NULL,
EXCLUDE USING gist (room WITH =, during WITH &&)
);
INSERT INTO reservations VALUES (101, '[2026-10-22 10:00+09, 2026-10-22 11:00+09)');
INSERT INTO reservations VALUES (101, '[2026-10-22 10:30+09, 2026-10-22 11:30+09)');
INSERT INTO reservations VALUES (102, '[2026-10-22 10:30+09, 2026-10-22 11:30+09)');
INSERT 0 1
ERROR: 23P01: conflicting key value violates exclusion constraint "reservations_room_during_excl"
DETAIL: Key (room, during)=(101, ["2026-10-22 10:30:00+09","2026-10-22 11:30:00+09")) conflicts with existing key (room, during)=(101, ["2026-10-22 10:00:00+09","2026-10-22 11:00:00+09")).
INSERT 0 1
同じ部屋(101)で30分重なる予約は止まり、別の部屋(102)は入りました。
トランザクションで防ぐ(途中で失敗したとき)
この節では、例4の「途中で失敗した振替」を、トランザクションでまとめて取り消す様子が分かります。
トランザクションは、複数の更新を「全部成功か、全部なし」の1つのまとまりとして扱う仕組みです。 BEGIN で始め、COMMIT で確定します。途中でエラーが出たら、それまでの更新はすべて取り消されます。
CREATE TABLE accounts (
id bigint PRIMARY KEY,
owner text NOT NULL,
balance int NOT NULL CHECK (balance >= 0)
);
INSERT INTO accounts VALUES (1, 'A', 10000), (2, 'B', 0);
BEGIN;
UPDATE accounts SET balance = balance - 3000 WHERE id = 1;
UPDATE bad_acounts SET balance = balance + 3000 WHERE id = 2; -- 失敗する
UPDATE accounts SET balance = balance + 3000 WHERE id = 2;
COMMIT;
BEGIN
UPDATE 1
ERROR: relation "bad_acounts" does not exist
ERROR: current transaction is aborted, commands ignored until end of transaction block
ROLLBACK
id | owner | balance
----+-------+---------
1 | A | 10000
2 | B | 0
(2 rows)
読み取れることは3つです。
- 1つ目の UPDATE(出金)も取り消され、A の残高は 10,000 円のままです。 例4のように 3,000 円が消えることはありません。
- エラーの後の文は
current transaction is abortedで受け付けられません(エラーコード 25P02)。 - エラーの後に
COMMITを送ると、PostgreSQL はROLLBACKと返します。 確定はされません。
制約とトランザクションを組み合わせる
残高不足の振替を、CHECK(balance >= 0)とトランザクションで止めます。先に入金、後で出金という順にしても、出金で CHECK に反した時点で全体が戻りました。
BEGIN
UPDATE 1
ERROR: new row for relation "accounts" violates check constraint "accounts_balance_check"
DETAIL: Failing row contains (1, A, -10000).
ROLLBACK
合計
-------
10000
(1 row)
途中で接続が切れたとき
出金の UPDATE の後、入金の前に、別のセッションから pg_terminate_backend で接続を切りました。アプリのプロセスが落ちたのと同じ状況です。
BEGIN
UPDATE 1
FATAL: terminating connection due to administrator command
server closed the connection unexpectedly
id | balance
----+---------
1 | 10000
2 | 0
(2 rows)
確定(COMMIT)する前に接続が切れた更新は、残りませんでした。 PostgreSQL の用語集は、Atomicity(原子性)を「全部完了するか、どれも完了しないか」で、障害の後に途中の結果が見えないこと、と説明しています。
一緒に成功すべき更新を見つける
出金と入金、注文と在庫の引き当て、ヘッダーと明細のように、片方だけ残ると矛盾する更新の組を洗い出します。
BEGIN と COMMIT で囲む
その組を1つのトランザクションにします。フレームワークを使うなら、そのトランザクションの仕組み(Spring の @Transactional など)で囲みます。
決まりは制約にも書く
残高がマイナスにならない、などの決まりを CHECK にすると、アプリの判定漏れがあってもデータベースが止め、トランザクションごと戻ります。
エラー時は最初からやり直す
エラーの後はそのトランザクションを捨て、必要なら最初から実行し直します。同じ処理を2回送っても二重にならないよう、冪等キーなどで備えます。
COMMIT・ROLLBACK・SAVEPOINT の書き方は、関連記事「SQLトランザクションとは|COMMIT・ROLLBACKの書き方と実行結果」で扱っています。
同時更新のロストアップデートを防ぐ4つの方法
この節では、例5の「在庫が1しか減らない」を防ぐ4つの方法と、それぞれの実行結果が分かります。
どれも、在庫10個に対して A と B が同時に1個ずつ買う処理を、psql を2つ同時に動かして試しました。
方法1:UPDATE の中で計算する
アプリで「読んで計算して書く」をやめ、計算をデータベースに任せます。
UPDATE items SET stock = stock - 1 WHERE id = 1 AND stock > 0;
UPDATE 1
UPDATE 1
stock
-------
8
在庫は正しく 8 になりました。 PostgreSQL の既定(Read Committed)では、後から来た UPDATE は先の UPDATE の確定を待ち、更新後の行に対して WHERE を評価し直してから実行します(ドキュメントの説明どおり)。
方法2:SELECT ... FOR UPDATE で行をロックする(悲観ロック)
読むときに行をロックします。他のトランザクションがその行を更新したり、同じように FOR UPDATE で読んだりするのを、確定まで待たせます。
BEGIN;
SELECT stock FROM items WHERE id = 1 FOR UPDATE \gset
SELECT pg_sleep(1) \gset
UPDATE items SET stock = :stock - 1 WHERE id = 1;
COMMIT;
Time: 1.117 ms ← A の FOR UPDATE はすぐ終わる
A が読んだ在庫 = 10
Time: 974.831 ms ← B は A の COMMIT まで待たされた
B が読んだ在庫 = 9
stock
-------
8
B は約 0.97 秒待たされ、A が書いた 9 を読んでから 8 にしました。 正しくなる代わりに、待ちが生まれます。
方法3:version 列で衝突を見つける(楽観ロック)
行に version 列を持たせ、読んだときの version のままなら書く、という条件付きの UPDATE にします。
SELECT stock, version FROM items WHERE id = 1 \gset
SELECT pg_sleep(1) \gset
UPDATE items SET stock = :stock - 1, version = version + 1
WHERE id = 1 AND version = :version;
B が読んだ在庫 = 10 version = 1
A が読んだ在庫 = 10 version = 1
UPDATE 1
UPDATE 0
stock | version
-------+---------
9 | 2
後から書いた方は UPDATE 0(0行更新)になりました。 在庫は 9 で、A の分は書かれていません。アプリは「0行だった」ことを見て、読み直してやり直すか、利用者に「他の人が先に更新しました」と知らせます。エラーにならないので、0行のときの処理を書き忘れると、更新が黙って捨てられます。
方法4:分離レベルを REPEATABLE READ にする
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT stock FROM items WHERE id = 1 \gset
SELECT pg_sleep(1) \gset
UPDATE items SET stock = :stock - 1 WHERE id = 1;
COMMIT;
A が読んだ在庫 = 10
B が読んだ在庫 = 10
UPDATE 1
COMMIT
ERROR: could not serialize access due to concurrent update
ROLLBACK
stock
-------
9
後の方は could not serialize access due to concurrent update(エラーコード 40001)で止まりました。 ドキュメントは、このエラーが出たらトランザクションを最初からやり直すよう勧めています。
| 方法 | 結果(在庫10から2個) | 後から来た方 | 向いている場面 |
|---|---|---|---|
| UPDATE の中で計算 | 8 | 待ってから正しく実行 | 在庫・ポイントの増減 |
FOR UPDATE(悲観ロック) |
8 | 約 0.97 秒待った | 短い処理で衝突が多い |
| version 列(楽観ロック) | 9(B を拒否) | UPDATE 0 |
画面で編集して保存する |
| REPEATABLE READ | 9(B を拒否) | エラー 40001 | 複数の読み取りを揃えたい |
| 何もしない | 9 | 黙って上書き | ― |
楽観ロックと悲観ロックの選び方は関連記事「楽観ロックと悲観ロックの違い|PostgreSQL実測でわかる使い分け」で、分離レベルごとに起きる現象は関連記事「トランザクション分離レベルの違いをわかりやすく解説」で扱っています。
ACIDのC(一貫性)と整合性の関係
この節では、ACID の「C」が整合性とどうつながるかが分かります。
ACID は、トランザクションが持つべき4つの性質の頭文字です。 PostgreSQL の用語集の定義を、この記事の実験と対応させると次のとおりです。
| 性質 | 用語集の説明(要約) | この記事で見た動き |
|---|---|---|
| A 原子性 | 全部完了するか、どれも完了しない | 途中で失敗した振替が全部戻った |
| C 一貫性 | データが常に整合性制約に従っている | CHECK に反した振替が全体ごと戻った |
| I 分離性 | 確定前の変更は他から見えない | 同時更新で待つ・拒否される |
| D 永続性 | 確定した変更は障害の後も残る | ― |
用語集の Consistency の定義には、「トランザクションは確定する前なら一時的に制約に反してもよいが、確定までに解消されなければ自動でロールバックされる」という説明もあります。これを体験できるのが、検査を COMMIT まで遅らせる DEFERRABLE INITIALLY DEFERRED の制約です。
CREATE TABLE emp (
id int PRIMARY KEY,
boss_id int REFERENCES emp(id) DEFERRABLE INITIALLY DEFERRED
);
BEGIN;
INSERT INTO emp VALUES (1, 2); -- 上司2はまだいない
INSERT INTO emp VALUES (2, NULL); -- 上司2を後から入れる
COMMIT; -- 成功
BEGIN;
INSERT INTO emp VALUES (3, 9); -- 上司9は最後までいない
SELECT 'まだエラーにならない' AS 状態;
COMMIT;
COMMIT
INSERT 0 1
状態
----------------------
まだエラーにならない
(1 row)
ERROR: 23503: insert or update on table "emp" violates foreign key constraint "emp_boss_id_fkey"
DETAIL: Key (boss_id)=(9) is not present in table "emp".
1つ目は途中で「上司がいない」状態を通りますが、COMMIT の時点で解消しているので成功しました。2つ目は COMMIT の時点でも上司9がいないため、COMMIT でエラーになり全体が戻りました。
ACID の C は「制約を守った状態から、制約を守った状態へ移る」ことで、守る中身(何が整合しているか)は、テーブルに書いた制約が決めます。 制約を書いていない決まりは、データベースは守れません。
結果整合性とは(強い整合性との違い)
この節では、分散システムで使われる「結果整合性」が何で、データベースの整合性とどう違うかが分かります。
結果整合性(eventual consistency)は、データのコピーが複数あるとき、更新直後はコピーによって値が食い違うことがあるが、時間がたつと最終的に揃う、という整合性のモデルです。 更新の直後から、どのコピーを読んでも最新の値が返るものを「強い整合性(strong consistency)」と呼びます。
ここでいう整合性は、分散システムの consistency(コピー同士が同じ値か)で、ここまでの制約の話(integrity)とは見ている所が違います。
実測:レプリカが2秒間、更新前の値を返した
PostgreSQL のレプリカ(ストリーミングレプリケーションのコピー)を作り、反映を2秒遅らせる設定(recovery_min_apply_delay = '2s')にしました。プライマリで残高を 10,000 から 7,000 に変え、レプリカを 0.5 秒おきに読みます。
replica(before): 10000
UPDATE 1
primary : 7000
0.6秒後 replica: 10000
1.2秒後 replica: 10000
1.7秒後 replica: 10000
2.3秒後 replica: 7000
2.8秒後 replica: 7000
3.4秒後 replica: 7000
プライマリは 7,000 なのに、レプリカは約2秒間 10,000 を返し、その後に揃いました。 これが結果整合性です。レプリカへの書き込みは ERROR: cannot execute UPDATE in a read-only transaction で拒否されるので、レプリカで食い違いが「増える」ことはありません。読む場所によって、遅れた値が見えるだけです。
flowchart TD
W[プライマリに<br>7000 を書く] --> P[プライマリを読む<br>すぐ 7000]
W --> R[レプリカを読む<br>しばらく 10000]
R --> E[時間がたつと<br>7000 に揃う]
強い整合性
書いた直後から、どこを読んでも最新の値
残高・在庫・決済など、ずれると困るもの
待ち時間が増えやすい
結果整合性
書いた直後は、前の値が見えることがある
いいね数・閲覧数・検索の索引など
速く、止まりにくい
クラウドのサービスでの例
| サービス | 整合性 | 公式ドキュメントの説明(要約) |
|---|---|---|
| Amazon DynamoDB の読み取り | 既定は結果整合性 | 直前の書き込みが反映されないことがある。ConsistentRead を true にすると強い整合性。結果整合性の読み取りは半分のコスト |
| DynamoDB のグローバルセカンダリインデックス | 結果整合性のみ | 強い整合性の読み取りには対応していない |
| Amazon S3 のオブジェクト | 強い整合性 | PUT・DELETE の直後の読み取りや一覧に新しい内容が反映される |
| Amazon S3 のバケットの設定 | 結果整合性 | 削除したバケットが直後の一覧に出ることがある |
CAP 定理
結果整合性がなぜ選ばれるかは、CAP 定理で説明されます。Eric Brewer 氏の記事では、ネットワークでデータを共有するシステムは、一貫性(C:最新のコピーが1つあるのと同じ状態)・可用性(A)・ネットワーク分断への耐性(P)の3つのうち、同時に2つまでしか持てない、とされます。同じ記事は、分断が起きている間は一貫性と可用性のどちらを取るかを選ぶ必要があるが、その扱い方には幅がある、とも述べています。
複数のサービスにまたがる更新を、最終的に揃える設計(補償トランザクション)は、関連記事「Sagaパターンとは|補償トランザクションの実装例と2PCとの違い」で扱っています。
整合性チェックのSQL(既存データの点検)
この節では、すでに入っているデータの整合性を点検する SQL と、汚れたデータに後から制約を足す手順が分かります。
制約の無いテーブル(例1〜3のデータ)に対して実行した結果です。どれも「0行なら整合している」形にしてあります。
1. 重複を探す(実体整合性)
SELECT email, count(*) AS 件数 FROM bad_customers GROUP BY email HAVING count(*) > 1;
email | 件数
------------------+------
sato@example.com | 2
(1 row)
2. 親のない子を探す(参照整合性)
SELECT o.* FROM bad_orders o
WHERE NOT EXISTS (SELECT 1 FROM bad_customers c WHERE c.id = o.customer_id);
id | customer_id | quantity | status
----+-------------+----------+--------
11 | 999 | 1 | paid
(1 row)
3. 範囲外・候補外の値を探す(ドメイン整合性)
SELECT * FROM bad_orders
WHERE quantity <= 0 OR status NOT IN ('new', 'paid', 'shipped', 'cancelled');
id | customer_id | quantity | status
----+-------------+----------+--------
12 | 1 | -3 | piad
(1 row)
4. 合計が合わないものを探す(業務ルール)
注文のヘッダーに持っている合計と、明細から計算した合計を比べます。
SELECT h.id, h.total, sum(l.price * l.qty) AS 明細の合計
FROM order_heads h JOIN order_lines l ON l.order_id = h.id
GROUP BY h.id, h.total
HAVING h.total <> sum(l.price * l.qty);
id | total | 明細の合計
----+-------+------------
2 | 5000 | 4000
(1 row)
5. テーブルに付いている制約を一覧する
SELECT conrelid::regclass AS テーブル, conname AS 制約名, contype AS 種類, convalidated AS 検証済み
FROM pg_constraint
WHERE connamespace = 'public'::regnamespace
ORDER BY 1, 2;
テーブル | 制約名 | 種類 | 検証済み
------------+-------------------------+------+----------
bad_orders | bad_orders_customer_fk | f | f
customers | customers_email_key | u | t
customers | customers_pkey | p | t
orders | orders_customer_id_fkey | f | t
orders | orders_pkey | p | t
orders | orders_quantity_check | c | t
orders | orders_status_check | c | t
種類の p は主キー、u は UNIQUE、f は外部キー、c は CHECK です(一部のテーブルだけを抜き出しています)。PostgreSQL 16 では NOT NULL はここには出ず、列の定義(\d テーブル名)で確かめます。
汚れたデータに後から制約を足す
崩れたデータが残ったままでは、制約は追加できません。
=> ALTER TABLE bad_customers ADD PRIMARY KEY (id);
ERROR: could not create unique index "bad_customers_pkey"
DETAIL: Key (id)=(1) is duplicated.
=> ALTER TABLE bad_orders ADD CHECK (quantity > 0);
ERROR: check constraint "bad_orders_quantity_check" of relation "bad_orders" is violated by some row
=> ALTER TABLE bad_orders ADD FOREIGN KEY (customer_id) REFERENCES customers(id);
ERROR: insert or update on table "bad_orders" violates foreign key constraint "bad_orders_customer_id_fkey"
DETAIL: Key (customer_id)=(999) is not present in table "customers".
すぐに直せないときは、外部キーと CHECK に NOT VALID を付けると「今あるデータは検査せず、これからの書き込みだけ検査する」形で追加できます。
=> ALTER TABLE bad_orders ADD CONSTRAINT bad_orders_customer_fk
FOREIGN KEY (customer_id) REFERENCES customers(id) NOT VALID;
ALTER TABLE
=> INSERT INTO bad_orders VALUES (14, 777, 1, 'new');
ERROR: insert or update on table "bad_orders" violates foreign key constraint "bad_orders_customer_fk"
DETAIL: Key (customer_id)=(777) is not present in table "customers".
=> ALTER TABLE bad_orders VALIDATE CONSTRAINT bad_orders_customer_fk;
ERROR: insert or update on table "bad_orders" violates foreign key constraint "bad_orders_customer_fk"
DETAIL: Key (customer_id)=(999) is not present in table "customers".
新しい崩れ(会員777)はすぐ止まり、前から残っていた崩れ(会員999)は VALIDATE CONSTRAINT のときに見つかりました。 手順は次のとおりです。
物理的な整合性:チェックサムで壊れ・改ざんを見つける
この節では、保存や転送の途中でデータが壊れていないかを確かめる方法が分かります。
物理的な整合性は、ディスクの故障・転送の失敗・不正な書き換えで、データのバイト列そのものが変わっていないかを見るものです。 制約は「意味の矛盾」を止めますが、バイトが壊れることは止められません。これを見つけるのがチェックサム(ハッシュ値)です。
ファイルのハッシュ値で確かめる
約 589KB の CSV をコピーし、片方の1バイト(101バイト目)だけを書き換えました。サイズは同じでも、SHA-256 のハッシュ値はまったく違う値になりました。
$ sha256sum sales.csv copy_ok.csv copy_ng.csv
b2bc7d3f8b652d2ec96865b68ad8f80e22cca174abe1aed7889e242a747d590f sales.csv
b2bc7d3f8b652d2ec96865b68ad8f80e22cca174abe1aed7889e242a747d590f copy_ok.csv
1d50b2b8d2254c612422e5375fc181a0456d4187631192ca9c72300086cbb9b5 copy_ng.csv
送る側でハッシュ値をファイルに残し、受け取った側で -c で照合すると、壊れたときに FAILED と終了コード 1 が返ります。
$ sha256sum -c sales.csv.sha256
sales.csv: OK
$ sha256sum -c sales.csv.sha256 # 壊れたファイルで
sales.csv: FAILED
sha256sum: WARNING: 1 computed checksum did NOT match
PostgreSQL のデータチェックサムと pg_amcheck
PostgreSQL には、データファイルのページごとにチェックサムを付け、読み込むときに壊れを見つける機能(データチェックサム)があります。PostgreSQL 16 で作ったクラスタでは、既定は無効でした。
=> SHOW data_checksums;
data_checksums
----------------
off
PostgreSQL 18 のリリースノートには「initdb の既定をデータチェックサム有効に変更」とあります。 16 以前では initdb --data-checksums(-k)で有効にし、停止中のクラスタなら pg_checksums で有効化・検査ができます。
テーブルと索引の中身が壊れていないかは、amcheck 拡張と pg_amcheck コマンドで検査できます。問題が無ければ何も出力せず、終了コード 0 で終わりました。
=> CREATE EXTENSION amcheck;
$ pg_amcheck -d s281db
$ echo $?
0
整合性はどこで守るか(アプリ・DB・点検)と崩れる原因
この節では、整合性を守る場所の選び方と、崩れる主な原因ごとの対策が分かります。
崩れる原因と対策
| 原因 | 起きること | 主な対策 |
|---|---|---|
| 人の入力ミス | 範囲外の値・重複・打ち間違い | 入力の検証・CHECK・UNIQUE |
| 移行・連携での変換ミス | 文字化け・型の暗黙変換・件数の欠け | 件数と合計の照合・ハッシュ値の照合 |
| 途中で止まる障害 | 片方だけ書かれた更新 | トランザクション |
| 同時の更新 | ロストアップデート | 楽観ロック・悲観ロック |
| 権限のある人・ない人の不正な変更 | 気づかない書き換え | アクセス制御・監査ログ |
| テーブル定義の変化 | 連携先が変更前の定義のまま読み書きする | マイグレーションの管理・点検SQL |
| ディスクの故障 | ページの破損 | データチェックサム・バックアップ・冗長化 |
監査ログで「誰がいつ変えたか」を残す
データセキュリティ(権限)では、権限のある人の誤操作は止められません。変更の前後を残しておくと、崩れた後に原因と元の値を追えます。PostgreSQL のトリガーで、口座の変更を記録しました。
CREATE TABLE audit_log (
at timestamptz NOT NULL DEFAULT now(),
who text NOT NULL DEFAULT current_user,
tbl text NOT NULL,
op text NOT NULL,
old_row jsonb,
new_row jsonb
);
CREATE FUNCTION audit() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
INSERT INTO audit_log (tbl, op, old_row, new_row)
VALUES (TG_TABLE_NAME, TG_OP,
CASE WHEN TG_OP <> 'INSERT' THEN to_jsonb(OLD) END,
CASE WHEN TG_OP <> 'DELETE' THEN to_jsonb(NEW) END);
RETURN NULL;
END $$;
CREATE TRIGGER accounts_audit AFTER INSERT OR UPDATE OR DELETE ON accounts
FOR EACH ROW EXECUTE FUNCTION audit();
=> UPDATE accounts SET balance = balance + 500 WHERE id = 2;
=> SELECT who, tbl, op, old_row ->> 'balance' AS 変更前, new_row ->> 'balance' AS 変更後 FROM audit_log;
who | tbl | op | 変更前 | 変更後
----------+----------+--------+--------+--------
postgres | accounts | UPDATE | 0 | 500
守る場所の判断表
| 守りたいもの | データベースの制約 | アプリの検証 | 定期の点検SQL |
|---|---|---|---|
| 重複しない・必須・範囲 | ◎ 必ず書く | ○ 分かりやすいエラー表示のため | ― |
| 参照先が実在する | ◎ 外部キー | △ | 外部キーを張れない連携先に |
| 同じ DB 内の複数の更新が揃う | ◎ トランザクション | ○ 境界を決める | ― |
| 複数行にまたがる決まり | ○ EXCLUDE・トリガー | ○ | ◎ 合計の一致など |
| 複数のサービス・DB にまたがる決まり | ― | ◎ Saga・冪等キー | ◎ 突き合わせ |
| バイト列が壊れていない | ○ データチェックサム | ― | ◎ ハッシュ値の照合・pg_amcheck |
アプリの検証だけに頼ると、別のアプリ・バッチ・手作業の SQL から入ったデータは素通りします。 例1〜3の実験のとおり、制約の無いテーブルは何でも受け付けます。決まりはデータベースに制約として書き、アプリの検証は「利用者に分かりやすいエラーを早く出すため」に重ねて書く、という分担がよく取られます。
整合性エラーの原文と直し方
この節では、この記事の実験で出た整合性まわりのエラー原文と、直し方が分かります。
| エラー原文(PostgreSQL) | 原因 | 直し方 |
|---|---|---|
duplicate key value violates unique constraint "…_pkey" |
主キーの重複 | ID の採番を見直す。登録済みなら UPDATE か ON CONFLICT |
duplicate key value violates unique constraint "…_key" |
UNIQUE の列(メールなど)の重複 | 既存の行を確かめ、入力側で重複を知らせる |
null value in column "…" violates not-null constraint |
必須の列が空 | 値を渡しているか、既定値(DEFAULT)を確かめる |
insert or update on table "…" violates foreign key constraint |
親が存在しない | 先に親を登録する。ID の打ち間違いを確かめる |
update or delete on table "…" violates foreign key constraint … on table "…" |
子が残る親の削除 | 子を先に消すか、ON DELETE CASCADE・SET NULL を設計する |
new row for relation "…" violates check constraint "…" |
CHECK の条件外 | 値を直す。決まりが変わったなら制約を変える |
conflicting key value violates exclusion constraint |
期間などの重なり | 重ならない値にする。既存の予約を確かめる |
current transaction is aborted, commands ignored until end of transaction block |
同じトランザクションの前の文が失敗 | ROLLBACK して最初からやり直す |
could not serialize access due to concurrent update |
REPEATABLE READ 以上で同時に更新した | トランザクションを最初からやり直す |
check constraint "…" is violated by some row |
既存データに違反がある状態で CHECK を追加 | 違反行を直すか、NOT VALID で追加して後で検証 |
could not create unique index "…" / Key (…) is duplicated. |
重複がある状態で主キー・UNIQUE を追加 | 重複を消してから追加する |
cannot execute UPDATE in a read-only transaction |
レプリカ(読み取り専用)に書いた | 書き込みはプライマリへ送る |
よくある質問
Q. 整合性とは、わかりやすく言うと何ですか? A. 2つ以上のものの間に矛盾が無く、きちんと合っていることです。ITでは「データ同士やデータと決まりごとが食い違っていない状態」を指します。冒頭の早見表に、崩れる形と防ぐ仕組みをまとめています。
Q. 「整合性をとる」とはどういう意味ですか? A. 食い違っているものを、矛盾が無いように合わせることです。「仕様書と設計書の整合性をとる」のように使います。「整合性を合わせる」は意味が重なるため、「整合性をとる」「整合性を図る」がよく使われます。
Q. 整合性を英語で言うと? A. 食い違いが無いことは consistency、正しく欠けていない・壊れていないことは integrity です。データベースの「整合性制約」は integrity constraint、分散システムの「結果整合性」は eventual consistency です。
Q. 整合性と一貫性の違いは何ですか? A. 一般の言葉では、整合性は「複数のものが食い違わない」、一貫性は「始めから終わりまで変わらない」ことです。データベースでは、ACID の一貫性(C)は「トランザクションの前後でデータが整合性制約を守っている」ことを指し、整合性を守るための性質として使われます。
Q. 整合性と完全性の違いは何ですか? A. 情報セキュリティの完全性(integrity)は「正確さ及び完全さの特性」で、データが壊れていない・改ざんされていないことです。データ整合性は、それに加えて主キー・外部キーなどの決まりに矛盾が無いことまで含めて使われます。
Q. データの整合性を保つ方法は?
A. データベースに制約(主キー・UNIQUE・外部キー・CHECK・NOT NULL)を書き、一緒に成功すべき更新をトランザクションで囲み、同時の更新には楽観ロックか FOR UPDATE を使います。既存のデータは点検用SQLで定期的に確かめます。
Q. 参照整合性とは何ですか? A. ある表の値が別の表の行を指すとき、指している先が必ず実在することです。外部キーで守ります。この記事の実験では、存在しない会員999の注文と、注文が残る会員の削除を、外部キーが止めました。
Q. 結果整合性とは何ですか? A. 複数のコピーがあるとき、更新直後はコピーによって前の値が見えることがあるが、時間がたつと最終的に揃う、という整合性のモデルです。この記事の実験では、レプリカが約2秒間、更新前の値を返しました。
Q. 整合性チェックとは何をすることですか?
A. データに矛盾が無いかを調べることです。データベースでは、重複・親のない子・範囲外の値・合計の不一致を探す SQL を流し、0行であることを確かめます。ファイルではハッシュ値の照合(sha256sum -c)を使います。
Q. 整合性はどうやって測り、どのくらいの頻度で点検しますか? A. 点検用SQLが返した行の数(違反件数)が、そのまま測る値になります。制約で守れるものは書き込みのたびにデータベースが検査するので、点検は制約で守れない業務ルールや、データ移行・システム連携の後に行うのが目安です。
まとめ
- 整合性とは、矛盾がなく整っていること。「整合性をとる」「整合性がない」と使い、「整合性が合う」は意味が重なる。
- ITでは、データ同士・データと決まりの間に食い違いが無い状態(データ整合性)を指す。種類は物理的な整合性と、実体・参照・ドメイン・業務ルールの論理的な整合性。
- 制約が無いと、二重登録で注文が4件に数えられ、途中失敗で 3,000 円が消え、同時更新で在庫が 9 のままになった。
- 主キー・UNIQUE・外部キー・CHECK・NOT NULL・EXCLUDE が、まずい書き込みをエラー(クラス 23)で止める。
- トランザクションは途中で失敗しても全部を戻す。同時更新は、UPDATE の中で計算・
FOR UPDATE・version 列・REPEATABLE READ のどれかで防ぐ。 - ACID の C は「制約を守った状態のまま移る」こと。結果整合性は分散システムの考え方で、レプリカは約2秒遅れて揃った。
- 既存データは点検用SQLで探し、
NOT VALID→ 修正 →VALIDATE CONSTRAINTの順で制約を足す。バイトの壊れはハッシュ値・データチェックサムで見つける。
参考・出典
確認日はいずれも 2026年10月22日です。
- PostgreSQL 16 Documentation「Glossary」:https://www.postgresql.org/docs/16/glossary.html
- PostgreSQL 16 Documentation「5.4. Constraints」:https://www.postgresql.org/docs/16/ddl-constraints.html
- PostgreSQL 16 Documentation「13.2. Transaction Isolation」:https://www.postgresql.org/docs/16/transaction-iso.html
- PostgreSQL 16 Documentation「Appendix A. PostgreSQL Error Codes」:https://www.postgresql.org/docs/16/errcodes-appendix.html
- PostgreSQL「Release 18」:https://www.postgresql.org/docs/release/18.0/
- Amazon DynamoDB 開発者ガイド「DynamoDB の読み取り整合性」:https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html
- Amazon S3 User Guide「What is Amazon S3?(Amazon S3 data consistency model)」:https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html
- Eric Brewer「CAP Twelve Years Later: How the "Rules" Have Changed」(InfoQ):https://www.infoq.com/articles/cap-twelve-years-later-how-the-rules-have-changed/
- JIPDEC「ISMS 適合性評価制度」パンフレット:https://www.jipdec.or.jp/archives/publications/ismspamph.pdf
- GDPR Article 5「Principles relating to processing of personal data」:https://gdpr-info.eu/art-5-gdpr/
- コトバンク「整合性」(精選版 日本国語大辞典):https://kotobank.jp/word/整合性-544493
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「整合性とは|意味とデータ整合性の種類・崩れる例と守り方」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。