mysql默認事務下樂觀鎖解決方案

一、sql語句

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '',
  `subject` varchar(20) NOT NULL COMMENT '科目',
  `score` int(11) NOT NULL DEFAULT '0' COMMENT '分數',
  `version` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_key` (`name`,`subject`) USING BTREE COMMENT '唯一索引'
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of student
-- ----------------------------
BEGIN;
INSERT INTO `student` VALUES (1, '阿飛', '語文', 99, 0);
INSERT INTO `student` VALUES (2, '阿飛', '數學', 98, 0);
INSERT INTO `student` VALUES (3, '阿飛', '英語', 79, 0);
INSERT INTO `student` VALUES (4, '雪梨', '語文', 89, 1);
INSERT INTO `student` VALUES (5, '雪梨', '數學', 78, 0);
INSERT INTO `student` VALUES (6, '雪梨', '英語', 67, 0);
INSERT INTO `student` VALUES (7, '小月', '語文', 100, 2);
INSERT INTO `student` VALUES (8, '小月', '數學', 99, 0);
INSERT INTO `student` VALUES (9, '小月', '英語', 98, 0);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

二、問題及解決方案:

1、mysql的事務默認隔離級別爲可重複讀(repeatable-read):

1、 一個事務讀取到另一個事務提交前後的數據保持不變
2、 如果兩個事務對同一條數據做修改,都未提交時候,晚修改的會阻塞住,直到早提交的事務commit或rollback了才能繼續執行

2、模擬程序:高併發下可能出問題的事務語句

BEGIN;
# 一個事務中查詢的數據有可能在執行update的時候已經被別的事務修改並提交了,
# 所以這時候如果執行更新語句(更新內容依賴查詢結果的數據)會出現非預期錯誤;
# 如下語句;
SELECT s.score FROM student s  WHERE s.`name` = '阿飛' and s.`subject` = '數學';
# 下面語句中score是從查詢語句獲取,此時的score有可能是錯誤的數據,已經被之前的事務所修改了
# 比如 開始查詢的score是100,然後另一個事務修改成了99並提交,此時再執行update拿的score依然是100,然後修改成了99
# 實際上預期的值應該是98
UPDATE student s SET s.score = s.score-1 WHERE s.`name` = '阿飛' and s.`subject` = '數學';
# 此時提交不是預期結果
commit;

# 解決辦法:樂觀鎖思想(版本號控制,即數據庫加上版本號version字段)
BEGIN;
# 每次查詢的時候都查出版本號version; 
SELECT s.score,s.`version` FROM student s  WHERE s.`name` = '阿飛' and s.`subject` = '數學';
# 程序中score是從查詢語句獲取,更新的時候條件帶上版本號
UPDATE student s SET s.score = s.score-1 , s.`version`= s.`version`+1  
WHERE s.`name` = '阿飛' and s.`subject` = '數學' and s.`version` = 0;
# 有可能更新失敗,因爲版本號已經升級,不會出現錯誤結果
commit;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章