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

Mysql Yii2数据库事务行为支持可重复读取,mysql,concurrency,yii2,transactions,transaction-isolation,Mysql,Concurrency,Yii2,Transactions,Transaction Isolation,我有以下问题。我有一个web应用程序(用phpyii2编写),其中多个post请求预计会在很短的时间内到达应用程序服务器。业务逻辑应该非常严格,这意味着只有第一个请求的数据应该插入MySQL表,其余的都应该忽略。 客户端在post请求中同时发送父记录和最新子记录的id。 我以这种方式使用Yii的db事务 $transaction = Yii::$app->db->beginTransaction(); $parent = ObjectParent::findOne(Yii::$app

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

$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不匹配。但我的问题是,数据库中插入了多行。数据库的隔离级别是可重复读取的,这应该保证(据我所知)在事务中读取的行在提交之前不会更改。如果这是真的,那么它就不会成为问题,因为它会使第二个事务“中断”。 问题可能是,Yii可能不使用或不知道这些DB锁,因此不知道记录已经是事务的一部分,并根据对象的当前状态进行验证。DB当然不知道任何关于验证规则的事情,所以从它的角度来看也是可以的

我的解决办法是:

  • 还将yii事务显式设置为可重复读取。这可能会改变它的行为。我对此表示怀疑,因为根据文档,在没有明确定义它的情况下,它使用了DB default(REPEATABLE READ)
  • 将验证逻辑放晚一点,靠近commitm,放在$parent->setLatestChildId($new_child->id)之后; 我不知道这是否是一个100%的解决方案,所以我不想开始重写测试代码。注意,上面的框架代码只是原始代码的简化版本

  • 用数据库触发器解决整个问题,这样就可以绕过应用程序上下文

  • 请让我知道在这些场景中什么是最佳实践。不幸的是,我在这些concurreny问题上没有那么丰富的经验,并且很难对其进行测试和模拟并发请求。
    感谢

    Repeatable read only可确保在事务中读取行时,重新读取这些行会得到相同的结果。另一个事务可能会改变结果

    要对其进行一些锁定,可以执行以下操作:

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

    但是,对于确保父/子项插入是唯一的情况,我建议将
    (父项id,子项id)
    作为表中的唯一键或主键,这样重复插入将生成重复键异常。

    可重复只读确保,如果在事务中读取行,重新读取这些行会得到相同的结果。另一个事务可能会改变结果

    要对其进行一些锁定,可以执行以下操作:

    SELECT ... [LOCK IN SHARE MODE|FOR UPDATE]
    
    但是,为了确保父/子插入是唯一的,我建议将
    (父/子id)
    作为表中的唯一键或主键,这样重复插入将生成重复键异常