2009-06-01 87 views
3

我在使用MySQL和PHP + Propel 1.3时似乎遇到并发问题。下面是Propel对象的“保存”方法的一个小例子。并发问题

public function save(PropelPDO $con = null) { 
    $con = Propel::getConnection(); 
    try { 
     $con->beginTransaction(); 
     sleep(3); // ignore this, used for testing only 
     parent::save($con); 
     $foo = $this->getFoo(); // Propel object, triggers a SELECT 

     // stuff is happening here... 

     $foo->save($con); 
     $con->commit(); 
    } catch (Exception $e) { 
     $con->rollBack(); 
     throw $e; 
    } 
} 

问题是$ foo对象。假设我们在很短的时间内陆续接到两个示例方法的调用。在某些情况下,如果第二个事务读取$ foo的...

$foo = $this->getFoo(); 

...第一个事务不得不保存它的机会之前...

$foo->save($con); 

... $ FOO通过第二笔交易阅读将会过时,并且会发生不好的事情。

如何强制锁定表Foo对象存储在后,只有第一个完成其工作后,后续事务才能从它读取?

编辑:上下文是一个web应用程序。总之,在某些情况下,我想要第一个请求做一些数据修改(发生在$ foo的获取和保存之间)。所有后续请求都不应该能够进行修改。该修改是否会发生取决于获取的$ foo状态(表格行属性)。如果两个事务获取相同的$ foo,则修改将发生两次,这会导致问题。

+0

你是否在循环中调用这个保存方法?或者你是否在谈论这是跨连接的问题,因为两个用户几乎在同一时间点击此资源? – 2009-06-01 18:29:37

+0

后一种情况。 – Ree 2009-06-01 18:34:52

+0

如果这是一个基于浏览器的应用程序,它们不必在同一时间。以此示例为例:人A和B在某个时间点加载相同的数据行。人A进行更改并保存,屏幕重新加载,数据看起来不错。 C人员加载屏幕,确认人员A的变更。人B最终保存数据。人A加载数据,但他们的改变现在不见了。 – 2009-06-01 18:51:33

回答

1

当您将此现有行加载到屏幕/应用程序时,也加载LastChgDate。当您保存它时,请使用“AND LastChgDate = ”。检查更新的受影响行数,如果它为零,则返回“别人已经保存此记录”的错误,并回滚和其他更改。有了这个逻辑,你只能保存一行,如果它与加载时相同。对于新行INSERT,这不是必须的,因为它们是新的。

0

在MySQL中,我认为你可以使用SELECT FOR UPDATE来完成锁定。

另一种选择是使用GET_LOCK和RELEASE_LOCK MySQL函数调用来创建用来控制对资源的访问的命名锁。

这些方法有一些缺点。我自己没有用过它们,它们都是MySQL专用的,但它们可以为你工作。