Mysql 自动提交设置为0的DBI数据库句柄未使用SELECT返回正确的数据?

Mysql 自动提交设置为0的DBI数据库句柄未使用SELECT返回正确的数据?,mysql,perl,select,dbi,autocommit,Mysql,Perl,Select,Dbi,Autocommit,这是一个很难解释的问题(而且很奇怪),所以请容忍我。我将解释这个问题,以及它的修复方法,但我想看看是否有人能解释为什么它会以这种方式工作:) 我有一个使用mod_perl的web应用程序。它使用MySQL数据库,我定期将数据写入数据库。它是模块化的,因此它也有自己的“数据库”类型的模块,我在其中处理连接、更新等。数据库::db_connect()子例程用于连接到数据库,自动提交设置为0 我制作了另一个Perl应用程序(独立守护进程),它定期从数据库中获取数据,并根据返回的数据执行各种任务。我在其

这是一个很难解释的问题(而且很奇怪),所以请容忍我。我将解释这个问题,以及它的修复方法,但我想看看是否有人能解释为什么它会以这种方式工作:)

我有一个使用mod_perl的web应用程序。它使用MySQL数据库,我定期将数据写入数据库。它是模块化的,因此它也有自己的“数据库”类型的模块,我在其中处理连接、更新等。数据库::db_connect()子例程用于连接到数据库,
自动提交
设置为0

我制作了另一个Perl应用程序(独立守护进程),它定期从数据库中获取数据,并根据返回的数据执行各种任务。我在其中包括database.pm模块,所以我不必重写/复制所有内容

我遇到的问题是:

应用程序在启动时连接到数据库,然后永远循环,每X秒从数据库获取一次数据。但是,如果数据库中的数据被更新,我的应用程序仍然会返回我在与数据库的初始连接/查询中获得的“旧”数据

例如,我有3行,列“Name”的每个记录都有值“a”、“b”和“c”。如果我更新其中一行(例如,使用命令行中的mysql客户端)并将名称从“c”更改为“x”,我的独立守护进程将不会获得该数据——它仍然会从mysql返回a/b/c。我用tcpdump捕获了数据库流量,我可以肯定地看到MySQL确实在返回这些数据。我也尝试过将SQL\u NO\u缓存与SELECT一起使用(因为我不确定发生了什么),但这也没有帮助

然后,我修改了独立守护进程中的DB连接字符串,并将
AutoCommit
设置为1。突然,应用程序开始获得正确的数据

我感到困惑,因为我认为自动提交只会影响语句的插入/更新类型,而不会影响SELECT语句。但这似乎是真的,我不明白为什么

有人知道为什么SELECT语句在
AutoCommit
设置为0时不返回数据库中的“更新”行,以及为什么在
AutoCommit
设置为1时返回更新的行吗

下面是我在独立守护进程中使用的一个简化(去掉错误检查等)代码,它不会返回更新的行

#!/usr/bin/perl

use strict;
use warnings;
use DBI;
use Data::Dumper;
$|=1;

my $dsn = "dbi:mysql:database=mp;mysql_read_default_file=/etc/mysql/database.cnf";
my $dbh = DBI->connect($dsn, undef, undef, {RaiseError => 0, AutoCommit => 0});
$dbh->{mysql_enable_utf8} = 1;

while(1)
{
    my $sql = "SELECT * FROM queue";
    my $stb = $dbh->prepare($sql);
    my $ret_hashref = $dbh->selectall_hashref($sql, "ID");
    print Dumper($ret_hashref);
    sleep(30);
}

exit;
AutoCommit
更改为1可修复此问题。为什么?

谢谢:)


附言:不确定是否有人在乎,但DBI版本是1.613,DBD::mysql是4.017,perl是5.10.1(在Ubuntu 10.04上)。

我认为当你关闭自动提交时,你也会启动一个事务。而且,当您启动事务时,您可能会受到保护,不受其他人的更改的影响,直到您提交或回滚它为止。因此,如果我的半知情猜测是正确的,并且由于您只是查询数据,请在睡眠操作之前添加回滚(没有必要持有您不使用的锁,等等):


我想您使用的是InnoDB表,而不是MyISAM表。如InnoDB中所述,所有查询(包括SELECT)都发生在事务中

启用“自动提交”时,将为每个查询启动事务,如果成功,则隐式提交(如果失败,行为可能会有所不同,但事务将保证结束)。您可以在MySQL的binlog中看到隐式提交。通过将
AutoCommit
设置为false,您需要自己管理事务

默认事务隔离级别为,这意味着所有
SELECT
查询将读取相同的快照(事务启动时建立的快照)

除了在另一个答案中给出的解决方案(
开始阅读前回滚
)之外,这里还有几个解决方案:

您可以选择另一个事务隔离级别,例如,它使您的
SELECT
查询每次都读取一个新的快照

您还可以将
自动提交
设置为true(默认设置),并通过发出
开始工作
启动自己的事务。这将暂时禁用
自动提交
行为,直到发出
提交
回滚
语句,之后每个查询将再次获得自己的事务(或使用
开始工作
启动另一个查询)


一、 就个人而言,我会选择后一种方法,因为它看起来更优雅。

是命令行mysql客户端中的
auto_commit
设置是开还是关(您在那里执行了
UPDATE
操作)?它是开的(默认情况下是开的,我没有更改它)。我可以从mysql客户端或任何其他“会话”(连接到DB的新DBI会话或任何其他客户端)看到“新”更新的数据-只有自动提交0的会话无法访问更新的数据。这真是一个令人惊讶的答案,非常感谢您花时间解释这一点。我确实阅读了文档并试图找出答案,但实际上并没有遇到您在这里提到的内容(可能是阅读了错误的文档;)。再次感谢,这真的解释了很多。另一个问题(假设你再看一遍:)。我们在MySQL端使用存储过程,所以我实际上不在Perl代码中执行任何事务。我可以使用AutoCommit=1,并在从Perl代码调用存储过程之前发出“开始工作”命令吗?是的,您可以这样做。您还可以将事务放在存储过程中(但不能放在存储函数中),只要它更适合您的工作流。
$dbh->rollback;