Python 如何在MySQL中造成死锁以进行测试
我想让我的Python库能够检测到死锁并重试。我相信我已经编写了一个很好的解决方案,现在我想测试它 对于使用MySQLdb创建死锁条件可以运行的最简单查询,有什么想法吗 系统信息:Python 如何在MySQL中造成死锁以进行测试,python,mysql,database,deadlock,Python,Mysql,Database,Deadlock,我想让我的Python库能够检测到死锁并重试。我相信我已经编写了一个很好的解决方案,现在我想测试它 对于使用MySQLdb创建死锁条件可以运行的最简单查询,有什么想法吗 系统信息: MySQL 5.0.19 客户5.1.11 视窗XP Python 2.4/MySQLdb 1.2.1 p2 您始终可以从另一个会话(例如mysql CLI)运行锁表tablename。这可能会奏效 在您释放它或断开会话连接之前,它将保持锁定状态。我不熟悉Python,如果我说错了,请原谅我的错误语言。。。但是打开
- MySQL 5.0.19
- 客户5.1.11
- 视窗XP
- Python 2.4/MySQLdb 1.2.1 p2
在您释放它或断开会话连接之前,它将保持锁定状态。我不熟悉Python,如果我说错了,请原谅我的错误语言。。。但是打开两个会话(在单独的窗口中,或者从单独的Python进程-从单独的框中…)然后 。在A部分:
Begin Transaction
Insert TableA() Values()...
。然后在会话B中:
Begin Transaction
Insert TableB() Values()...
Insert TableA() Values() ...
。然后返回会话A
Insert TableB() Values () ...
你会陷入僵局 你想要的东西大致如下 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()
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)
child.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()
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)以不一致的顺序获取锁的事务。我发现,通过保持事务的简短和一致性,最容易防止死锁
还要注意非决定论。你无法预测哪个孩子会死在死锁中,哪个孩子会在另一个孩子死后继续。只有其中一个需要死亡才能为另一个释放所需的资源。一些关系数据库管理系统声称有一个基于所持有资源数量的规则等等等等,但一般来说,你永远不会知道受害者是如何被选中的
由于这两次写入是按特定顺序进行的,所以您可能希望child1先死。然而,你不能保证这一点。直到child 2尝试获取child 1的资源,它才是死锁——谁先获得的顺序可能无法决定谁死了
还要注意,这些是进程,而不是线程。由于Python GIL,线程可能会在无意中被同步,并且需要大量调用
time.sleep(0.001)
,以便让另一个线程有机会赶上。对于这一点,流程稍微简单一些,因为它们是完全独立的。不确定上述任何一项是否正确。
看看这个:
以下是一些关于如何在PHP中执行此操作的伪代码: 脚本1:
START TRANSACTION;
INSERT INTO table <anything you want>;
SLEEP(5);
UPDATE table SET field = 'foo';
COMMIT;
启动事务;
插入表格;
睡眠(5);
更新表集字段='foo';
犯罪
脚本2:
START TRANSACTION;
UPDATE table SET field = 'foo';
SLEEP(5);
INSERT INTO table <anything you want>;
COMMIT;
启动事务;
更新表集字段='foo';
睡眠(5);
插入表格;
犯罪
执行脚本1,然后立即在另一个终端中执行脚本2。如果数据库表中已经有一些数据,就会出现死锁(换句话说,在您第二次尝试此操作后,它就会开始死锁)
请注意,如果mysql不支持SLEEP()命令,请在应用程序本身中使用Python的等效命令。这只会导致操作错误:(1205,“超过锁等待超时;尝试重新启动事务”),否?@Greg:我想他们说的是一个会话执行锁表a,另一个会话执行锁表B。此时,它们必须以某种方式同步。然后会话一尝试锁定表B。当会话二尝试锁定表a时,它将死锁。我如何确保它们在完全相同的时间运行?对不起,我的意思是“手动”控制上述三个步骤的执行时间。。。首先,在会话a中开始事务并插入表a,然后移动到另一个进程并执行第二个块,然后移动到第一个进程并(可能有一个用户按钮来执行此操作)执行最后一个块,而不使用Python或任何其他客户机代码,我只需在两个SQL窗口中编写insert语句,从一个来回移动到另一个。。。但是您希望您的代码“陷阱”并检测死锁……您能在第一次插入和第二次插入之间的第一个会话中设置一个计时器,等待秒吗?然后在启动会话A后,启动会话B。。。会话A的后半部分(在10秒延迟之后,将发生在会话B之后,这将导致死锁…或者使用线程同步原语来协调线程。