事務(wù)的隔離級別
SQL標(biāo)準(zhǔn)定義的4個隔離級別為:
-
READ UNCOMMITED(未提交讀)
事務(wù)中的修改缸剪,即使沒有提交吗铐,對其他事務(wù)也都是可見的。事務(wù)可以讀取未提交的數(shù)據(jù)杏节,也被稱為臟讀抓歼。這個級別是隔離級別中最低的,實際情況基本不用拢锹。 -
READ COMMITED(提交讀)
事務(wù)從開始直到提交之前谣妻,所做的任何修改對其他事務(wù)都是不可見的。這個級別有時候也叫做不可重復(fù)讀卒稳,因為兩次執(zhí)行同樣的查詢蹋半,可能會得到不一樣的結(jié)果(兩次同樣的查詢之間可能會有其他事務(wù)提交修改)。阿里云MySQL的默認(rèn)事務(wù)隔離級別
-
REPEATABLE READ(可重復(fù)讀)
該事務(wù)界別解決了臟讀的問題充坑,也保證了在同一個事務(wù)中多次讀取同樣記錄的結(jié)果是一致的(通過數(shù)據(jù)快照)减江。標(biāo)準(zhǔn)MySQL的默認(rèn)事務(wù)隔離界別
但是理論上,可重復(fù)讀隔離級別還是無法解決另外一個幻讀的問題捻爷。所謂幻讀辈灼,指的是當(dāng)前某個事務(wù)在讀取某個范圍內(nèi)的記錄時,另外一個事務(wù)又在該范圍內(nèi)插入新的記錄也榄,當(dāng)之前的事務(wù)再次讀取該范圍的記錄時巡莹,會產(chǎn)生幻行(會讀取出新增的數(shù)據(jù)行)司志。
MySQL InnoDB存儲引擎通過多版本兵法控制(MVCC)解決了幻讀的問題〗嫡基本原理就是通過在每行記錄后面保存兩個隱藏的列來實現(xiàn)骂远,一個保存行的創(chuàng)建時間,另一個保存行的過期時間(或刪除時間)腰根。當(dāng)然存儲的不是實際時間值激才,而是系統(tǒng)版本號。每開始一個事務(wù)额嘿,系統(tǒng)版本號就會自動遞增瘸恼。事務(wù)開始時刻的系統(tǒng)版本號會做為事務(wù)的版本號,用來和查詢到的每行記錄的版本號進(jìn)行比較册养。具體詳情可參看《高性能MySQL》的1.4多版本并發(fā)控制章節(jié)钞脂。 -
SERIALIZABLE(可串行化)
最高的事務(wù)隔離級別,通過強(qiáng)制事務(wù)串行執(zhí)行捕儒,避免了前面幻讀的問題冰啃。簡單來說,該事務(wù)會在讀取的每一行數(shù)據(jù)上都加鎖刘莹,所以可能導(dǎo)致大量的超時和鎖爭用的問題阎毅。實際應(yīng)用中很少用到這個隔離界別。
MySQL中的事務(wù)
MySQL默認(rèn)采用自動提交(AUTOCOMMIT)模式点弯。也就是說扇调,如果不是顯示地開始一個事務(wù),則每個查詢都被當(dāng)作一個事務(wù)執(zhí)行提交操作抢肛。
MySQL鎖
MySQL的鎖分為共享鎖(shared lock)和排他鎖(exclusive lock)狼钮,也叫讀鎖(read lock)和寫鎖(write lock)。讀鎖是共享的捡絮,或者說是排他的熬芜,也就是說一個寫鎖會阻塞其他的寫鎖和讀鎖,這是出于安全策略的考慮福稳,只有這樣涎拉,才能確保在給定的時間里,只有一個用戶能執(zhí)行寫入的圆,并防止其他用戶讀取正在寫入的同一資源鼓拧。
InnoDB在事務(wù)執(zhí)行過程中,會自動加鎖越妈,并在執(zhí)行COMMIT或ROLLBACK的時候釋放季俩,所有的鎖都是在同一時刻釋放的。這里描述的鎖定都是隱式鎖定梅掠,InnoDB除了隱式的鎖定還支持通過特定的語句來進(jìn)行顯示鎖定酌住。
- 增加共享鎖:SELECT ... LOCK IN SHARE MODE
- 增加排他鎖:SELECT ... FOR UPDATE
MySQL事務(wù)與鎖的應(yīng)用
現(xiàn)在有個需求要實現(xiàn)一個圖片分發(fā)的功能店归,一張圖片只能分發(fā)給兩個人,如何實現(xiàn)才能不出現(xiàn)圖片超發(fā)的情況赂韵?
在不考慮性能的前提下我們可以通過MySQL事務(wù)和顯示鎖來解決這個問題:
- 開始事務(wù),START TRANSACTION
- 使用排他鎖方式挠蛉,SELECT 圖片分發(fā)次數(shù)<2
- 簡單業(yè)務(wù)記錄
- 更新圖片分發(fā)次數(shù)
- 結(jié)束事務(wù)祭示,COMMIT
使用事務(wù)+顯示鎖的方式,可以使操作同一行的所有事務(wù)串行化處理谴古,從而解決并發(fā)寫的問題质涛。
最后考慮到更高的性能處理,該問題還可以使用Redis事務(wù)的方式解決(WATCH+MULTI+EXEC)掰担,具體詳情大家可以谷歌下汇陆。