私の連絡先情報
郵便メール:
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
データベースの同時運用環境では、複数のトランザクションが同時に同じデータを更新しようとすると、競合が発生する可能性があります。 PostgreSQL は、データの一貫性と整合性を確保するために、これらの同時更新の競合を処理する一連のメカニズムを提供します。
同時更新の競合は、2 つ以上のトランザクションがデータの同じ行を同時に変更しようとすると発生することがあります。一般的なシナリオには次のようなものがあります。
PostgreSQL は主に MVCC (Multiversion Concurrency Control) を使用して同時トランザクションを処理します。 MVCC を使用すると、トランザクションは、他のトランザクションの読み取り操作をブロックするロックを行わずに、分離レベルの要件を満たすデータ バージョンを読み取ることができます。ただし、書き込み操作中に競合が発生する可能性があります。
PostgreSQL は、データへの同時アクセスを制御するためにいくつかのタイプのロックを使用します。一般的なロックのタイプは次のとおりです。
ロックの粒度は、行レベル (Row-Level)、ページ レベル (Page-Level)、およびテーブル レベル (Table-Level) です。
PostgreSQL は 4 つのトランザクション分離レベルをサポートしています。
簡単なアプローチは、競合が発生したときにトランザクションを再試行することです。例は次のとおりです。
DO
$$
DECLARE
conflict_detected BOOLEAN := FALSE;
BEGIN
LOOP
-- 尝试执行更新操作
UPDATE products SET price = 100 WHERE id = 1;
-- 检查是否有冲突(例如,通过检查受影响的行数)
IF NOT FOUND THEN
conflict_detected := TRUE;
ELSE
EXIT;
END IF;
-- 若有冲突,等待一段时间并重试
IF conflict_detected THEN
PERFORM pg_sleep(1);
END IF;
END LOOP;
END;
$$;
上記の例では、更新操作がどの行にも影響しない場合 (競合がある可能性があることを示します)、フラグが設定され、一定期間待機してから再試行されます。
オプティミスティック同時実行制御は、同時実行の競合がほとんど発生しないことを前提としています。このメソッドでは、トランザクションはデータを更新するときにロックしませんが、コミット時にデータが他のトランザクションによって変更されているかどうかを確認します。競合がない場合、トランザクションは正常にコミットされます。競合がある場合、トランザクションはロールバックされ、必要に応じて再試行されます。
-- 获取数据的初始版本
SELECT price AS original_price FROM products WHERE id = 1;
-- 进行业务处理和修改
UPDATE products SET price = 100 WHERE id = 1 AND price = original_price;
上記の例では、データが他のトランザクションによって変更されていない場合にのみ更新操作が成功します。
ペシミスティック同時実行制御では、同時実行の競合が発生する可能性が高いと想定し、トランザクションの実行中に必要なロックを取得して、競合する可能性のある他のトランザクションをブロックします。
BEGIN;
-- 获取排他锁
LOCK TABLE products IN SHARE ROW EXCLUSIVE MODE;
-- 进行数据更新
UPDATE products SET price = 100 WHERE id = 1;
COMMIT;
データの変更を追跡するには、テーブルにバージョン フィールドを追加します。
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price DECIMAL(10, 2),
version INT DEFAULT 0
);
データを更新するときは、バージョン フィールドもインクリメントします。
UPDATE products SET price = 100, version = version + 1 WHERE id = 1 AND version = <expected_version>;
更新の影響を受ける行数が 0 の場合、予期されるバージョンが実際のバージョンと一致しないため、競合が発生します。
データの各行にタイムスタンプ フィールドを追加して、データの最終変更時刻を記録します。
CREATE TABLE products (
id SERIAL PRIMARY KEY,
price DECIMAL(10, 2),
last_modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
更新するときは、現在のトランザクションによって読み取られたタイムスタンプよりも古いタイムスタンプを持つデータのみを更新します。
UPDATE products SET price = 100 WHERE id = 1 AND last_modified <= <read_timestamp>;
オンライン ストアの在庫管理システムがあるとします。 inventory
商品の在庫数量を格納するテーブル。
CREATE TABLE inventory (
product_id INT PRIMARY KEY,
quantity INT,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
現在、2 つの同時トランザクションがあります。
トランザクション 1:
BEGIN;
SELECT * FROM inventory WHERE product_id = 1;
-- 假设读取到的数量为 10
UPDATE inventory SET quantity = 5 WHERE product_id = 1 AND last_updated <= <read_timestamp>;
COMMIT;
トランザクション 2:
BEGIN;
SELECT * FROM inventory WHERE product_id = 1;
-- 假设也读取到的数量为 10
UPDATE inventory SET quantity = 8 WHERE product_id = 1 AND last_updated <= <read_timestamp>;
COMMIT;
これら 2 つのトランザクションがほぼ同時に実行されると、競合が発生する可能性があります。
タイムスタンプベースの競合解決を使用する場合:
T1
)。T2
)。トランザクション 1 が更新しようとしたとき、データを読み取ってから他のトランザクションがそのデータを変更していない場合 (つまり、 last_updated <= T1
)、更新は成功しました。
トランザクション 2 が更新しようとしたときに、データが last_updated
以上T2
(トランザクション 2 が読み取った後に変更されたことを示します)、更新は失敗し、トランザクション 2 はロールバックして再試行するか、ビジネス ロジックに従って他の処理を実行するかを選択できます。
🎉相关推荐