77:トランザクション内はなるべく短い時間で処理する

Webアプリケーションの実装で、ブラウザからのリクエスト処理開始時にデータベースのトランザクションを開始してしまうと、さまざまな問題の原因となります。 データベースのトランザクションは、何か問題があった場合に中途半端なデータ更新を行わないようにするために利用されます。

具体的な失敗

たとえばWebで商品の購入しようとしたとき、内部で何かのエラーが発生して商品の購入が失敗したのに商品の出荷が始まってしまっては困ります。 こういった場合、開始したトランザクションを確定せずにロールバックすることで問題を回避します。 Djangoでは、トランザクションを開始する関数呼び出しを明示的に実装する方法と、viewの呼び出し時にトランザクションを自動的に開始する設定 ATOMIC_REQUESTS があります。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        ...
        'ATOMIC_REQUESTS': True,
    }
}

ATOMIC_REQUESTS は便利な設定ですが、これを利用した状態では意図しないテーブルロックが発生することがあります。 テーブルがロックされた場合、同時にアクセスしている他の処理ではそのテーブルの更新ができなくなり、ロック解除まで更新が待たされます。 また、複数のトランザクション処理がテーブルのロックを奪い合う状況では、 デッドロック によるエラーも発生します。

このシステム障害は、アクセスが集中したり、負荷などによってリクエスト処理時間が長引くことでランダムに発生します 。 しかし、開発中やシステム運用開始直後など、アクセス数が少なく負荷が低い状態ではほとんど発生しません。

cover

(中略)詳細は書籍 自走プログラマー をご参照ください

ベストプラクティス

トランザクション内で時間がかかる処理を行わないようにしましょう。 具体的には以下の複数の観点で対策します。

  • リクエスト全体をトランザクションとする場合、リクエスト処理にかかる時間を短くする

  • トランザクション処理を自動にせず、必要最小限の範囲に明示的に設定する

  • データベースのトランザクション分離レベルを設計時に選択する

cover

(中略)詳細は書籍 自走プログラマー をご参照ください