ハブにはすでにDjangoモデルに関する多くの記事がありますが、熊手を踏まずにそれらを効果的に使用する方法を公開したいと思います。
開始データ
- DjangoがuWSGIで実行されている2台のサーバー
- 1秒あたり1-2kクエリ
- お金の動きを内部に含むプロジェクト
次は?
ユーザーに残高更新メソッドを実装するとします。 そして、このメソッドは次のようになります。
class Profile(models.Model): …. def update_balance(self, balance): self.balance += balance self.save()
この場合、残高を更新する2つのリクエストを同時に受け取ると、残高は2番目のリクエストのみを更新します。これは、最後のリクエストが最初のリクエストを押し出し、古いデータを取得したためです。
この段階で、 Fメソッドは.update()とともに助けになります。
F()は、現在の状態のデータベースから値を返します。 前のセクションは次のように書くことができます
class Profile(models.Model): …. def update_balance(self, balance): Profile.objects.\ filter(pk=self.pk)\ .update(balance=F('balance') + balance)
この場合、フィールドの実際の値を常に取得し、このメソッドは問題を解決すると言う人もいますが、そうではありません。 この場合、すべてが正しく実装されていますが、私たちが信じているように、これは問題を解決しません。
この場合、データベースレベルでのトランザクションが役立ちます。
トランザクションとは何ですか?
まず、Django 1.4.xおよび1.5.xでトランザクションミドルウェアを有効にすることができます。 Django 1.6以降では、ATOMIC_REQUESTS定数に置き換えられました。これは、プロジェクトで使用される各データベースに含めることができます。
彼らは次のように動作します。 リクエストが届き、この処理リクエストを表示する前に、Djangoはトランザクションを開きます。 リクエストが例外なしで処理された場合、データベースでコミットが行われ、例外がポップアップした場合はロールバックされます。
ATOMIC_REQUESTSとミドルウェアの違いは、ミドルウェアがプロジェクト全体で有効になっていることと、ATOMIC_REQUESTSを1つ以上のデータベースで使用できることです。
このアプローチを使用する欠点は、データベースへのオーバーヘッドが作成されることです。
この場合、手動のトランザクション管理が役立ちます。
手動トランザクション管理
Djangoはdjango.db.transactionモジュールを使用して多くの作業オプションを提供します
手動制御の可能な方法の1つを検討してください-これはtransaction.atomicです
transaction.atomicはメソッドとデコレータの両方であり、ビューメソッドにのみ使用されます。
デコレータでビューをラップすることにより、商品の購入を確保できます。 例えば
... from django.db import transaction ... @transaction.atomic def buy_something(request): .... request.user.update_balance(money) return render(request, template, data)
この場合、商品購入のトランザクションの原子性を含めました。 データの整合性に関するすべての責任がデータベースに移され、原子性が問題を解決します。
アトミックトランザクションと組み合わせても、select_for_updateメソッドを使用できます。
この場合、変更される行は、更新が呼び出されるまで変更のためにロックされます。
残高更新方法は次のように記述できます。
class Profile(models.Model): …. def update_balance(self, balance): Profile.objects.select_for_update().\ filter(pk=self.pk)\ .update(balance=F('balance') + balance)
結論:
- 原子性が助けになります
- アトミックのみの重要なコードを作成する
- select for updateを使用して、変更中にデータをロックします
- 可能であれば、データベース内のデータの処理を妨げないように、トランザクションをできるだけ短くするようにしてください。
また、MySQLのトランザクションレベルについては、 「MySQL:トランザクション分離レベル」と述べています 。