10

我想让我的 Python 库与 MySQLdb 一起使用能够检测死锁并重试。我相信我已经编写了一个很好的解决方案,现在我想对其进行测试。

对于我可以使用 MySQLdb 运行以创建死锁条件的最简单查询的任何想法是什么?

系统信息:

  • MySQL 5.0.19
  • 客户端 5.1.11
  • 视窗
  • Python 2.4 / MySQLdb 1.2.1 p2
4

5 回答 5

4

这是我如何在 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 的等效命令。

于 2011-11-11T22:09:50.163 回答
1

我不熟悉 Python,所以请原谅我的语言不正确如果我说错了......但是打开两个会话(在单独的窗口中,或从单独的 Python 进程中 - 从单独的框可以工作......)然后.. .

. 在会话 A 中:

   Begin Transaction 
      Insert TableA()  Values()... 

. 然后在会话 B 中:

Begin Transaction
  Insert TableB() Values()... 
  Insert TableA() Values() ...

. 然后返回会话 A

  Insert TableB() Values () ...

你会陷入僵局...

于 2008-11-06T22:01:53.533 回答
1

您始终可以从另一个会话(例如 mysql CLI)运行 LOCK TABLE 表名。这可能会奏效。

在您释放它或断开会话之前,它将保持锁定状态。

于 2008-11-06T21:51:06.720 回答
1

您想要以下内容。

父.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()

孩子.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 )让其他线程有机会赶上。流程——为此——稍微简单一些,因为它们是完全独立的。

于 2008-11-07T11:12:14.593 回答
1

不确定以上是否正确。看看这个:

http://www.xaprb.com/blog/2006/08/08/how-to-deliberately-cause-a-deadlock-in-mysql/

于 2009-09-23T21:42:49.193 回答