SELECT … FOR UPDATE 在MySQL的事務範圍之外是否有任何風險?
在我工作的項目中,用於選擇對象的 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)之前,所有語句都是同一事務的一部分。所以…
如果您編寫程式碼以使
BEGIN
…COMMIT
(顯式或隱式)盡可能短,那麼請不要擔心。如果您編寫長時間執行的事務,那麼這
SELECT ... FOR UPDATE
只是您的程式碼會意外停止的眾多情況之一。也就是說,看“大局”比關註一個細節更重要。
同時,我相信(沒有證據)
SELECT ... FOR UPDATE
如果其他一些連接即將UPDATE
記錄,你的孤獨可能會停止。但也許這是一件好事?當它完成時,它將具有更新的值。