這幾天面試多次被問到了數據庫事務機制、隔離級別、樂觀鎖悲觀鎖類的問題,之前對這些只能說有所了解,有些概念還停留在記憶層面,沒有理解,所以回答的不好。后面翻書學習了下,理解了一些東西,在此做一個記錄。
?
什么是事務?
事務我理解的是一個完整的業務行為,一個業務行為可能包含多個動作,這個完整的動作就構成一個事務。比較經典的例子是銀行轉賬,A賬戶轉到B賬戶,需要兩個動作:A賬戶減,B賬戶加,必須保證這兩個動作要么都做,要么都不做。
事務具有ACID特征,具體包括:
●?原子性(atomicity):原子性是說事務的不可分割,要么全成功,要么全失敗,不可部分成功,部分失敗。半途失敗的情況下,需要打掃戰場,也就是數據回滾。
●?一致性(consistency):一致性是說事務的最后結果,要保證數據上沒有異常。一致性是強調結果,是建立在原子性上實現的,也就是說能保證原子性,那就會有一致性的結果。
●?隔離性(isolation):隔離性是說事務沒有提交前對其他事務是不可見的,事務間數據是隔離的(當然不同級別隔離程度不一樣)。
●?持久性(durability): 事務提交后會持久化,可以長久保存。
?
事務隔離級別
了解事務的隔離級別之前,需要明白數據讀取的幾個概念:
●?臟讀:就是讀到了別人還沒提交的數據。
●?可重復讀:就是同一個事物內的兩次查詢,中間如果別人修改了查詢內的記錄且提交了,對第二次查詢是不可見的,不會出現同條記錄兩次查詢不一致問題。
●?幻讀:就是一個事物內的兩次查詢,中間如果別人增加了記錄并且提交了,第二次查詢能查詢到的,會出現和第一次記錄不一致的現象。
?
事務的控制分很多個級別,級別的高低決定隔離的程度,mysql中分四個級別:
●?讀未提交:這種級別是最低的,A事務的修改沒有提交對B事物是可見的,會出現數據的臟讀,一般情況下不會用到此種類型。
●?讀已提交:A事物的修改提交后才對B可見,這種情況會出現數據的幻讀的問題,兩次查詢的結果不一樣。
●?可重復讀:是MySQL默認的級別,這種級別事物內的兩次查詢,中間其他修改了某條記錄,對其他事務是不可見的,保證了重復查的情況下同條記錄的一致性,但是對于新增的情況其他事務是可見的,所以還是會出現新增幻讀的現象。
●?可串行化:事務之間是串行執行的,對查詢到的每條記錄都加鎖,會出現阻塞的情況,并發情況下會造成嚴重的性能問題,所以一般也不會用到這種類型。
隔離級別一覽圖
?
事務的隔離實現
事務當中的隔離是通過兩種方式控制:一種是鎖的方式,通過時間上的挫開達到隔離;另一種是版本控制的方式,記錄多個版本達到隔離。
?
1、鎖
MySQL當中的鎖分讀鎖和寫鎖,讀鎖因為是讀取數據所以可以多個同時讀取同一份數據,具有共享性質;寫鎖涉及到數據的變動,所以和其他寫鎖和讀鎖是相沖突的,具有排他性質。
從鎖的粒度上分表級鎖和行級鎖,表鎖一般發生在對表結構的修改或對全表更新的時候,會阻塞所有對這張表的讀寫操作;行級鎖一般發生在指定記錄更新的時候,只會鎖定指定記錄。鎖的粒度越小并發度越高,能優先行級鎖盡量不要表鎖,和程序中鎖的粒度是一樣的原則。
?
2、多版本并發控制
MySQL為了性能考慮除了行級鎖以外還是另外一種方式,多版本并發控制,這中控制是由存儲引擎實現。
書中說明了InnoDB一種簡單的實現方式,這種方式是采用一條記錄多個版本的方式,每條記錄上增加了兩個隱藏列,一個是創建版本號,一個是刪除版本號,每開啟一個事務都會分配一個事務版本號,事務版本號是遞增的,事務內操作都會根據這個版本號比較。具體如下:
●?查詢時:查詢當前事務之前存在的記錄和本事務創建的記錄,且沒有被刪除的,即:創建版本號 當前版本號)
●?插入時:記錄的創建版本號為當前事務版本號。
●?刪除時:更新記錄的刪除版本號為當前事務版本號。
●?更新時:插入一條新記錄,創建版本號為當前事務版本號,同時把原記錄刪除版本號改為當前事務版本號,代表已經刪除。實際上這里的更新相當于刪除再加一條記錄。
?
3、樂觀鎖和悲觀鎖
鎖從使用的角度又分悲觀鎖和樂觀鎖,悲觀鎖是持有很悲觀的態度,認為我查到的數據都有可能被別人修改,所以查詢的時候就把這一批數據鎖起來,不讓別人操作;樂觀鎖是持有很樂觀的態度,認為我查到的數據基本不可能被別人修改,所以查詢的時候不鎖住這批數據,修改提交的時候再確認有沒有被別人修改,有種亡羊補牢,為時不晚的意思。
?
樂觀鎖和悲觀鎖的實現:
●?悲觀鎖可以在數據庫層面很簡單的解決,利用select … for update,在查詢的時候就鎖定這部分數據。
●?樂觀鎖的實現較悲觀鎖復雜,可以在數據庫在一個版本號的列,更新的時候版本號都+1,以此來確認我查出來的數據后面有沒有別人修改,已修改的不更新或程序拋一個異常。
?
使用樂觀鎖還是悲觀鎖:
從性能的角度考慮樂觀鎖性能更好,在查詢到更新這段時間沒有鎖定操作,但是實現起來沒有悲觀鎖簡單,可能出錯。所以要考慮的因素是系統的并發高不高?出現沖突的概率有多大?并發高的情況下選用樂觀鎖更好,反之選用悲觀鎖這種簡單的方式更好。