【发布时间】:2020-06-06 00:40:04
【问题描述】:
我有以下问题。我有一个 Web 应用程序(用 php Yii2 编写),其中多个 post 请求预计会在很短的时间内到达应用程序服务器。业务逻辑应该非常严格,这意味着只有第一个请求的数据应该插入到 MySQL 表中,其余的都应该忽略。 客户端在发布请求中同时发送父记录和最新子记录的 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 与来自客户端的不匹配。但我的问题是,数据库中插入了多行。数据库的隔离级别是 REPEATABLE READ,它应该确保(据我所知)在事务中读取的行保证在提交发生之前不会更改。如果这是真的,那就不是问题,因为它会使第二笔交易“中断”。 问题可能是,Yii 可能不使用或不知道这些数据库锁,所以不知道记录已经是事务的一部分,并根据对象的当前状态进行验证。数据库当然对验证规则一无所知,所以从它的角度来看也很好。
我解决这个问题的想法:
- 也将 yii 事务显式设置为 REPEATABLE READ。这可能会改变它的行为。我怀疑,因为根据文档,没有明确定义它,它使用数据库默认值(REPEATABLE READ)。
把验证逻辑放在后面一点,更接近提交,在 $parent->setLatestChildId($new_child->id); 之后 我不知道这是否是 100% 的解决方案,所以我不想开始重写测试过的代码。注意,上面的骨架代码只是原版的简化版。
使用数据库触发器解决整个问题,因此它会绕过应用程序上下文。
请让我知道在这些情况下的最佳做法是什么。不幸的是,我在这些并发问题上经验不足,而且很难测试和模拟并发请求。 谢谢
【问题讨论】:
标签: mysql concurrency yii2 transactions transaction-isolation