オブジェクトを選択するためにSQLを使用するプロジェクトでは、コンテキストがトランザクションであるかどうかにかかわらず、常に更新を選択します。
私の質問は、それがリスク、パフォーマンスの問題、またはデッドロックを引き起こす可能性があるかどうか、または何か他のいたずらですか?
Select for updateはトランザクション内でのみ行をロックできることを知っています。これは一般的な使用例です。
以下の2つの具体的な使用例を提供します。選択した更新後は、トランザクション内かどうかに関係なく使用されます。
book = booksRepository->getBook(7);
return book;
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つの関数を作成する必要があるため、常に更新のために選択するだけです...」.
その答えは私がどこで学ぶことができるかについての洞察や説明を提供していないので、私はここで尋ねています。
明示的に開始しなくても、クエリは常にトランザクション内にあります。 SELECT FOR UPDATE
は、一致する行の排他ロックを取得します。他のトランザクションが同じレコードを変更する必要があり、SELECT FOR UPDATE
が終了してロックを解放するまで待機する必要がある可能性があります。
そう。 SELECT FOR UPDATE
を実行しても問題はありませんが、ロック待機の可能性があるため、全体的なパフォーマンスが低下する可能性があります。
デッドロックも可能です。他のトランザクションが共有ロックを保持していて、SELECT FOR UPDATE
が待機している間にレコードを更新したい場合。
すべてのステートメントはトランザクションにあります。 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
しようとすると、ストールする可能性があります。しかし、それは良いことでしょうか?完了すると、値が更新されます。