先說概念性的東西
事務的四種隔離級別:
01:Read uncommitted(讀未提交):最低級別,任何情況都會發生。
02:Read Committed(讀已提交):可避免髒讀的發生。
03:Repeatable read(可重複讀):可避免髒讀、不可重複讀的發生。一般數據庫默認級別
04:Serializable(串行化):避免髒讀、不可重複讀,幻讀的發生。
髒讀:事務A讀取到了事物B更新的數據,然後B回滾
不可重複讀:事務A多次讀取同一數據,事務B在A讀取過程中修改了並提交,導致A多次讀取同一數據結果不一樣
幻讀:A操作數據庫的過程中B插入了一條記錄,當A執行完發現有一條記錄沒有處理,就像發生了幻覺。
不可重複讀側重於修改,幻讀側重於新增和刪除。
實驗準備數據:
CREATE TABLE `yunfei` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` int(11) DEFAULT NULL,
`value` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_type` (`type`)
) ENGINE=InnoDB AUTO_INCREMENT DEFAULT CHARSET=utf8;
INSERT INTO `yunfei` (`id`, `type`, `value`) VALUES ('1', '1', 'aa');
INSERT INTO `yunfei` (`id`, `type`, `value`) VALUES ('2', '2', 'bb');
INSERT INTO `yunfei` (`id`, `type`, `value`) VALUES ('3', '3', 'cc');
INSERT INTO `yunfei` (`id`, `type`, `value`) VALUES ('4', '4', 'dd');
INSERT INTO `yunfei` (`id`, `type`, `value`) VALUES ('5', '7', 'ee');
INSERT INTO `yunfei` (`id`, `type`, `value`) VALUES ('6', '10', 'ff');
實驗A:
sessionA sessionB
begin; begin;
select * from yunfei;
id type value
1 1 aa
2 2 bb
3 3 cc
4 4 dd
5 7 ee
6 10 ff
insert into yunfei(type,value) values ('9','ddddddd');
commit;
select * from yunfei;
id type value
1 1 aa
2 2 bb
3 3 cc
4 4 dd
5 7 ee
6 10 ff
insert into yunfei(type,value) values ('9','ddddddd');
[SQL]
[Err] 1062 - Duplicate entry '9' for key 'aa'
select * from yunfei;
id type value
1 1 aa
2 2 bb
3 3 cc
4 4 dd
5 7 ee
6 10 ff
update yunfei set value='ddddddd' where type=9;
[SQL]
Affected rows: 0
Time: 0.000s
select * from yunfei;
id type value
1 1 aa
2 2 bb
3 3 cc
4 4 dd
5 7 ee
6 10 ff
34 9 ddddddd
實驗B
sessionA sessionB
begin; begin
select * from yunfei for update;
id type value
1 1 aa
2 2 bb
3 3 cc
4 4 dd
5 7 ee
6 10 ff
insert into yunfei(type,value) values ('9','ddddddd');
無法插入!會一直等待,直到session1 commit或rollback
insert into yunfei(type,value) values ('11','ddddddd');
無法插入!會一直等待,直到session1 commit或rollback
rollback;
begin;
select * from yunfei where 4<type<10 for update;
id type value
1 1 aa
2 2 bb
3 3 cc
4 4 dd
5 7 ee
6 10 ff
insert into yunfei(type,value) values ('9','ddddddd');
無法插入!會一直等待,直到session1 commit或rollback
insert into yunfei(type,value) values ('11','ddddddd');
[SQL]
Affected rows: 1
Time: 0.003s
結論(自己實驗總結的,有錯誤請留言指正):
mysql innodb只在一定程度上避免了一些幻讀,但明沒有真正解決幻讀。
快照讀(普通select):
1:一個session永遠讀不到另外一個session提交的數據,避免了幻讀
2:一個session在執行過程中另外一個session插入了一條記錄並提交,那麼在當前session重複插入的時候唯一索引衝突,明明沒數據爲什麼衝突了?再次查詢並無多數據,但是衝突提醒變相的出現了幻讀。
3:一個session在執行過程中另外一個session插入了一條記錄並提交,那麼在當前session中修改另外一個session插入的數據提示會無數據受影響,但是再次查詢多了條數據?出現幻讀。
當前讀(select for update):
1·:一個session在當前讀過程中沒有用到索引,其他session無法插入數據,update鎖了全表,避免幻讀。
2::一個session在當前讀過程中用到了範圍索引,那麼其他session也會因爲行鎖(臨鍵鎖或者間隙鎖)的情況無法插入,避免幻讀。
mysql鎖的幾個實現原理:
意象鎖:
當行鎖存在時,表鎖無法拿到。意象鎖是一個標誌,當標誌位TRUE時表示已經有人拿到了意象鎖,在這個時候就無法鎖表了
臨鍵鎖(行鎖默認算法):
當type=range使用範圍查詢索引update時候有數據命中,InnoDB會鎖上Btree當前區間和下一個區間。
1、4、7、10 一共分爲5個區間,當範圍查詢>5且<9的時候理論上只有7會上鎖,實際上會鎖(4,7]和(7,10]兩個區間
間隙鎖:
臨鍵鎖查詢記錄不存在的時候,會退化成間隙鎖,鎖到了一個閉區間,只存在RR隔離級別
1、4、7、10 一共分爲5個區間,當範圍查詢>4且<6的時候沒有索引命中,這個時候會鎖上(4,7)區間。
記錄鎖:
當type=eq-ref 唯一性(主鍵、唯一)索引,條件爲精確匹配,退化成記錄鎖