我遇到一个问题,我正在运行一个执行涉及两个表Users
和Transactions
的事务的Web服务。问题是,当我从Transactions
中选择时,它有时无法找到我能够看到的最新行。
我使用Perl / Dancer作为Web框架,虽然我认为我遇到的问题是在数据库级别(我正在使用MySQL / InnoDB)。伪代码如下所示:
my $dbh = DBI->connect_cached(("DBI:mysql:Database;host=localhost;mysql_ssl=1",
"user", "password",
{RaiseError => 0, AutoCommit => 0});
my $sth_transact = $dbh->prepare("select balance from Users ".
"where id=? for update");
... store result in local variable ...
my $sth_inner = $dbh->prepare("select deposit from Transactions ".
"where id=?");
$sth_inner->execute();
if (my $deposit = $sth_inner->fetch_row()) {
$deposit *= $bonus;
$sth_inner = $dbh->prepare("update Transactions set deposit=?".
"where id=?");
$sth_inner->bind_param(1, $deposit);
$sth_inner->bind_param(2, $transaction_id);
$sth_inner->execute();
... update balance in Users table ...
}
在前几个请求中,Transactions表上的select返回最近一行。但是,在第三个请求中,它无法再找到最近的行。当我这样做时:
my $sth_inner = $dbh->prepare("select id from Transactions where id=(SELECT max(id) from Transactions)");
它返回比最近一行更早的3行的id。例如,如果Transactions
表中有87行,则返回84。
我不确定我是否正确处理锁定。我正在用Users
锁保护select ... for update
表,但我没有为Transactions
表执行此操作。我不确定这是否重要。由于我将更新嵌套到Transactions
表的select ... for update
中的Users
表,我认为它会在事务中受到保护。
有关Transactions
表上的选择未返回最新行的原因的任何帮助表示赞赏。
答案 0 :(得分:3)
此问题通常由InnoDB的默认隔离级别引起,即“可重复读取”。这意味着在当前事务启动后,连接无法看到添加到数据库的任何内容。
您可以通过更改隔离级别(可能是“read committed”(Oracle的默认值))或在select开始新事务之前发出提交来避免此问题。