web-dev-qa-db-ja.com

MySQLのトランザクションスコープ外のSELECT ... FOR UPDATEにリスクはありますか?

オブジェクトを選択するためにSQLを使用するプロジェクトでは、コンテキストがトランザクションであるかどうかにかかわらず、常に更新を選択します。

私の質問は、それがリスク、パフォーマンスの問題、またはデッドロックを引き起こす可能性があるかどうか、または何か他のいたずらですか?

Select for updateはトランザクション内でのみ行をロックできることを知っています。これは一般的な使用例です。

以下の2つの具体的な使用例を提供します。選択した更新後は、トランザクション内かどうかに関係なく使用されます。

使用例1-本を選択して返品するだけ

book = booksRepository->getBook(7);
return book;

使用例2-トランザクション、書籍を選択し、それが有効であることを確認し、有効な場合は更新します

bookRepository->startTransaction();
book = booksRepository->getBook(7);
if (//some condition with the book properties) {
   bookRepository->updateBookAuthor(7, 'bbb');
}
bookRepository->commitTransaction();

上記の両方の例で、リポジトリ内の関数getBookは更新を選択します

SELECT * FROM BOOKS
WHERE id = :id
FOR UPDATE;

私は他の開発者に質問し、なぜそうするのかという答えを出しました。「そうでなければ、リポジトリにgetBookとgetBookForUpdateの2つの関数を作成する必要があるため、常に更新のために選択するだけです...」.

その答えは私がどこで学ぶことができるかについての洞察や説明を提供していないので、私はここで尋ねています。

4
Kristi Jorgji

明示的に開始しなくても、クエリは常にトランザクション内にあります。 SELECT FOR UPDATEは、一致する行の排他ロックを取得します。他のトランザクションが同じレコードを変更する必要があり、SELECT FOR UPDATEが終了してロックを解放するまで待機する必要がある可能性があります。

そう。 SELECT FOR UPDATEを実行しても問題はありませんが、ロック待機の可能性があるため、全体的なパフォーマンスが低下する可能性があります。

デッドロックも可能です。他のトランザクションが共有ロックを保持していて、SELECT FOR UPDATEが待機している間にレコードを更新したい場合。

4
akuzminsky

すべてのステートメントはトランザクションにあります。 3つのケースがあります。

  • autocommit=ON、先行するBEGINなし:

ステートメントはトランザクションであり、事実上BEGIN; Sql; COMMIT;

  • autocommit=OFF、先行するBEGINなし:

COMMIT(または暗黙的にコミットするいくつかのDDL)を発行するまで、すべてのステートメントは同じトランザクションの一部です。 COMMITの発行を忘れるのは簡単すぎるため、このケースは嫌いです。

  • BEGINはまだCOMMITなしで発生しています:

COMMIT(または暗黙的にコミットするいくつかのDDL)を発行するまで、すべてのステートメントは同じトランザクションの一部です。

そう...

BEGIN...COMMIT(明示的または暗黙的)が実用的な時間になるようにコードを記述しても、心配する必要はありません。

実行時間の長いトランザクションを作成する場合、これはSELECT ... FOR UPDATEは、コードが予期せず停止する多くのケースの1つにすぎません。

つまり、1つの詳細に焦点を合わせるよりも、「全体像」を見ることが重要です。

その間、私は(証明なしで)あなたの孤独はSELECT ... FOR UPDATE他の接続がレコードをUPDATEしようとすると、ストールする可能性があります。しかし、それは良いことでしょうか?完了すると、値が更新されます。

0
Rick James