我有一个在托管共享服务器上运行的单线程Perl脚本,主要执行以下代码:
my $O_dbh = DBI->connect("dbi:mysql:dbname=dbname", "abc", "xxx", {RaiseError => 1});
$O_dbh->begin_work();
my $O_sth1 = $O_dbh->prepare('SELECT COUNT(*) FROM mytable WHERE any = 5');
$O_sth1->execute();
my @result1 = $O_sth1->fetchrow_array();
my $oldValue = $result1[0];
$O_sth1->finish();
my $O_sth2 = $O_dbh->prepare('INSERT INTO mytable (any) VALUES (5)');
$O_sth2->execute();
$O_sth1->execute();
my @result2 = $O_sth1->fetchrow_array();
my $newValue = $result2[0];
if ($oldValue + 1 == $newValue) {
$O_dbh->commit();
}
else {
$O_dbh->rollback();
die "why?! new = $newValue, old = $oldValue";
}
有时(<1%)代码会遇到回滚情况并失败。在我的本地系统上,我无法重现此错误。数据库是MySQL 5.
CREATE TABLE `mytable` (
`id` int(11) NOT NULL auto_increment,
`any` int(11) NOT NULL default '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
如何追踪此错误?任何帮助将不胜感激。
答案 0 :(得分:3)
假设您的数据库使用默认设置运行,我会更惊讶您的SELECT
曾返回两个不同的值。
如果事务隔离级别为REPEATABLE READ(默认级别),则同一事务中的所有一致性读取将读取该事务中第一次此类读取所建立的快照。您可以通过提交当前事务并在发出新查询之后为查询获取更新的快照。
因此,如果默认REPEATABLE READ
隔离级别生效,我希望所有查询都会返回与第一次查询时数据库状态一致的数据。
但是,这听起来似乎有帮助
使用READ COMMITTED隔离级别,事务中的每个一致读取都会设置并读取自己的新快照。
我想你应该试试
$O_dbh->do('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');
连接后立即,看看是否能解决问题。
但是,您应确保在此事务之后disconnect
数据库句柄或将其返回到先前的隔离级别。否则你将开始得到不一致的结果。