[英]Understanding MySQL InnoDB locks
我對 MySQL InnoDB 鎖的工作方式感到困惑。
為了更好地理解它,我進行了以下實驗。 我已經使用 Spring boot 和 JPA 在下表中插入/更新記錄(盡管這個問題與 Spring boot 或 JPA 無關) 。
表名: test
列名 | 類型 | 約束 |
---|---|---|
ID | 大整數 | PK人工智能 |
姓名 | 瓦爾查爾 | |
值 | 整數 | |
br_id | 整數 |
Innodb 鎖等待超時: 50 秒
存儲庫: ITestRepository.java
@Modifying
@Query(value = "update test set val=val+1 where val>:val and br_id=:brId", nativeQuery = true) // Just a random update query
public void updateVal(Integer val, Integer brId);
服務: TestService.java
@org.springframework.transaction.annotation.Transactional
public void saveMany(final int brId) { // brId is always passed as 1
for (int i = 0; i < 200; i++) {
this.testRepository.updateVal(i, brId); // ======> LINE: 1
this.testRepository.save(new TestEntity(String.valueOf(i), i, brId)); // ======> LINE: 2
try {
Thread.sleep(2000);
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
@org.springframework.transaction.annotation.Transactional
public void saveOne(final int val, final int brId) { // brId is always passed as 2
this.testRepository.updateVal(val, brId); // ======> LINE: 3
this.testRepository.save(new TestEntity(String.valueOf(val), val, brId)); // ======> LINE: 4
}
update
查詢不會更新任何內容。 案例 1:只觸發一次saveMany
方法
在這種情況下,一切正常,一旦事務完成且沒有任何錯誤,就會插入 200 行。
案例2:觸發saveMany
第一,然后saveOne
一次
在這種情況下,如果我們觸發saveOne
,當saveMany
正在執行時, saveOne
將在 50 秒后因Lock wait timeout
異常而失敗,並且一旦循環結束, saveMany
將成功完成。
案例 3:評論LINE: 1
& LINE: 3
或評論LINE: 2
& LINE: 4
並先觸發saveMany
,然后再saveOne
在這種情況下,如果我們觸發saveOne
,當saveMany
正在執行時,一切都會正常工作,兩個方法都會成功完成,沒有任何異常。
案例4:評論LINE: 1
並觸發saveMany
先,然后saveOne
一次
在這種情況下,如果我們觸發saveOne
,當saveMany
正在執行時, saveOne
將在LINE: 3
上失敗,並且更新操作的lock wait timeout
異常。
案例5:評論LINE: 2
並先觸發saveMany
,然后saveOne
一次
在這種情況下,如果我們觸發saveOne
,當saveMany
正在執行時, saveOne
將在LINE: 4
上失敗,並且插入操作的lock wait timeout
異常。
從上面的案例這里是我的推導:
inserts
或updates
不會鎖定表insert
和update
會鎖表(先觸發哪個操作先獲取鎖) 我無法理解上述兩種推導是如何工作的。 我的意思是從 MySQL 文檔中,他們聲明整個表永遠不會在 InnoDB 中鎖定,並且只有在執行inserts
或updates
時才鎖定行。 正如你在上面的例子中看到的,兩種方法的br_id
總是不同的,因此updates
是在不同的行集上執行的,那么為什么會引發lock wait timeout
異常? 此外,並行inserts
或updates
不會導致任何問題,如何以及為什么?
編輯 1:
如果br_id
沒有被索引,那么它的工作方式和上面的情況一樣,但是如果br_id
被索引,那么Deadlock found when trying to get lock; try restarting transaction
Deadlock found when trying to get lock; try restarting transaction
異常而執行被立即拋出saveOne
方法並行saveMany
方法。
update test
set val = ...
where val > ...
and br_id = ...
由於相關列上沒有索引,所有行都被鎖定。
最好有
INDEX(br_id)
或者
INDEX(br_id, val)
后者更適合定位要修改的行,但缺點是需要更新索引的 BTree。
請注意,某些“鎖”是“間隙鎖”。 這涉及鎖定索引值之間的差距。 這有時會引起意外。 (我不知道這是否與此更新有關。)
查看鎖的一種工具是SHOW ENGINE INNODB STATUS;
(由於那里的信息是暫時的,它可能無法顯示您需要的信息。)
BEGIN;
UPDATE ...
((spend lots of time before COMMIT))
COMMIT;
這將延遲或死鎖需要相同行鎖的任何競爭連接。
幾乎總是 InnoDB 可以看到簡單地停止(最多innodb_lock_wait_timeout
秒)以獲得必要的鎖。
InnoDB總是可以發現死鎖,並且會ROLLBACK
其中一個事務,同時讓另一個完成。
有幾種情況(尤其是“間隙鎖定”,其中 InnoDB 在可能沒有死鎖的情況下保守地聲明一個死鎖。(我不知道這是否是您的情況。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.