PythonライブラリをMySQLdbで動作させて、デッドロックを検出して再試行できるようにしたいと思います。良いソリューションをコーディングしたと思います。今度はそれをテストしたいと思います。
MySQLdbを使用してデッドロック状態を作成するために実行できる最も単純なクエリのアイデアはありますか?
システム情報:
- MySQL 5.0.19
- クライアント5.1.11
- Windows XP
- Python 2.4 / MySQLdb 1.2.1 p2
PHPでそれを行う方法の擬似コードを次に示します。
スクリプト 1:
START TRANSACTION;
INSERT INTO table <anything you want>;
SLEEP(5);
UPDATE table SET field = 'foo';
COMMIT;
スクリプト 2:
START TRANSACTION;
UPDATE table SET field = 'foo';
SLEEP(5);
INSERT INTO table <anything you want>;
COMMIT;
スクリプト 1 を実行し、すぐに別のターミナルでスクリプト 2 を実行します。データベース テーブルに既にデータが含まれている場合は、デッドロックが発生します (つまり、これを 2 回試行すると、デッドロックが発生します)。
mysql が SLEEP() コマンドを受け入れない場合は、アプリケーション自体で Python の同等のコマンドを使用してください。
別のセッション(たとえば、mysql CLI)からいつでもLOCKTABLEテーブル名を実行できます。それでうまくいくかもしれません。
リリースするか、セッションを切断するまで、ロックされたままになります。
上記のいずれかが正しいかどうかはわかりません。これをチェックしてください:
http://www.xaprb.com/blog/2006/08/08/how-to-deiberately-cause-a-deadlock-in-mysql/
私はPythonに慣れていないので、私の間違った言語を許してください私がこれを間違って言っているなら...しかし、2つのセッションを開いてください(別々のウィンドウで、または別々のPythonプロセスから - 別々のボックスから動作します...)それから.. .
. セッション A:
Begin Transaction
Insert TableA() Values()...
. 次に、セッション B で:
Begin Transaction
Insert TableB() Values()...
Insert TableA() Values() ...
. その後、セッション A に戻ります。
Insert TableB() Values () ...
デッドロックが発生します...
次の行に沿って何かが必要です。
親.py
import subprocess
c1= subprocess.Popen( ["python", "child.py", "1"], stdin=subprocess.PIPE, stdout=subprocess.PIPE )
c2= subprocess.Popen( ["python", "child.py", "2"], stdin=subprocess.PIPE, stdout=subprocess.PIPE )
out1, err1= c1.communicate( "to 1: hit it!" )
print " 1:", repr(out1)
print "*1:", repr(err1)
out2, err2= c2.communicate( "to 2: ready, set, go!" )
print " 2:", repr(out2)
print "*2:", repr(err2)
out1, err1= c1.communicate()
print " 1:", repr(out1)
print "*1:", repr(err1)
out2, err2= c2.communicate()
print " 2:", repr(out2)
print "*2:", repr(err2)
c1.wait()
c2.wait()
child.py
import yourDBconnection as dbapi2
def child1():
print "Child 1 start"
conn= dbapi2.connect( ... )
c1= conn.cursor()
conn.begin() # turn off autocommit, start a transaction
ra= c1.execute( "UPDATE A SET AC1='Achgd' WHERE AC1='AC1-1'" )
print ra
print "Child1", raw_input()
rb= c1.execute( "UPDATE B SET BC1='Bchgd' WHERE BC1='BC1-1'" )
print rb
c1.close()
print "Child 1 finished"
def child2():
print "Child 2 start"
conn= dbapi2.connect( ... )
c1= conn.cursor()
conn.begin() # turn off autocommit, start a transaction
rb= c1.execute( "UPDATE B SET BC1='Bchgd' WHERE BC1='BC1-1'" )
print rb
print "Child2", raw_input()
ra= c1.execute( "UPDATE A SET AC1='Achgd' WHERE AC1='AC1-1'" )
print ta
c1.close()
print "Child 2 finish"
try:
if sys.argv[1] == "1":
child1()
else:
child2()
except Exception, e:
print repr(e)
対称性に注意してください。各子は、最初は 1 つのリソースを保持します。次に、他の誰かが保持しているリソースを取得しようとします。楽しみのために、本当に悪循環のために3人の子供と3つのリソースを持つことができます.
デッドロックが発生する状況を想定することは困難であることに注意してください。トランザクションが短く、一貫している場合、デッドロックを発生させるのは非常に困難です。デッドロックには、(a) 長時間ロックを保持するトランザクションと、(b) 一貫性のない順序でロックを取得するトランザクションが必要です。トランザクションを短く一貫性のあるものにすることで、デッドロックを防ぐのが最も簡単であることがわかりました。
非決定性にも注意してください。どの子がデッドロックで死亡し、どの子が他の子が死亡した後も続くかを予測することはできません。もう一方に必要なリソースを解放するために死ぬ必要があるのは、2 つのうちの 1 つだけです。一部の RDBMS は、保持されているリソースの数に基づいたルールがあると主張していますが、一般的には、犠牲者がどのように選択されたかはわかりません。
2 つの書き込みが特定の順序で行われるため、子 1 が最初に終了することが予想されます。ただし、それを保証することはできません。子 2 が子 1 のリソースを取得しようとするまでは、デッドロックではありません。最初に取得した人の順序によって、誰が死ぬかが決まるわけではありません。
また、これらはスレッドではなくプロセスであることに注意してください。スレッドは -- Python GIL のために -- 誤って同期される可能性がありtime.sleep( 0.001 )
、他のスレッドに追いつく機会を与えるために を多数呼び出す必要があります。プロセスは完全に独立しているため、プロセスは少し単純です。