Dav*_*sky 2 mysql perl transactions dbix-class
请考虑以下perl代码:
$schema->txn_begin();
my $r = $schema->resultset('test1')->find({id=>20});
my $n = $r->num;
$r->num($n+1);
print("updating for $$\n");
$r->update();
print("$$ val: ".$r->num."\n");
sleep(4);
$schema->txn_commit();
Run Code Online (Sandbox Code Playgroud)
我期望由于更新受到事务的保护,如果两个进程尝试更新"num"字段,则第二个应该失败并出现一些错误,因为它丢失了竞争.Interbase将此称为"死锁"错误.然而,MySQL会在update()调用上暂停,但在第一个调用commit之后会很乐意继续.然后第二个进程具有num的"old"值,导致增量不正确.注意:
$ perl trans.pl & sleep 1 ; perl trans.pl
[1] 5569
updating for 5569
5569 val: 1015
updating for 5571
5571 val: 1015
[1]+ Done perl trans.pl
Run Code Online (Sandbox Code Playgroud)
两种情况下的结果值都是"1015".这怎么可能是正确的?
假设您使用InnoDB作为存储引擎,这是我期望的行为.InnoDB的默认事务隔离级别是REPEATABLE READ.这意味着当您执行时SELECT,事务会在该特定时间获取数据库的快照.快照不会包含尚未提交的其他事务的更新数据.由于SELECT每个进程都在提交之前发生,因此它们每个都会看到数据库处于相同的状态(num = 1014).
为了获得您似乎期待的行为,您应该遵循Lluis的建议并执行一个SELECT ... FOR UPDATE锁定您感兴趣的行.为此,请更改此行
my $r = $schema->resultset('test1')->find({id=>20});
Run Code Online (Sandbox Code Playgroud)
对此
my $r = $schema->resultset('test1')->find({id=>20}, {for=>'update'});
Run Code Online (Sandbox Code Playgroud)
并重新运行您的测试.
如果您不熟悉MySQL中事务的复杂性,我强烈建议您阅读有关InnoDB事务模型和锁定的文档部分.此外,如果您还没有,请阅读有关交易的DBIC使用说明,并且也要AutoCommit非常仔细.在方式txn_方法的行为的时候AutoCommit开启或关闭,是有点棘手.如果你愿意的话,我也建议阅读消息来源.就个人而言,我必须阅读源代码才能完全理解DBIC正在做什么,以便我能够获得我想要的确切行为.