12

MySQL で競合状態を停止するにはどうすればよいですか? 当面の問題は、単純なアルゴリズムによって引き起こされます。

  1. テーブルから行を選択する
  2. 存在しない場合は、挿入します

次に、重複する行を取得するか、一意/主キーを介してそれを防止すると、エラーが発生します。

通常、ここではトランザクションが役立つと思いますが、行が存在しないため、トランザクションは実際には役に立ちません (または、何か不足していますか?)。

特にテーブルが 1 秒間に複数回更新される場合、LOCK TABLE はやり過ぎのように聞こえます。

私が考えることができる唯一の他の解決策は、すべての異なる ID に対して GET_LOCK() ですが、より良い方法はありませんか? ここでもスケーラビリティの問題はありませんか? また、すべてのテーブルに対してこれを行うのは少し不自然に聞こえます。これは、同時実行性の高いデータベースでは非常に一般的な問題のように思えます。

4

8 に答える 8

10

あなたが望むのはLOCK TABLESです

または、それが過剰に思われる場合は、行が実際に挿入されたことを確認してINSERT IGNOREを使用してください。

IGNORE キーワードを使用すると、INSERT ステートメントの実行中に発生したエラーは代わりに警告として扱われます。

于 2008-11-05T10:44:24.557 に答える
4

id 列に一意のインデックスを付ける必要があるように思われるため、繰り返し挿入すると、盲目的に再び受け入れられるのではなく、エラーが発生します。

これは、id を主キーとして定義するか、一意のインデックスを単独で使用することによって実行できます。

あなたが尋ねる必要がある最初の質問は、なぜまったく同じ作業をしている多くのスレッドがあるのですか? まったく同じ行を挿入する必要があるのはなぜですか?

それが答えられた後、エラーを無視することが最もパフォーマンスの高い解決策になると思いますが、両方のアプローチを測定し (GET_LOCK v/s はエラーを無視します)、自分の目で確かめてください。

私が知っている他の方法はありません。なぜエラーを回避したいのですか?別の種類のエラーが発生した場合に備えて、コーディングする必要があります。

staticsan が言うように、トランザクションは役に立ちますが、通常は暗示されているように、2 つの挿入が異なるスレッドによって実行される場合、それらは両方とも暗黙のトランザクション内にあり、データベースの一貫したビューが表示されます。

于 2008-11-05T10:45:23.133 に答える
3

テーブル全体をロックするのは確かにやり過ぎです。必要な効果を得るには、文学が「述語ロック」と呼ぶものが必要です。学術研究が発表された紙に印刷されたものを除いて、誰もそれらを見たことがありません。次善の策は、データへの「アクセスパス」のロックです(一部のDBMSでは「ページロック」)。

一部の非SQLシステムでは、(1)と(2)の両方を1つのステートメントで実行できます。つまり、OSが(1)と(2)の間で実行スレッドを一時停止することで発生する可能性のある競合状態は、完全に排除されました。

それにもかかわらず、述語ロックがない場合でも、そのようなシステムはある種のロックスキームに頼る必要があり、ロックの「粒度」(/「スコープ」)が細かいほど、並行性が向上します。

(結論として、一部のDBMS(特に料金を支払う必要のないDBMS)は、実際には「テーブル全体」よりも細かいロックの粒度を提供しません。)

于 2009-11-15T21:40:39.587 に答える
2

技術的なレベルでは、トランザクションをコミットするまで他のスレッドには新しい行が表示されないため、トランザクションはここで役立ちます。

しかし実際には、それは問題を解決しません-それはそれを動かすだけです。アプリケーションは、コミットが失敗したかどうかを確認し、何をすべきかを決定する必要があります。通常は、実行した内容をロールバックして、行が表示されるようになるため、トランザクションを再開します。これは、トランザクションベースのプログラマーが機能することになっている方法です。

于 2008-11-05T10:47:01.557 に答える
0

同様の問題があります。ほとんどの状況で一意の ticket_id 値を持つ必要があるテーブルがありますが、重複する場合があります。最高のデザインではありませんが、それはそれです。

  1. ユーザー A は、チケットが予約されているかどうかを確認しますが、予約されていません。
  2. ユーザー B は、チケットが予約されているかどうかを確認しますが、予約されていません。
  3. ユーザー B は、そのチケットのテーブルに「予約済み」レコードを挿入します。
  4. ユーザー A は、そのチケットのテーブルに「予約済み」レコードを挿入します。
  5. ユーザー B は重複をチェックしますか? はい、私の記録は新しいですか?はい、任せてください
  6. ユーザー A 重複チェック?はい、私の記録は新しいですか?いいえ、削除します

ユーザー B がチケットを予約しました。ユーザー A は、チケットが他の誰かによって取得されたことを報告します。

私の場合の鍵は、タイブレーカーが必要だということです。私の場合、それは行の自動インクリメント ID です。

于 2013-08-16T20:33:16.723 に答える
0

テーブルに一意のインデックスを配置するだけで、重複する行を簡単に防ぐことができます。これは LOCKS や TRANSACTIONS とは関係ありません。

重複しているために挿入が失敗しても気にしますか? 失敗した場合に通知する必要がありますか? それとも、行が挿入されたことだけが重要であり、誰によって、またはいくつの重複挿入が失敗したかは問題ではありませんか?

気にしないなら、必要なのはINSERT IGNORE. トランザクションやテーブル ロックについて考える必要はまったくありません。

InnoDB には自動的に行レベルのロックがありますが、これは更新と削除にのみ適用されます。インサートには適用されないというのは正しいです。まだ存在していないものをロックすることはできません。

LOCKテーブル全体を明示的に指定できます。しかし、あなたの目的が重複を防ぐことであるなら、それは間違っています。ここでも、一意のインデックスを使用します。

一連の変更を行う必要があり、オール オア ナッシングの結果が必要な場合 (または、より大きなオール オア ナッシングの結果内のオール オア ナッシングの結果のセットでさえ) は、トランザクションとセーブポイントを使用します。次に、ROLLBACKまたはROLLBACK TO SAVEPOINT *savepoint_name*を使用して、削除、更新挿入などの変更を元に戻します。

LOCKテーブルはトランザクションに代わるものではありませんが、トランザクションをサポートしない MyISAM テーブルの唯一のオプションです。行レベル レベルのロックでは不十分な場合は、InnoDB テーブルでも使用できます。lock table ステートメントでトランザクションを使用する方法の詳細については、このページを参照してください。

于 2013-01-14T06:18:01.937 に答える