5

TL;DR - MySQL では、テーブルのロックとトランザクションの使用を同時に行うことはできません。これを回避する方法はありますか?

(遅い) 外部システムからのデータをキャッシュするために使用している MySQL テーブルがあります。データは Web ページ (PHP で記述) を表示するために使用されます。キャッシュされたデータが古すぎると判断された場合、Web 接続の 1 つがキャッシュされたデータの更新をトリガーする必要があります。

私が対処しなければならない3つの問題があります:

  • 他のクライアントは、更新中にキャッシュ データを読み取ろうとします。
  • 複数のクライアントがキャッシュ データが古すぎると判断し、同時に更新を試みる場合があります。
  • 作業を行っている PHP インスタンスはいつでも予期せず終了する可能性があり、データが破損することはありません。

トランザクションを使用して最初と最後の問題を解決できるので、クライアントはトランザクションがコミットされるまで古いデータを読み取ることができ、すぐに新しいデータが表示されます。問題が発生すると、トランザクションがロールバックされるだけです。

2 番目の問題は、テーブルをロックすることで解決できます。これにより、1 つのプロセスだけが更新を実行できるようになります。他のプロセスがロックを取得するまでに、彼らは打ち負かされ、何も更新する必要がないことに気付くでしょう。

これは、テーブルをロックしてトランザクションを開始する必要があることを意味します。MySQL のマニュアルによると、これは不可能です。トランザクションを開始するとロックが解放され、テーブルをロックするとアクティブなトランザクションがコミットされます。

これを回避する方法はありますか、それとも私の目標を完全に達成する別の方法はありますか?

4

4 に答える 4

5

これは、テーブルをロックしてトランザクションを開始する必要があることを意味します

これはあなたがそれを行う方法です:

SET autocommit=0;
LOCK TABLES t1 WRITE, t2 READ, ...;
... do something with tables t1 and t2 here ...
COMMIT;
UNLOCK TABLES;

詳細については、mysql のドキュメントを参照してください。

于 2014-02-07T15:13:52.783 に答える
4

私の場合、MySQL内のアドバイザリロック機能を使用して、キャッシュを更新するためのミューテックスと、読み取り分離のためのトランザクションを実装します。例えば

begin_transaction(); // although reading a single row doesnt really require this
$cached=runquery("SELECT * FROM cache WHERE key=$id");
end_transaction();

if (is_expired($cached)) {
   $cached=refresh_data($cached, $id);
}
...

function refresh_data($cached, $id)
{
 $lockname=some_deterministic_transform($id);
 if (1==runquery("SELECT GET_LOCK('$lockname',0)") {
    $cached=fetch_source_data($id);
    begin_transaction();
    write_data($cached, $id);
    end_transaction();
    runquery("SELECT RELEASE_LOCK('$lockname')");
 }
 return $cached; 
}

(ところで:持続的接続でこれを試してみると、悪いことが起こる可能性があります)

于 2012-08-22T11:40:59.110 に答える
0

2 番目の問題は、データベースをまったく使用せずに解決できる場合があります。キャッシュ更新手順用のロック ファイルを用意して、他のクライアントが既に誰かが使用していることを認識できるようにします。これですべてのケースをキャッチできるわけではありませんが、2 つのクライアントが同時にキャッシュを更新している場合、それほど大きな問題になるのでしょうか? 結局のところ、彼らはトランザクションでキャッシュへの更新を行っているため、一貫性が保たれます。

最後にキャッシュを更新した時刻をテーブルに格納することで、自分でロックを実装することもできます。クライアントがキャッシュを更新したい場合は、そのテーブルをロックし、最終更新時刻を確認してからフィールドを更新します。

つまり、独自のロック メカニズムを実装して、複数のクライアントがキャッシュを更新しないようにします。トランザクションは残りを処理します。

于 2012-08-22T10:59:11.127 に答える