Yii2数据库事务行为以支持可重复读取

问题描述 投票:0回答:1

我有以下问题。我有一个Web应用程序(用php Yii2编写),预计多个发布请求将在很短的时间内到达应用程序服务器。业务逻辑应该非常严格,这意味着只有第一个请求的数据应该插入MySQL表中,其余的都应该被忽略。客户在发布请求中同时发送了父母的ID和最新的子记录的ID。我以这种方式使用Yii的数据库事务。

$transaction = Yii::$app->db->beginTransaction();
$parent = ObjectParent::findOne(Yii::$app->request->post('parent_id')));
$latest_child = ObjectChild::findOne(Yii::$app->request->post('latest_child_id')));

if($parent->latest_child_id == $latest_child->id) {
    try{
        $new_child = $latest_child->createNewChild();
        $parent->setLatestChild($new_child->id);
        $transaction->commit();
    } catch{
        $transaction->rollback();
    }
}

如果请求按顺序获得收入,则第二个请求将被忽略,因为最新的子记录的ID与来自客户端的ID不匹配。但是我的问题是,数据库中插入了多行。数据库的隔离级别是REPEATABLE READ,它应确保(根据我的知识),保证在事务中读取的行在提交之前不会更改。如果这是真的,那么那将不是问题,因为它将使第二笔交易“中断”。问题可能是Yii可能不使用或不知道这些DB锁,因此不知道该记录已是事务的一部分,并根据对象的当前状态进行验证。数据库当然对验证规则一无所知,因此从它的角度来看也很好。

我解决这个问题的想法:

  1. 将yii交易明确地也设置为REPEATABLE READ。这可能会改变其行为。我怀疑,因为根据文档,没有显式定义它,而是使用数据库默认值(REPEATABLE READ)。
  2. 稍后再输入验证逻辑,靠近commitm并在$ parent-> setLatestChildId($ new_child-> id)之后;我不知道这是否是100%的解决方案,所以我不想开始重写经过测试的代码。请注意,上面的框架代码只是原始代码的简化版本。

  3. 用数据库触发器解决整个问题,因此它将绕过应用程序上下文。

请让我知道在这些情况下的最佳实践。不幸的是,我在这些并发问题上经验不足,并且很难测试并模拟并发请求。谢谢

mysql concurrency yii2 transactions transaction-isolation
1个回答
0
投票

可重复只读确保如果您读取事务中的行,则重新读取这些行将得到相同的结果。另一笔交易可能会改变结果。

对它们进行一些锁定是可能的:

SELECT ... [LOCK IN SHARE MODE|FOR UPDATE]

但是,对于确保父母/子女插入物唯一的情况,我建议(parent_id,child_id)是表中的唯一键或主键,这样重复插入将产生重复键异常。

© www.soinside.com 2019 - 2024. All rights reserved.