SQL: 設計そのものが競合バグを招く

SQL: Incorrect by Construction

SQLの設計は、一見合理的なコードでも深刻な並行処理バグを招きやすい。トランザクションの原子性不足、TOCTOU問題、デッドロックなど、単純な送金処理ですら修正に多大な手間がかかる。私はRustのような「安全な並行処理」をデフォルトにする新しいデータベースツールが必要だと主張する。

SQLプログラムは完全に合理的に見えるのに、深刻なバグで溢れている。
  1. tcp_handshaker

    投稿は、SQL Server のロックベースの READ COMMITTED デフォルト設定の下での初心者的な T-SQL を示し、それを「SQL は設計上誤っている」と一般化しています。

    結論はずっと狭い範囲であるはずです。同じ行への二重引き落としについては、RCSI だけでなく、実際に有効化され使用されている真の SNAPSHOT 隔離レベルを使用すれば、敗者は更新コンフリクト/リトライになります。SQL の慣習的な修正方法は、条件付きの引き落とし UPDATE、行数チェック、制約、そして転送のための 1 つのトランザクションです。

    デッドロックの例も誇張されています。SQL Server はサイクルを検出し、犠牲者をロールバックしてエラー 1205 [1] を返し、アプリケーションはそれをリトライすることが期待されます。

    Microsoft のデフォルト設定は最悪で、著者はそれをずっと広い主張に変えてしまいました..

    [1] - "Your transaction (process ID #...) was deadlocked on {lock | communication buffer | thread} resources with another process and has been chosen as the deadlock victim. Rerun your transaction"

    "Deadlocks guide" - https://learn.microsoft.com/en-us/sql/relational-databases/s...

  2. hilariously

    一般的には、ここで一連の更新ではなく、追記専用データ構造を推奨します。

    BEGIN TRANSACTION;

    IF EXISTS (

    SELECT 1

    FROM account_balances WITH (UPDLOCK, HOLDLOCK)

    WHERE owner = 'alice'

    AND balance >= 10

    )

    BEGIN

    INSERT INTO account_ledger (owner, amount, memo)

    VALUES

    ('alice', -10, 'transfer to bob'),

    ('bob', 10, 'transfer from alice');

    END

    COMMIT TRANSACTION;

    あるいは、ロックを既に取得しているため更新パターンを使用したい場合は、

    BEGIN TRANSACTION;

    UPDATE accounts WITH (UPDLOCK)

    SET balance = balance - 10

    WHERE owner = 'alice'

    AND balance >= 10;

    IF @@ROWCOUNT = 1

    BEGIN

    UPDATE accounts

    SET balance = balance + 10

    WHERE owner = 'bob';

    END

    COMMIT TRANSACTION;

  3. crazygringo

    これはまさに、トランザクションとロックに関する SQL 101 です。これらはデータベースにおける基本的な初歩的な概念です。

    「設計上誤っている」ということはありません。

    著者は元のスニペットが「完全に妥当に見える」と主張していますが、クライアント - サーバーデータベースについて少しでも知っていれば、それは絶対にそうではありません。

この日のほかの記事

2026-07-07