30

2つの接続が同じレコードを更新したいときにMySQLデータベースの競合状態を防ぐにはどうすればよいですか?

たとえば、接続1は「試行」カウンターを増やしたいと考えています。そして、2番目の接続も同じことをしたいと考えています。両方の接続SELECTで「試行」がカウントされ、値が増加し、両方の接続で値UPDATEが増加します。突然、「tries」は「tries + 2」ではなく、「tries + 1」になります。これは、両方の接続が同じ「tries」を取得し、それを1つインクリメントしたためです。

この問題を解決する方法は?

4

2 に答える 2

51

3つの異なるアプローチがあります:

アトミックアップデート

update table set tries=tries+1 where condition=value;

そしてそれは原子的に行われます。

トランザクションを使用する

最初に値を選択してアプリケーションで更新する必要がある場合は、トランザクションを使用する必要があります。つまり、MyISAMテーブルではなく、InnoDBを使用する必要があります。クエリは次のようになります。

BEGIN; //or any method in the API you use that starts a transaction
select tries from table where condition=value for update;
.. do application logic to add to `tries`
update table set tries=newvalue where condition=value;
END;

トランザクションが失敗した場合は、手動で再試行する必要がある場合があります。

バージョンスキーム

一般的なアプローチは、テーブルにバージョン列を導入することです。クエリは次のようになります。

select tries,version from table where condition=value;
.. do application logic, and remember the old version value.
update table set tries=newvalue,version=version + 1 where condition=value and version=oldversion;

その更新が失敗した場合/影響を受けた0行が返される場合、他の誰かがその間にテーブルを更新しました。最初からやり直す必要があります。つまり、新しい値を選択し、アプリケーションロジックを実行して、更新を再試行する必要があります。

于 2010-03-02T15:34:56.933 に答える
15

2つではなく1つのステートメントを使用します。読み取りと書き込みの両方を実行する単一のUPDATEステートメントはアトミックであり、別の同時更新と競合しません。

UPDATE table SET tries = tries + 1 WHERE ...

または、トランザクションを使用して2つの操作をアトミックにすることができます。

BEGIN
SELECT ...
UPDATE ...
COMMIT

または、より原始的には、テーブルへの読み取り/書き込み中にテーブルをロックします。

LOCK TABLES table WRITE
SELECT ...
UPDATE ...
UNLOCK TABLES
于 2010-03-02T15:31:47.987 に答える