我想让我的Python库使用MySQLdb能够检测死锁并再试一次。我相信我编写了一个很好的解决方案,现在我想测试它。
我可以使用MySQLdb运行以创建死锁条件的最简单查询的任何想法是?
系统信息:
答案 0 :(得分:2)
这里有一些关于我在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。如果数据库表中已经包含一些数据,那么您将遇到死锁(换句话说,它会在您第二次尝试此操作后开始死锁)。
请注意,如果mysql不遵守SLEEP()命令,请在应用程序本身中使用Python等效项。
答案 1 :(得分:1)
您始终可以从另一个会话(例如mysql CLI)运行LOCK TABLE tablename。这可能会成功。
在您释放或断开会话之前,它将保持锁定状态。
答案 2 :(得分:1)
我不熟悉Python,所以请原谅我错误的语言如果我说错了......但打开两个会话(在单独的窗口中,或者从单独的Python进程 - 从单独的框中工作......)然后......
。在会议A中:
Begin Transaction
Insert TableA() Values()...
。然后在会议B中:
Begin Transaction
Insert TableB() Values()...
Insert TableA() Values() ...
。然后回到会话A
Insert TableB() Values () ...
你会陷入僵局......
答案 3 :(得分:1)
您需要以下几行内容。
<强> parent.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)
注意对称性。每个孩子开始持有一种资源。然后他们试图获得别人的资源。为了好玩,你可以有3个孩子和3个资源来制造真正的恶性循环。
请注意,难以设法发生死锁的情况。如果您的交易很短且一致,那么很难实现死锁。死锁需要(a)长时间持有锁的事务和(b)以不一致的顺序获取锁的事务。我发现通过保持我的交易简短和一致来防止死锁是最容易的。
还要注意非决定论。你无法预测哪个孩子会因死锁而死亡,哪个孩子会在另一个孩子死后继续死亡。只有两个中的一个需要死才能为另一个释放所需的资源。一些RDBMS声称有一个基于资源数量的规则等等等等,但总的来说,你永远不会知道受害者的选择方式。
由于两次写入按特定顺序排列,因此您希望子级1首先死亡。但是,你无法保证。在孩子2试图获得孩子1的资源之前,这不是僵局 - 先获得者的序列可能无法确定谁死亡。
另请注意,这些是进程,而不是线程。线程 - 由于Python GIL - 可能会无意中同步,并且需要大量调用time.sleep( 0.001 )
才能让其他线程有机会赶上。流程 - 为此 - 稍微简单一点,因为它们是完全独立的。
答案 4 :(得分:1)