SQLでは、トランザクションを使用して複数の操作(insert/update/delete)を1つのアトミックな作業単位にまとめて、完全に適用するか、または取り消すことができます。トランザクションのライフサイクルは次のコマンドに基づいて構築されます:
BEGIN または START TRANSACTION — トランザクションの開始;COMMIT — 変更の確定;ROLLBACK — トランザクション内のすべての変更の取り消し。SQLはトランザクションの分離レベル(Read Uncommitted, Read Committed, Repeatable Read, Serializable)をサポートしており、これによって並行トランザクション間のデータの可視性が決まり、「ダーティリード」や「ファントム行」のような問題を防ぎます。
データの整合性を管理するためには、次のことが必要です:
SELECT ... FOR UPDATE)。PostgreSQLの例:
BEGIN; -- 商品の行を取得してロックする SELECT * FROM inventory WHERE id = 1 FOR UPDATE; UPDATE inventory SET quantity = quantity - 1 WHERE id = 1; COMMIT;
人気のあるDBMS(PostgreSQL, MySQL)でデフォルトに設定されている分離レベルは何で、それはSERIALIZABLEとどのように異なりますか?
回答:
PostgreSQLではデフォルトでRead Committedレベルが使用されており、それによりトランザクションはリクエストの時点で確定されたデータのみを見ますが、「不再読」が発生する可能性があります。
MySQL(InnoDB)ではRepeatable Readです。Serializableとの違いは、最後のものがファントムや並行の変更を完全に防ぐことができるが、グローバルロックのために作業がかなり遅くなることです。
例:
-- Repeatable ReadではSELECTが同じ行を返すことができますが、Read Committedではトランザクション内の2つのSELECTの間に新しい行が現れる可能性があります。
歴史
大規模な金融システムにおいて、低い分離レベル(Read Committed)での大量送金中に同じ残高が複数のトランザクションで同時に使用される状況が時折発生しました。これにより資金の二重支出(レースコンディション)が発生しました。
Serializableへの移行と適切なロック管理により、この問題は解決しました。
歴史
電子商取引では、
UPDATE product SET stock = stock - 1のトランザクションを行わないと、在庫を超えて商品が販売される結果となりました。この問題は、多くの競合注文があった場合にのみ発覚しました。解決策は、トランザクションを利用し、SELECT ... FOR UPDATEを使用して行ロックを行うことです。
歴史
ロジスティクスシステムでは、テーブルの頻繁な更新中に明示的なコミットを忘れていました。障害が発生した場合、オートコミットまたは不正なロールバックにより一部のデータが失われました。その結果、レコードの損失と高額な監査が発生しました。