Mysql

SELECT … FOR UPDATE 在MySQL的事務範圍之外是否有任何風險?

  • July 9, 2021

在我工作的項目中,用於選擇對象的 sql 總是選擇更新,無論上下文是否為事務。

我的問題是它是否有任何風險,性能問題,或者導致死鎖或其他頑皮的機會?

我知道 select for update 只能在事務中鎖定一行,這是常見的案例。

我提供了以下兩個具體案例,無論是否在事務內部,都在選擇更新後使用。

案例 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,所以我們只選擇始終進行更新”……

該答案沒有為我可以在哪裡學習提供任何見解或解釋,所以我在這裡問。

即使您沒有明確啟動查詢,查詢也始終在事務中。 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)之前,所有語句都是同一事務的一部分。

所以…

如果您編寫程式碼以使BEGINCOMMIT(顯式或隱式)盡可能短,那麼請不要擔心。

如果您編寫長時間執行的事務,那麼這SELECT ... FOR UPDATE只是您的程式碼會意外停止的眾多情況之一。

也就是說,看“大局”比關註一個細節更重要。

同時,我相信(沒有證據)SELECT ... FOR UPDATE如果其他一些連接即將UPDATE記錄,你的孤獨可能會停止。但也許這是一件好事?當它完成時,它將具有更新的值。

引用自:https://dba.stackexchange.com/questions/211456