mysql中的鎖機制之悲觀鎖和樂觀鎖

  • 2019 年 11 月 8 日
  • 筆記

1、悲觀鎖?

悲觀鎖顧名思義就是很悲觀,悲觀鎖認為數據隨時就有可能會被外界進行修改,所以悲觀鎖一上來就會把數據給加上鎖。悲觀鎖一般都是依靠關係型資料庫提供的鎖機制,然而事實上關係型資料庫中的行鎖,表鎖不論是讀寫鎖都是悲觀鎖。

2、樂觀鎖?

樂觀鎖顧名思義,就是很樂觀,每次自己操作數據的時候認為沒有人會來修改它,所以不會去對數據進行加鎖。但是在更新的時候會去判斷在此期間數據有沒有被修改,需要用戶自己去實現樂觀鎖。樂觀鎖不會發生並發搶佔資源,只有在提交操作的時候檢查是否違反數據完整性。

2.1、為什麼要使用樂觀鎖呢?

①、對於讀操作遠多於寫操作的時候,大多數都是讀取,這時候一個更新操作加鎖會阻塞所有讀取,降低了吞吐量

②、最後還要釋放鎖,鎖是需要一些開銷的,我們只要想辦法解決極少量的更新操作的同步問題

③、換句話說,如果讀寫比例差距不是非常大或者你的系統沒有響應不及時,吞吐量瓶頸問題,那就不要去使用樂觀鎖,它增加了複雜度,也帶來了額外的風險

2.2、樂觀鎖的實現方式

前期準備工作(準備數據表和測試數據):

CREATE TABLE `employee`  (    `id` int(11) NOT NULL AUTO_INCREMENT,    `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,    `dep_id` int(11) NULL DEFAULT NULL,    `age` int(11) NULL DEFAULT NULL,    `salary` decimal(10, 2) NULL DEFAULT NULL,    `cus_id` int(11) NULL DEFAULT NULL,    `version` int(11) NULL DEFAULT NULL COMMENT '用來給樂觀鎖進行標識的欄位',    PRIMARY KEY (`id`) USING BTREE,    INDEX `idx_name_age`(`name`, `age`) USING BTREE  ) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;      -- 插入測試數據  INSERT INTO `employee` VALUES (1, '李白', 1, 10, 1000.00, 1, 1);  INSERT INTO `employee` VALUES (2, '韓信', 1, 20, 2000.00, 1, 1);  INSERT INTO `employee` VALUES (3, '露娜', 1, 20, 2500.00, 1, 1);  INSERT INTO `employee` VALUES (4, '公孫離', 4, 20, 3000.00, 1, 1);  INSERT INTO `employee` VALUES (5, '虞姬', 4, 40, 3500.00, 2, 1);  INSERT INTO `employee` VALUES (6, '孫尚香', 6, 20, 5000.00, 1, 1);  INSERT INTO `employee` VALUES (7, '馬可波羅', 6, 50, 5000.00, 1, 1);  INSERT INTO `employee` VALUES (8, '后羿', 30, 35, 4000.00, 1, 1);

2.2.1、版本號的實現方式:

①、就是給數據增加一個版本標識,在數據表上增加一個version欄位

②、每次更新把這個欄位加1

③、讀取數據的時候把version讀出來,更新的時候比較version

④、如果還是開始讀取的version就可以更新了

⑤、如果現在的version比老的version大,說明有其他事務更新了該數據,並增加了版本號

⑥、這時候得到一個無法更新的通知,用戶自行根據這個通知來決定怎麼處理,比如重新開始一遍

版本號的方式實現樂觀鎖示例:

-- 先獲取version欄位的值,這裡version欄位的值目前是:1  SELECT version FROM employee WHERE id = 1;    -- 更新該條數據的時候做數據驗證,更新成功後將version欄位的值累加1,更新條件除了id還要加上version=上面取出來的值 才能進行更新  UPDATE employee SET `name` = '李白大魔王',version = version + 1 WHERE id = 1 AND version = 1;

示例截圖:

2.2.2、時間戳的實現方式:

①、和上面的版本號基本一樣,只是通過時間戳來判斷而已,注意時間戳要使用資料庫伺服器的時間戳不能是業務系統的時間

②、同樣是在需要樂觀鎖控制的table(表)中增加一個欄位,欄位名稱無所謂

③、欄位類型使用時間戳, 和上面的version類似,也是在更新提交的時候檢查當前資料庫中數據的時間戳和自己更新前取到的時間戳進行對比

④、如果一致則OK 執行更新操作,否則就是版本衝突