宏觀
併發造成什麼問題?
- 髒讀 : 一個事物讀取了另一個事物未提交的數據
- 不可重複讀 : 一個事物多次讀取一行數據導致前後數據不一致
- 幻讀: 一個事物讀取了別的事物插入的數據
後面會詳細的講解爲什麼,O(∩_∩)O嘿嘿~
模擬事物隔離級別
事物特性
- 原子性A ,指的是不可再次分割, 也就是A和B事物要麼成功, 要麼失敗
- 一致性C: 表示,事物讀取數據要保證正確,一致性
- 隔離性I: 事物A不可以對事物B的數據進行更改
- 持久性D: 事物A操作之後數據存儲在了磁盤上,不會丟失
隔離級別
- 讀未提交: 髒讀, 不可重複讀, 幻讀
- 讀已提交: 不可重複讀, 幻讀
- 可重複讀 : 幻讀
- 串行化 : 安全, 都沒有
隔離級別舉栗子
首先根據下面的sql結構測試栗子
-- ----------------------------
-- Table structure for t_student
-- ----------------------------
DROP TABLE IF EXISTS `t_student`;
CREATE TABLE `t_student` (
`sno` int(11) NOT NULL,
`sname` varchar(255) CHARACTER SET gbk DEFAULT NULL,
`sdree` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`ssex` char(255) CHARACTER SET utf8 DEFAULT NULL,
`sage` decimal(10,0) DEFAULT NULL,
PRIMARY KEY (`sno`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of t_student
-- ----------------------------
INSERT INTO `t_student` VALUES ('1', 'judy', '天融信', '男', '26');
INSERT INTO `t_student` VALUES ('2', '杜雨', '中銀', '男', '26');
INSERT INTO `t_student` VALUES ('3', '雅雯雯', '買賣寶', '男', '27');
INSERT INTO `t_student` VALUES ('4', '子騰', 'IBM', '男', '23');
INSERT INTO `t_student` VALUES ('5', '旭日', '微軟', '女', '25');
1 查看當前數據庫隔離級別, 設置隔離級別
SELECT @@tx_isolation;
-- 非正常情況下
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
讀未提交出現的併發問題
A 窗口執行sql
start transaction;
select * FROM t_student WHERE sno ='1';
結果:
1 judy 天融信 男 26
B 窗口執行sql
start transaction;
select * FROM t_student WHERE sno ='1';
UPDATE t_student SET sname='wangxuefen' WHERE sno =1;
執行後的結果
1 wangxuefen 天融信 男 26
A 窗口執行查詢語句(這句話出現的問題)
select * FROM t_student WHERE sno ='1';
得到結果: 1 wangxuefen 天融信 男 26
B 窗口執行
ROLLBACK;
select * FROM t_student WHERE sno ='1';
得到的結果是
1 judy 天融信 男 26
A 窗口執行
select * FROM t_student WHERE sno ='1';
得到的結果是
1 judy 天融信 男 26
從最後的執行結果中我們可以發現A讀取了B未提交的數據, 當B事物回滾的時候, A之前讀到的數據變成了髒數據, 這就是髒讀
提交讀出現的問題
1 更改事物的隔離級別
SELECT @@tx_isolation;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
A 窗口
start transaction;
select * FROM t_student WHERE sno ='1';
執行結果
1 judy 天融信 男 26
B 窗口
start transaction;
UPDATE t_student SET sname='12' WHERE sno =1;
A 窗口 (出現問題的原因,B還沒有提交但是已經讀了,發送不可重複讀問題)
select * FROM t_student WHERE sno ='1';
執行結果
1 judy 天融信 男 26
B 窗口 (出現問題的原因)
COMMIT
A 窗口
select * FROM t_student WHERE sno ='1';
執行結果
1 12 天融信 男 26
讀已提交,出現不可重複讀問題
可重複讀 REPEATABLE-READ(mysql默認級別)
A 窗口
start transaction;
SELECT * FROM t_student WHERE sno='1'
執行結果:1 judy 天融信 男 26
B 窗口
start transaction;
SELECT * FROM t_student WHERE sno='1';
UPDATE t_student SET sname='wxf' WHERE sno =1;
SELECT * FROM t_student WHERE sno='1';
執行結果
1 wxf 天融信 男 26
A 窗口 (這裏可以跟讀已提交隔離級別進行對比)
1 judy 天融信 男 26
B 窗口
COMMIT
A 窗口
SELECT * FROM t_student WHERE sno='1'
得到結果:
1 judy 天融信 男 26
從得出的結果可以看出我們已經避免了可重複讀的問題, 但是發現最後一次的A窗口得到的結果卻不是我們想要的數據,也就是不是最後更改後的數據.
事物隔離級別設置爲串行化 Serializable
事物隔離級別是最爲安全的,設置隔離級別
SET SESSION tx_isolation='SERIALIZABLE';
A窗口
start transaction ;
B 窗口
start transaction ;
A窗口
SELECT * FROM t_student WHERE sno='1';
得到結果:1 wxf 天融信 男 26
B窗口
UPDATE t_student SET sname='judy' WHERE sno =1
得到結果: 被卡住了, 無法得到結果
爲什麼會出現被卡住呢? 原因是因爲當A去開啓事物並且去執行查詢事件, 這個時候B去執行更新操作,那麼這個時候會被卡住,原因是因爲A的select操作被上了鎖, 所以B窗口的update操作是無法執行的, 所以就解決了我們上述所有問題,簡單粗暴~
悲觀鎖
悲觀鎖分爲共享鎖和排它鎖
什麼是共享鎖
共享鎖的意思是多個事物對一行數據操作的時候, 可以共享一把鎖,都可以訪問到數據,但是隻能讀不能更改,寫.
排他鎖的意思是指當有一行數據是排他鎖的時候, 其他事物就不能再獲取該鎖的其他鎖,包括共享鎖和排它鎖,
A 窗口 ,(FOR UPDATE指的是悲觀鎖)
START TRANSACTION;
SELECT * FROM t_student WHERE sno ='1' FOR UPDATE
B 窗口
START TRANSACTION;
SELECT * FROM t_student WHERE sno ='1' FOR UPDATE
執行結果:
被卡住了
樂觀鎖
依靠數據本身來保證數據的正確性
樂觀鎖的兩種方式
1 使用版本號
2 使用時間戳
A 窗口
SELECT * FROM t_student WHERE sno='1'
UPDATE t_student SET sname='judy',version=version+1 WHERE sno='1'
B窗口
SELECT * FROM t_student WHERE sno='1'
UPDATE t_student SET sname='judy',version=version+1 WHERE sno='1' AND version =1