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,则修改将发生两次,这会导致问题。

4

2 回答 2

1

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

于 2009-06-01T18:31:44.753 回答
0

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

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

这些方法有一些缺点。我自己并没有经常使用它们,它们是特定于 MySQL 的,但它们可以为你工作。

于 2009-06-01T18:39:34.473 回答