1

挿入されないようにロックする必要があるテーブルがありますが、挿入が防止されている間は更新できる必要もあります。

function myfunction() {
  $locked = mysql_result(mysql_query("SELECT locked FROM mylock"),0,0);
  if ( $locked ) return false;
  mysql_query("LOCK TABLES mylock WRITE");
  mysql_query("UPDATE mylock SET locked=1");
  mysql_query("UNLOCK TABLES");

  /* I'm checking another table to see if a record doesn't exist already */
  /* If it doesn't exist then I'm inserting that record */

  mysql_query("LOCK TABLES mylock WRITE");
  mysql_query("UPDATE mylock SET locked=0");
  mysql_query("UNLOCK TABLES");  
}

しかし、これでは十分ではありません。別のスクリプトから関数が再度呼び出され、関数への 2 つの呼び出しから同時に挿入が行われています。レコードが重複しているため、それを行うことはできません。

これは緊急です助けてください。フィールドで UNIQUE を使用することを考えましたが、2 つのフィールド (player1、player2) があり、どちらにもプレイヤー ID の重複を含めることはできません。

望ましくない動作: レコード A = ( Player1: 123 Player2: 456 ) Record B = ( Player1: 456 Player2: 123 )

4

3 に答える 3

1

コードで競合状態が発生していることに気付きました。エラーがないと仮定すると (私のコメントを参照)... 2 つのプロセスがチェックされ、「ロックされていない」という結果が得られます。「LOCK TABLES」はアクセスをシリアル化しますが、どちらもロックを持っていると考え続けるため、レコードが重複します。

次のように書き換えることができます。

mysql_query("LOCK TABLES mylock WRITE");
mysql_query("UPDATE mylock SET locked=1 WHERE locked=0");
$have_lock = mysql_affected_rows() > 0;
mysql_query("UNLOCK TABLES");
if (!$have_lock ) return false;
于 2012-08-16T05:00:05.933 に答える
0

ロックをまったく使用しないことをお勧めします。代わりに、データを挿入するときは、次のようにします。

mysql_query("INSERT IGNORE INTO my_table VALUES(<some values here>)");
if(mysql_affected_rows()>0)
{
    // the data was inserted without error
    $last_id = mysql_insert_id();
    // add what you need here
}
else
{
    // the data could not be inserted (because it already exists in the table)
    // query the table to retrieve the data
    mysql_query("SELECT * FROM my_table WHERE <some_condition>");
    // add what you need here
}

IGNOREキーワードをINSERTステートメントに追加すると、MySQL はデータの挿入を試みます。同じ主キーを持つレコードが既にテーブルにあるために機能しない場合は、サイレントに失敗します。mysql_affected_rowsは、挿入されたレコードの数を知り、何をすべきかを決定するために使用されます。

于 2012-08-16T04:29:06.680 に答える
0

ここではテーブル レベルのロックは必要ありません。行レベルのロックを使用することをお勧めします。行レベルのロックとは、変更している 1 つの行のみがロックされることを意味します。通常の代替手段は、変更中にテーブル全体をロックするか、テーブルの一部のサブセットをロックすることです。行レベルのロックは、その行のサブセットを、整合性を確保できる最小の数に単純に減らします。

InnoDB トランザクション モデルの目標は、マルチ バージョン データベースの最良の特性と従来の 2 フェーズ ロックを組み合わせることです。InnoDB は行レベルでロックを行い、デフォルトでは、Oracle のスタイルで非ロックの一貫した読み取りとしてクエリを実行します。InnoDB のロック テーブルはスペース効率よく格納されるため、ロックのエスカレーションは必要ありません。通常、複数のユーザーが InnoDB テーブルのすべての行、または行の任意のランダムなサブセットをロックすることが許可されており、InnoDB のメモリが枯渇することはありません。

問題がまだ解決されていない場合は、メモリ サイズが問題である可能性があります。InnoDB は、そのロック テーブルをメイン バッファー プールに格納します。これは、同時に保持できるロックの数が、MySQL の起動時に設定された innodb_buffer_pool_size 変数によって制限されることを意味します。デフォルトでは、MySQL はこれを 8MB のままにしておきます。これは、サーバーで InnoDB を使用して何かを行っている場合にはほとんど役に立ちません。

幸いなことに、この問題の修正は非常に簡単です。innodb_buffer_pool_size をより妥当な値に調整します。ただし、その修正には MySQL デーモンの再起動が必要です。この変数をその場で調整する方法はまったくありません (この投稿の執筆時点での現在の安定した MySQL バージョンでは)。

変数を調整する前に、サーバーが追加のメモリ使用量を処理できることを確認してください。innodb_buffer_pool_size 変数はサーバー全体の変数であり、スレッドごとの変数ではないため、MySQL サーバーへのすべての接続間で共有されます (クエリ キャッシュなど)。1GB などに設定すると、MySQL はそのすべてを前もって使用しません。MySQL がバッファに入れるものをさらに見つけると、メモリ使用量は 1GB に達するまで徐々に増加します。その時点で、新しいデータが必要になると、最も古くて使用頻度の低いデータが削除され始めます。

于 2012-08-16T04:37:55.297 に答える