Django transaction誤用後に遭遇した問題と解決方法
3889 ワード
今日、プロジェクトが開発したモジュールをデバッグしたとき、奇妙な現象が発見され、最後に追跡したのは、プロジェクトでトランザクションが間違っているためです.この問題は私にしばらく穴をあけたので,これ以上穴を踏まないように記録した.詳しく説明します.
Djangoフレームワークは、後述するトランザクションを開く方法を多く提供していることを知っています.筆者が好きなのは
はい.それなら、使いましょう.ひとしきりパチパチした後、開発が終わり、デバッグのロゴも打ったので、測定しましょう.奇妙なことがここで起こった.logにはデータベースで生成されたプライマリ・キーidが印刷されているのに、データベースでは死活して検出されない.WTF?!
そこで、Djangoのトランザクションを開く方法を見てみると、やはり
Djangoの公式ドキュメントを見たところ、お勧めしていないことがわかりました.なぜなら、トランザクションがHTTPリクエストにバインドされると、viewはアプリケーションのデータベースに対するクエリー文の効率性とデータベースの現在のロック競合に依存するためです.流量が上がると、性能に影響があります.では、トランザクションの両方が使用できることをどのように保証すればいいのでしょうか.
1つ目は、上述したように、databaseで
もう一つの方法は、筆者が好んで使っているように、最外層のatomicコードブロックに入るとトランザクションが開きます. 内層atomicコードブロックに入ると、保存ポイントが作成されます. 内部ブロックを終了すると、保存ポイントが解放またはロールバックされる. 外部ブロックを終了すると、物事がコミットまたはロールバックされる.savepointパラメータをFalseに設定することで、内層の保存ポイントを無効にすることができます.異常が発生した場合、savepointが設定されている場合、Djangoは最初のレイヤのコードブロックを終了したときにロールバックを実行します.そうしないと、最外層のコードブロック上でロールバックを実行します.原子性は常に外層の事物に保証される.このオプションは、セーブポイントのオーバーヘッドが明らかな場合にのみ使用します.その欠点は上述の誤り処理の原則を破ったことである.
Djangoはautocommitをサポートし、各SQL文の実行時にトランザクションを開始します.閉じるには、プロファイルに
参照先:データベース・トランザクション 転載先:https://www.cnblogs.com/scharfsinnig/p/7709859.html
Djangoフレームワークは、後述するトランザクションを開く方法を多く提供していることを知っています.筆者が好きなのは
@transaction.atomic
の装飾を使って事務を起動することです.この形式により、db原子の操作を保証しながら、トランザクションに関連するモジュール範囲をカスタマイズすることができます.atomicは、コンテキストの形式で使用することもできます.たとえば、次のようにします.with transaction.atomic():
transaction_plalala()
.
.
.
はい.それなら、使いましょう.ひとしきりパチパチした後、開発が終わり、デバッグのロゴも打ったので、測定しましょう.奇妙なことがここで起こった.logにはデータベースで生成されたプライマリ・キーidが印刷されているのに、データベースでは死活して検出されない.WTF?!
SHOW CREATE TABLE xxx
によってxxxテーブルの自己付加価値も変化していることが分かった.でもどうしてなくなったの?誰が私のデータを動かしたの?このとき、ビューから見なければならず、開発した新しいモジュールを追跡し続けた.viewから呼び出しモジュールに問題はありません.これはどういうことですか.幽霊がいるの?違うに違いない.どこかで、新しいトランザクションが構成されていますが、このトランザクションにはview全体が含まれていますか?筆者はviewにraise exceptionの操作があることしか発見しなかったからだ.推測はこうなるしかない.私が新しく開発したモジュールには問題がないので、他のviewで検証しました.そこで、Djangoのトランザクションを開く方法を見てみると、やはり
HTTP
を開く方法があることに気づきました.そのオープン方式はdbでATOMIC_REQUESTS=True
オープンを指定することである.settingsファイルを開いて、対応する構成を見つけましたが、やはり、問題はここにあります!どうする?こちらがすでに構成されている以上、他のview(このトランザクション操作に依存しているかもしれない)に影響を与えない場合、どのようにしてこの構成を閉じますか?答えはtransaction.non_atomic_requests
でviewを飾ります.はい.テストして、確かにいいです.問題が解決した.次は公式Demoです.from django.db import transaction
@transaction.non_atomic_requests
def my_view(request):
do_stuff()
@transaction.non_atomic_requests(using='other')
def my_other_view(request):
do_stuff_on_the_other_database()
Djangoの公式ドキュメントを見たところ、お勧めしていないことがわかりました.なぜなら、トランザクションがHTTPリクエストにバインドされると、viewはアプリケーションのデータベースに対するクエリー文の効率性とデータベースの現在のロック競合に依存するためです.流量が上がると、性能に影響があります.では、トランザクションの両方が使用できることをどのように保証すればいいのでしょうか.
1つ目は、上述したように、databaseで
ATOMIC_REQUESTS
の形式を指定することによって、HTTPリクエストにトランザクションをバインドすることである.トランザクションにおけるviewの操作に触れる方法はviewにtransaction.non_atomic_requests
を飾ることであり、前述したように、具体的にはDjango公式ドキュメントも読むことができる.default_db = {
"ENGINE": "",
"NAME": "",
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
"OPTIONS": "",
"ATOMIC_REQUESTS": True,
}
もう一つの方法は、筆者が好んで使っているように、
transaction.atomic
を通じて事務をより明確にコントロールすることです.atomicでは、コードブロックを実行するときに、データベースレベルで原子的保証を提供できます.コードブロックが正常に完了すると、対応する変化はデータベースにコミットされてcommitされます.実行中に例外が発生した場合、セグメントコードに関連するすべての変更がロールバックされます.ここで、atomicで異常をキャプチャしないように注意する必要があります.1つの原子ブロックが終了すると、Djangoは正常にコミットされたかロールバックされたかを確認します.原子ブロックに異常なハンドルをキャプチャすると、Djangoに問題の発生を隠す可能性があります.これは予想外の結果をもたらす可能性があります.データベース異常を正しくキャプチャするには、前述したようにatomicコードブロックに基づいて行う必要があります.必要に応じて、atomicコードを追加してこの目的に使用することができます.このモードにはもう一つの利点があります.例外が発生した場合、どの操作がロールバックされるかを明確にします.最下位のDjangoのトランザクション管理コード:from django.db import transaction
def viewfunc(request):
# This code executes in autocommit mode (Django's default).
do_stuff()
with transaction.atomic():
# This code executes inside a transaction.
do_more_stuff()
Djangoはautocommitをサポートし、各SQL文の実行時にトランザクションを開始します.閉じるには、プロファイルに
AUTOCOMMIT=False
パラメータを設定して閉じることができます.これにより、Djangoはautocommitを有効にすることも、commitsを実行することもできません.これは、すべてのものに対して明確なcommit操作を実行する必要があります.したがって、これは、カスタマイズされた物事制御ミドルウェアや奇抜なシーンにのみ使用することが望ましい.参照先: