MySQL 樂觀鎖 悲觀鎖 共享鎖 排他鎖
樂觀鎖
樂觀鎖是邏輯概念上的鎖,不是數據庫自帶的,需要我們自己去實現。樂觀鎖是指操作數據庫時(更新操作),想法很樂觀,認為這次的操作不會導致沖突,在操作數據時,並不進行任何其他的特殊處理(也就是不加鎖),而在進行更新後,再去判斷是否有沖突了。
通常實現是這樣的:在表中的數據進行操作時(更新),先給數據表加一個版本(version)字段,每操作一次,將那條記錄的版本號加1。也就是先查詢出那條記錄,獲取出version字段,如果要對那條記錄進行操作(更新),則先判斷此刻version的值是否與剛剛查詢出來時的version的值相等,如果相等,則說明這段期間,沒有其他程序對其進行操作,則可以執行更新,將version字段的值加1;如果更新時發現此刻的version值與剛剛獲取出來的version的值不相等,則說明這段期間已經有其他程序對其進行操作了,則不進行更新操作。
舉例:
下單操作包括3步驟:
1.查詢出商品信息
select (status,version) from t_goods where id=#{id}
2.根據商品信息生成訂單
3.修改商品status為2
update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
悲觀鎖
與樂觀鎖相對應的就是悲觀鎖了。悲觀鎖就是在操作數據時,默認此操作會出現數據沖突,所以在進行每次操作時都要通過獲取鎖才能進行對相同數據的操作,這點跟java中的synchronized很相似,所以悲觀鎖需要耗費較多的時間。悲觀鎖的實現,依靠數據庫提供的鎖機制,使用的時候直接調用數據庫的相關語句就可以了。
數據庫上的操作可以歸納為兩種:讀和寫。多個事務同時讀取一個對象的時候,是不會有沖突的。同時讀和寫,或者同時寫才會產生沖突。
MyIsam引擎會為查詢和更新等操作自動添加表級鎖,因此它的情況比較簡單。InnoDB引擎情況比較復雜,它通常會定義兩種鎖:共享鎖和排它鎖。
共享鎖
共享鎖(Shared Lock,也叫S鎖)表示對數據進行讀操作,加鎖後其他事務可以讀,但不能寫。多個事務可以同時為一個對象加共享鎖。
SELECT ... LOCK IN SHARE MODE走的是IS鎖(意向共享鎖),即在符合條件的rows上都加了共享鎖,這樣的話,其他人可以讀取這些記錄,也可以繼續添加IS鎖,但是無法修改這些記錄直到你這個加鎖的過程執行完成(完成的情況有:事務的提交,事務的回滾,否則直接鎖等待超時)。
SELECT ... LOCK IN SHARE MODE的應用場景適合於兩張表存在關系時的寫操作,拿mysql官方文檔的例子來說,一個表是child表,一個是parent表,假設child表的某一列child_id映射到parent表的c_child_id列,那麽從業務角度講,此時我直接insert一條child_id=100記錄到child表是存在風險的,因為剛insert的時候可能在parent表裏刪除了這條c_child_id=100的記錄,那麽業務數據就存在不一致的風險。正確的方法是再插入時執行select * from parent where c_child_id=100 lock in share mode,鎖定了parent表的這條記錄,然後執行insert into child(child_id) values (100)就不會存在這種問題了。
排它鎖
排他鎖(Exclusive Lock,也叫X鎖)也叫寫鎖。加鎖後,其他事務不能讀取,也不能寫。
如果一個事務對對象加了排他鎖,其他事務就不能再給它加任何鎖了。
產生排他鎖的sql: SELECT ... FOR UPDATE
簡單總結
共享鎖適用於兩張表存在業務關系時的一致性要求,排它鎖適用於操作同一張表時的一致性要求。
MySQL 樂觀鎖 悲觀鎖 共享鎖 排他鎖