4

我有一个在托管共享服务器上运行的单线程 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;

如何追踪此错误?任何帮助将不胜感激。

4

1 回答 1

3

假设您的数据库使用默认设置运行,我更惊讶您SELECT 曾经返回两个不同的值。

文档说这个

如果事务隔离级别为 REPEATABLE READ(默认级别),则同一事务中的所有一致性读取都会读取该事务中第一次此类读取所建立的快照。您可以通过提交当前事务并在此之后发出新查询来获得查询的更新快照。

因此,如果默认REPEATABLE READ隔离级别生效,我希望所有查询都会返回与第一次查询时数据库状态一致的数据。

但是,听起来这确实可能会有所帮助

使用 READ COMMITTED 隔离级别,事务中的每个一致读取都会设置并读取自己的新快照。

我觉得你应该试试

$O_dbh->do('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED');

连接后立即,看看是否可以为您解决问题。

但是,您应该确保disconnect在此事务之后处理数据库或将其返回到先前的隔离级别。否则,您将开始得到不一致的结果。

于 2013-06-22T12:08:10.250 回答