mysqlは競合状態を挿入します
-
06-07-2019 - |
質問
MySQLの競合状態をどのように停止しますか?目前の問題は、単純なアルゴリズムが原因です:
- 表から行を選択
- 存在しない場合は挿入します
そして重複した行を取得するか、一意/主キーを介してそれを防ぐとエラーになります。
通常はここでトランザクションが役立つと思いますが、行が存在しないため、トランザクションは実際には役に立ちません(または何か不足していますか?)。
LOCK TABLEは、テーブルが1秒間に複数回更新される場合は特に、やり過ぎのように聞こえます。
私が考えることができる他の唯一の解決策は、すべての異なるIDに対するGET_LOCK()ですが、より良い方法はありませんか?ここでもスケーラビリティの問題はありませんか?また、すべてのテーブルでこれを行うのは少し不自然に聞こえます。これは、同時実行性の高いデータベースでは非常に一般的な問題のように聞こえます。
解決
または INSERT IGNORE が過剰な場合行が実際に挿入されたことの確認。
IGNOREキーワードを使用すると、エラー INSERTの実行中に発生する ステートメントは警告として扱われます 代わりに。
他のヒント
id列に一意のインデックスを設定する必要があるように思えるので、繰り返し挿入すると、盲目的に再び受け入れられるのではなく、エラーが発生します。
それは、idを主キーとして定義するか、一意のインデックスを単独で使用することで実行できます。
最初に質問する必要があるのは、多くのスレッドがまったく同じ作業をしているのはなぜですか?まったく同じ行を挿入する必要があるのはなぜですか?
それが回答された後、エラーを無視することが最もパフォーマンスの高いソリューションになると思いますが、両方のアプローチを測定し(GET_LOCK v / sエラーを無視)、自分で確認してください。
私が知っている他の方法はありません。なぜエラーを避けたいのですか?別のタイプのエラーが発生した場合は、ケースのコードを作成する必要があります。
staticsanがトランザクションは役立つと言いますが、通常暗示されているように、2つの挿入が異なるスレッドで実行される場合、それらは両方とも暗黙のトランザクション内にあり、データベースの一貫したビューを表示します。
テーブル全体をロックするのは本当にやり過ぎです。希望する効果を得るには、リテラチャーが「述語ロック」と呼ぶものが必要です。学術研究が出版されている論文に印刷されているものを除いて、誰も見たことがない。次に最適なのは、「アクセスパス」のロックです。データへ(一部のDBMSの「ページロック」)。
一部の非SQLシステムでは、(1)と(2)の両方を1つのステートメントで実行できます。これは、OSが(1)と(2)の間で実行スレッドを一時停止することから生じる潜在的な競合状態を意味します、完全に排除されます。
それにもかかわらず、述語ロックがない場合、そのようなシステムはまだ何らかの種類のロック方式に頼る必要があり、「粒度」が細かくなればなるほど、 (/" scope")必要なロックの数は、並行性に優れています。
(結論として、一部のDBMS-特に料金を支払う必要のないDBMS-は、「テーブル全体」よりも細かいロック粒度を提供しません。)
技術レベルでは、トランザクションがコミットされるまで他のスレッドには新しい行が表示されないため、ここでトランザクションが役立ちます。
しかし、実際には問題を解決しません-それは移動するだけです。アプリケーションは、コミットが失敗したかどうかを確認し、何をすべきかを決定する必要があります。行が表示されるようになるので、通常は実行した内容をロールバックし、トランザクションを再開します。これが、トランザクションベースのプログラマの動作方法です。
同じ問題にぶつかり、しばらくネットを検索しました:)
最後に、一時ファイルを安全に開くための共有(一時)ディレクトリでのファイルシステムオブジェクトの作成:
$exists = $success = false;
do{
$exists = check();// select a row in the table
if (!$exists)
$success = create_record();
if ($success){
$exists = true;
}else if ($success != ERROR_DUP_ROW){
log_error("failed to create row not 'coz DUP_ROW!");
break;
}else{
//probably other process has already created the record,
//so try check again if exists
}
}while(!$exists)
ビジーループを恐れないでください-通常は1回または2回実行されます。
テーブルに一意のインデックスを配置するだけで、重複行を非常に簡単に防ぐことができます。これはLOCKSやTRANSACTIONSとは関係ありません。
挿入が重複しているために失敗するかどうか気にしますか?失敗した場合に通知する必要がありますか?または、行が挿入されたことが重要であり、誰が失敗したか、重複挿入が何回失敗したかは関係ありませんか?
気にしない場合、必要なのは INSERT IGNORE
だけです。トランザクションやテーブルロックについて考える必要はまったくありません。
InnoDBには行レベルのロックが自動的にありますが、これは更新と削除にのみ適用されます。挿入には適用されないのは正しいことです。まだ存在しないものをロックすることはできません!
テーブル全体を明示的に LOCK
できます。ただし、重複を防ぐことが目的の場合は、間違っています。繰り返しますが、一意のインデックスを使用します。
一連の変更があり、オールオアナッシングの結果(または、より大きなオールオアナッシングの結果内のオールオアナッシングの結果)が必要な場合は、トランザクションとセーブポイント。次に、 ROLLBACK
または ROLLBACK TO SAVEPOINT * savepoint_name *
を使用して、削除、更新、挿入などの変更を元に戻します。
LOCK
テーブルはトランザクションの代替ではありませんが、トランザクションをサポートしないMyISAMテーブルの唯一のオプションです。行レベルのロックだけでは不十分な場合は、InnoDBテーブルでも使用できます。詳細については、このページをご覧ください。ロックテーブルステートメントでトランザクションを使用します。
同様の問題があります。ほとんどの場合、一意のticket_id値を持つテーブルがありますが、重複する場合があります。最高のデザインではありませんが、それはそれです。
- ユーザーAはチケットが予約されているかどうかを確認しますが、予約されていません
- ユーザーBは、チケットが予約されているかどうかを確認しますが、予約されていません
- ユーザーBは、そのチケットのテーブルに「予約済み」レコードを挿入します
- ユーザーAは、そのチケットのテーブルに「予約済み」レコードを挿入します
- ユーザーBは重複をチェックしますか?はい、私の記録は新しいですか?はい、そのままにします
- ユーザーAは重複をチェックしますか?はい、私の記録は新しいですか?いいえ、削除します
ユーザーBはチケットを予約しました。ユーザーAは、チケットが他の誰かに奪われたことを報告します。
私のインスタンスのキーは、タイブレーカーが必要なことです。私の場合、それは行の自動インクリメントIDです。