0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「行ロックを活かす設計」と「再試行ロジック」の具体的な方法を紹介

Posted at

SQL Server 向けに、
「行ロックを活かす設計」と「再試行ロジック」の具体的な方法を紹介します。


1. 行ロックを活かす設計

(1) インデックスを正しく設定

  • WHERE 句に使うカラムに適切なインデックスを付与
  • テーブルスキャンが発生すると、意図せず広範囲のロック(ページロックやテーブルロック)が発生しやすくなる
-- 例: 顧客IDごとに更新する場合
CREATE NONCLUSTERED INDEX IX_Orders_CustomerId
    ON Orders(CustomerId);

(2) ヒントを使ってロックの粒度を制御

SQL Server ではロックヒントを明示することで、不要なテーブルロックを防げます。

-- 行ロックを強制する例
UPDATE Orders WITH (ROWLOCK)
SET Status = 'Completed'
WHERE OrderId = 12345;
-- 行ロック+更新時の競合回避
SELECT * FROM Orders WITH (ROWLOCK, UPDLOCK)
WHERE OrderId = 12345;
  • ROWLOCK → 行ロックを強制
  • UPDLOCK → 更新のためのロックを確保(読んだ時点で更新予定を示す)

※ヒントはやりすぎると逆にスループット低下を招くので、競合が激しい部分だけに使うのがコツです。


(3) トランザクションを短くする

BEGIN TRAN;

UPDATE Orders WITH (ROWLOCK)
SET Status = 'Completed'
WHERE OrderId = 12345;

COMMIT;
  • なるべく処理をまとめすぎない
  • SELECT → UPDATE → 別テーブル UPDATE → COMMIT みたいに長いとロック競合が増える

2. 再試行ロジックの実装

デッドロックは「完全にゼロ」にできないので、アプリ側で「失敗したらリトライ」する仕組みが現実的です。
SQL Server はデッドロックが起きると 1205 (Deadlock victim) エラーを返すので、それをキャッチしてリトライします。


(1) T-SQL 内で再試行

DECLARE @retryCount INT = 0;
DECLARE @maxRetry INT = 3;

WHILE @retryCount < @maxRetry
BEGIN
    BEGIN TRY
        BEGIN TRAN;

        UPDATE Orders WITH (ROWLOCK)
        SET Status = 'Completed'
        WHERE OrderId = 12345;

        COMMIT;
        BREAK; -- 成功したらループを抜ける
    END TRY
    BEGIN CATCH
        IF ERROR_NUMBER() = 1205 -- Deadlock victim
        BEGIN
            ROLLBACK;
            SET @retryCount += 1;
            WAITFOR DELAY '00:00:01'; -- 少し待ってリトライ
        END
        ELSE
        BEGIN
            ROLLBACK;
            THROW; -- 別のエラーなら即時スロー
        END
    END CATCH
END

(2) C# / Java などアプリ側でのリトライ例

C# の場合:

int retries = 3;
for (int i = 0; i < retries; i++)
{
    try
    {
        using (var tran = connection.BeginTransaction())
        {
            var cmd = new SqlCommand(
                "UPDATE Orders WITH (ROWLOCK) SET Status='Completed' WHERE OrderId=@OrderId",
                connection, tran);
            cmd.Parameters.AddWithValue("@OrderId", 12345);
            cmd.ExecuteNonQuery();
            tran.Commit();
        }
        break; // 成功
    }
    catch (SqlException ex) when (ex.Number == 1205) // Deadlock victim
    {
        if (i == retries - 1) throw; // 最後の試行でも失敗したら投げる
        Thread.Sleep(1000); // 待機してリトライ
    }
}

まとめ

  • 行ロック活用ROWLOCK / UPDLOCK ヒント、インデックス最適化、短いトランザクション
  • 再試行ロジック:1205 エラーをキャッチしてリトライ(T-SQL またはアプリ側で実装)

この2つを組み合わせると「実運用で耐えられる」仕組みになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?