AutoCommit设置为0的DBI数据库句柄没有用SELECT返回正确的数据?

sen*_*nel 11 mysql perl select autocommit dbi

这是一个难以解释的(并且非常奇怪),所以请耐心等待.我将解释这个问题,并解决它,但我想看看是否有人可以解释为什么它的工作方式:)

我有一个使用mod_perl的Web应用程序.它使用MySQL数据库,我定期将数据写入数据库.它是模块化的,因此它也有自己的"数据库"类型的模块,我处理连接,更新等.database :: db_connect()子例程用于连接数据库,并AutoCommit设置为0.

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

我遇到的问题是:

应用程序在启动时连接到数据库,然后永远循环,每隔X秒从数据库中获取数据.但是,如果数据库中的数据被更新,我的应用程序仍然会返回"旧"数据,这是我对数据库的初始连接/查询.

例如 - 我有3行,"Name"列的值为'a','b'和'c' - 用于每条记录.如果我更新其中一行(例如,从命令行使用mysql客户端)并将名称从"c"更改为"x",我的独立守护程序将无法获取该数据 - 它仍然会返回/ b/c MySQL的.我用tcpdump捕获了数据库流量,我肯定可以看到MySQL确实正在返回那些数据.我也尝试过将SQL_NO_CACHE与SELECT一起使用(因为我不知道发生了什么),但这也无济于事.

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

我很困惑,因为我认为AutoCommit只影响INSERT/UPDATE类型的语句,并且对SELECT语句没有影响.但它看起来确实如此,我不明白为什么.

有没有人知道为什么SELECT语句在AutoCommit设置为0 时不会从数据库返回'updated'行,为什么在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;
Run Code Online (Sandbox Code Playgroud)

更改AutoCommit为1可修复此问题.为什么?

谢谢 :)

PS:不确定是否有人关心,但DBI版本是1.613,DBD :: mysql是4.017,perl是5.10.1(在Ubuntu 10.04上).

kix*_*ixx 15

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

AutoCommit打开时,事务开始对每个查询,如果它是成功的,它是隐含提交(如果失败,该行为可能会有所不同,但交易是保证结束).您可以在MySQL的binlog中看到隐式提交.通过设置AutoCommit为false,您需要自己管理事务.

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

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

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

您也可以保留AutoCommit为true(默认设置)并通过发出开始自己的交易BEGIN WORK.这将暂时禁用该AutoCommit行为,直到您发出一个COMMITROLLBACK语句,之后每个查询再次获得自己的事务(或者您启动另一个事务BEGIN WORK).

我个人会选择后一种方法,因为它看起来更优雅.