11

これは説明が難しい (そして非常に奇妙である) ので、我慢してください。問題とその修正について説明しますが、なぜそれが機能するのかを誰かが説明できるかどうかを確認したいと思います:)

mod_perl を使用する Web アプリケーションがあります。MySQL データベースを使用しており、定期的にデータベースにデータを書き込んでいます。これはモジュール式であるため、独自の「データベース」タイプのモジュールもあり、接続や更新などを処理します。database::db_connect() サブルーチンはデータベースへの接続に使用されAutoCommit、0 に設定されます。

データベースから定期的にデータを取得し、返されるデータに応じてさまざまなタスクを実行する、別の Perl アプリケーション (スタンドアロン デーモン) を作成しました。そこに database.pm モジュールを含めているので、すべてを書き直したり複製したりする必要はありません。

私が経験している問題は次のとおりです。

アプリケーションは起動時にデータベースに接続し、無限にループして、X 秒ごとにデータベースからデータを取得します。ただし、データベース内のデータが更新された場合でも、データベースへの最初の接続/クエリで取得した「古い」データがアプリケーションに返されます。

たとえば、3 つの行があり、列「名前」には各レコードの値「a」、「b」、および「c」があります。行の 1 つを更新し (たとえば、コマンド ラインから mysql クライアントを使用)、名前を「c」から「x」に変更すると、スタンドアロン デーモンはそのデータを取得しません。 MySQL。tcpdump で db トラフィックをキャプチャしたところ、MySQL が実際にそのデータを返していることがはっきりとわかりました。SELECT で SQL_NO_CACHE を使用しようとしましたが (何が起こっているのかわからなかったので)、どちらも役に立ちませんでした。

次に、スタンドアロン デーモンで DB 接続文字列を変更し、AutoCommit1 に設定しました。突然、アプリケーションが適切なデータを取得し始めました。

AutoCommit は INSERT/UPDATE タイプのステートメントにのみ影響し、SELECT ステートメントには影響しないと思っていたので、私は困惑しています。しかし、それは一見そうです、そして私はその理由を理解していません。

AutoCommitが0 に設定されている場合に SELECT ステートメントがデータベースから「更新された」行を返さない理由と、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;

1 に変更AutoCommitすると、これが修正されます。なんで?

ありがとう :)

PS: 誰が気にするかわかりませんが、DBI バージョンは 1.613、DBD::mysql は 4.017、perl は 5.10.1 (Ubuntu 10.04) です。

4

2 に答える 2

15

MyISAM テーブルではなく InnoDB テーブルを使用していると思います。InnoDBトランザクション モデルで説明されているように、すべてのクエリ (SELECT を含む) はトランザクション内で実行されます。

がオンの場合AutoCommit、クエリごとにトランザクションが開始され、成功した場合は暗黙的にコミットされます (失敗した場合、動作は異なる場合がありますが、トランザクションは終了することが保証されます)。MySQL の binlog で暗黙的なコミットを確認できます。false に設定AutoCommitすると、自分でトランザクションを管理する必要があります。

デフォルトのトランザクション分離レベルはREPEATABLE READです。これは、すべてのSELECTクエリが同じスナップショット (トランザクションの開始時に確立されたスナップショット) を読み取ることを意味します。

他の回答(ROLLBACK読み始める前に)で与えられた解決策に加えて、いくつかの解決策があります:

READ COMMITTEDなどの別のトランザクション分離レベルを選択すると、SELECTクエリで毎回新しいスナップショットを読み取ることができます。

AutoCommittrue (デフォルト設定) のままにして、 を発行して独自のトランザクションを開始することもできますBEGIN WORK。これにより、各クエリが独自のトランザクションを再度取得するorステートメントAutoCommitを発行するまで (または で別のトランザクションを開始するまで) 、動作が一時的に無効になります。COMMITROLLBACKBEGIN WORK

個人的には、よりエレガントに見える後者の方法を選択します。

于 2010-10-17T08:20:20.707 に答える
4

自動コミットをオフにすると、トランザクションも開始されると思います。また、トランザクションを開始すると、コミットするかロールバックするまで、他の人の変更から保護される場合があります。したがって、私の半情報に基づく推測が正しく、データをクエリしているだけなので、スリープ操作の前にロールバックを追加します(使用していないロックを保持する意味はありません)。

$dbh->rollback;
于 2010-10-17T07:05:12.980 に答える