目錄
一、MySQL鎖機制
1.1 按鎖粒度劃分
1.2 按鎖功能劃分
1.3 InnoDB鎖實現機制
(1)記錄鎖(Record Lock)
(2) 間隙鎖(Gap Lock)
(3) 臨鍵鎖(Next-Key Lock)
(4) 插入意向鎖(Insert Intention Lock)
二、基于 JVM 本地鎖實現,保證線程安全
2.1 線程不安全的分析
2.1.1 多線程并發訪問(未加鎖)
2.2 基于 synchronized 加鎖訪問
2.3 基于 synchronized 加鎖訪問
2.4 JVM 本地鎖的缺陷
三、基于 MySQL 鎖實現,保證線程安全問題
3.1 基于原子 SQL 實現
3.2 基于悲觀鎖實現(靈活多 SQL )
3.2.1 原生 SQL 實現
3.2.2 Java 代碼實現
3.2.3 悲觀鎖優缺點
3.2.4 死鎖演示
3.3 基于樂觀鎖實現(CAS)
3.3.1 原生SQL實現
3.3.2 Java 代碼實現
3.3.3 樂觀鎖存在的問題
四、本地不同類型鎖的總結
一、MySQL鎖機制
1.1 按鎖粒度劃分
-
表級鎖?:鎖定整張表
- 優點:開銷小,加鎖快
- 缺點:并發度低
- 實現:
LOCK TABLES
語句或存儲引擎自動加鎖
-
?行級鎖?:鎖定單行記錄
- 優點:并發度高
- 缺點:開銷大,加鎖慢
- 實現:InnoDB通過索引實現
-
?頁級鎖?:鎖定一頁(16KB)
- 折中方案,BDB引擎使用
1.2 按鎖功能劃分
-
?共享鎖(S鎖)??:
- 語法:
SELECT ... LOCK IN SHARE MODE
- 特性:多個事務可同時獲取,但不能與排他鎖共存
- 語法:
-
?排他鎖(X鎖)??:
- 語法:
SELECT ... FOR UPDATE
- 特性:獨占鎖,其他事務不能獲取任何鎖
- 語法:
-
?意向鎖(Intention Lock)??:
- 表級鎖,表示事務將要獲取行鎖
- IS鎖(意向共享鎖):事務準備給行加S鎖
- IX鎖(意向排他鎖):事務準備給行加X鎖
1.3 InnoDB鎖實現機制
(1)記錄鎖(Record Lock)
- 鎖定索引中的單條記錄
- 實現方式:通過索引項加鎖
(2) 間隙鎖(Gap Lock)
- 鎖定索引記錄間的間隙
- 防止幻讀問題
- 示例:
SELECT * FROM t WHERE id > 10 AND id < 20 FOR UPDATE
(3) 臨鍵鎖(Next-Key Lock)
- 記錄鎖+間隙鎖的組合
- 鎖定記錄及記錄前的間隙
- InnoDB默認行鎖算法
(4) 插入意向鎖(Insert Intention Lock)
- 特殊的間隙鎖
- 多個事務在相同間隙插入不同記錄時不沖突
二、基于 JVM 本地鎖實現,保證線程安全
2.1 線程不安全的分析
多線程環境下多個線程(并發用戶訪問)訪問同一個共享資源,并對資源進行修改(觸發了線程安全問題)。
接下來只關注 Service 層的邏輯實現
2.1.1 多線程并發訪問(未加鎖)
1. 創建多線程環境下生產級減庫存案例
@Service
public class StockService {private Stock stock = new Stock();public void deduct(){stock.setStock(stock.getStock()-1);System.out.println("扣減成功,剩余庫存:"+stock.getStock());}
}
2. 運行項目,通過 JMeter 進行并發測試:
查看聚合報告,樣本數量為 5000 次,吞吐量為 1900個事務/秒 左右。
3. 查看項目日志:發生超賣現象。
此時查看 MySQL 表中的 1001 商品編號的第一條記錄,校驗庫存數量是否為0:
2.2 基于 synchronized 加鎖訪問
1. 修改減庫存的方法,進行加鎖操作:synchronized 直接修改方法(底層是基于 Monitor 實現)