1、事務的四大特性(ACID)
1.1、原子性(Atomicity)
原子性是指事務包含的一系列操作要麼全部成功,要麼全部回滾,不存在部分成功或者部分回滾,是一個不可分割的操作整體(這個側重點是事務執行的完整)。
利用InnoDB的undo log(回滾日誌)記錄需要回滾的日誌信息,是實現原子性的關鍵,當事務回滾時能夠撤銷所有已經成功執行的sql語句
例如:
delete一條數據的時候,就會記錄這條數據的曾經的信息,回滾的時候,insert這條舊數據
update一條數據的時候,就會記錄之前的舊值,回滾的時候,根據舊值執行update操作
insert一條數據的時候,就會這條記錄的主鍵,回滾的時候,根據主鍵執行delete操作
undo log記錄了這些回滾需要的信息,當事務執行失敗或調用了rollback,導致事務需要回滾,便可以利用undo log中的信息將數據回滾到修改之前的樣子。
1.2、一致性(Consistency)
數據庫總是從一種一致性狀態轉換到另一種一致性狀態,有非法數據時,事務回滾,任何事務處理過程中所做的數據改變,也不會影響到數據庫的內容。
一致性是要保證操作前和操作後數據或者數據結構的一致性,關注數據的中間狀態,也就是一致性需要監視中間狀態的數據,如果有變化,即刻回滾。
一致性是基礎,也是最終目的,其他三個特性(原子性、隔離性和持久性)都是爲了保證一致性的。
1.3、隔離性(Isolation)
隔離性是指當多個用戶併發操作數據庫,比如操作同一張表,數據庫爲每一個用戶開啓的事務,不能被其他的事務所幹擾或者影響,事務之間是彼此獨立的,如果有變化,即刻回滾(隔離性是多個事務的時候,事務之間感知不到彼此的存在,就好像只存在本身一個事務一樣, 相互不能干擾)。
1.4、永久性(Durability)
永久性是指一個事務一旦提交了,那麼對數據庫中數據的改變就是永久的,即使是在數據庫發生故障時,也不會丟失事務提交的數據。
PS:一致性和原子性的區別
原子性和一致性的的側重點不同:
原子性關注狀態,要麼全部成功,要麼全部失敗,不存在部分成功的狀態。
而一致性關注數據的可見性,中間狀態的數據對外部不可見,只有最初狀態和最終狀態的數據對外可見。
2、InnoDB如何保證這些事務特性的
- redo log重做日誌用來保證事務的持久性
- undo log回滾日誌保證事務的原子性
- undo log+redo log保證事務的一致性
- 鎖(共享、排他)用來保證事務的隔離性
2.1、重做日誌 redo log
重做日誌 redo log 分爲兩部分:一部分是內存中的重做日誌緩衝(redo log buffer),是易丟失的;二部分是重做日誌文件(redo log file),是持久的。InnoDB通過Force Log at Commit機制來實現持久性,當commit時,必須先將事務的所有日誌寫到重做日誌文件進行持久化,待commit操作完成纔算完成。
InnoDB在下面情況下會將重做日誌緩衝的內容寫入重做日誌文件:
- master thread 每一秒將重做日誌緩衝刷新到重做日誌文件;
- 每個事務提交時
- 當重做日誌緩衝池剩餘空間小於1/2時
爲了確保每次日誌都寫入重做日誌文件,在每次將日誌緩衝寫入重做日誌文件後,InnoDB存儲引擎都需要調用一次fsync(刷盤)操作。但這也不是絕對的。用戶可以通過修改
innodb_flush_log_at_trx_commoit參數來控制重做日誌刷新到磁盤的策略,這個可以作爲大量事務提交時的優化點。
- 1參數默認值,表示事務提交時必須調用一次fsync操作。
- 0表示事務提交時,重做日誌緩存並不立即寫入重做日誌文件,而是隨着Master Thread的間隔進行fsync操作。
- 2表示事務提交時將重做日誌寫入重做日誌文件,但僅寫入文件系統的緩存中,不進行fsync操作。
- fsync的效率取決於磁盤的性能,因此磁盤的性能決定了事務提交的性能,也就是數據庫的性能。所以如果有人問你如何優化Mysql數據庫的時候別忘了有硬件這一條,讓他們提升硬盤配置,換SSD固態硬盤
- 重做日誌都是以512字節進行存儲的,稱之爲重做日誌塊,與磁盤扇區大小一致,這意味着重做日誌的寫入可以保證原子性,不需要doublewrite技術。它有以下3個特性:
1、重做日誌是在InnoDB層產生的
2、重做日誌是物理格式日誌,記錄的是對每個頁的修改
3、重做日誌在事務進行中不斷被寫入,而且是順序寫入
2.2、回滾日誌 undo log
爲了滿足事務的原子性,在操作任何數據之前,首先將數據備份到一個地方(這個存儲數據備份的地方稱爲Undo Log),然後進行數據的修改。如果出現了錯誤或者用戶執行了 ROLLBACK語句,系統可以利用Undo Log中的備份將數據恢復到事務開始之前的狀態。
undo log實現多版本併發控制(MVCC)來輔助保證事務的隔離性。
回滾日誌不同於重做日誌,它是邏輯日誌,對數據庫的修改都邏輯的取消了。當事務回滾時,它實際上做的是與先前相反的工作。對於每個INSERT,InnoDB存儲引擎都會完成一個DELETE;對於每個UPDATE,InnoDB存儲引擎都會執行一個相反的UPDATE。
事務提交後並不能馬上刪除undo log,這是因爲可能還有其他事務需要通過undo log 來得到行記錄之前的版本。故事務提交時將undo log 放入一個鏈表中,是否可以刪除undo log 根據操作不同分以下2種情況:
- Insert undo log: insert操作的記錄,只對事務本身可見,對其他事務不可見(這是事務隔離性的要求),故該undo log可以在事務提交後直接刪除。不需要進行 purge操作。
- update undo log:記錄的是對 delete和 update操作產生的 undo log。該undo log可能需要提供MVCC機制,因此不能在事務提交時就進行刪除。提交時放入undo log鏈表,等待 purge線程進行最後的刪除。
2.3、鎖
事務的隔離性的實現原理就是鎖,因而隔離性也可以稱爲併發控制、鎖等。事務的隔離性要求每個讀寫事務的對象對其他事務的操作對象能互相分離。再者,比如操作緩衝池中的LRU列表,刪除,添加、移動LRU列表中的元素,爲了保證一致性那麼就要鎖的介入。
鎖的類型
InnoDB主要有2種鎖:行級鎖,意向鎖
行級鎖:
- 共享鎖(讀鎖 S),允許事務讀一行數據。事務拿到某一行記錄的共享S鎖,纔可以讀取這一行,並阻止別的事務對其添加X鎖。共享鎖的目的是提高讀讀併發。
- 排它鎖(寫鎖 X),允許事務刪除一行數據或者更新一行數據。事務拿到某一行記錄的排它X鎖,纔可以修改或者刪除這一行。排他鎖的目的是爲了保證數據的一致性。
行級鎖中,除了S和S兼容,其他都不兼容。
意向鎖:
- 意向共享鎖(讀鎖 IS ),事務想要獲取一張表的幾行數據的共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
- 意向排他鎖(寫鎖 IX),事務想要獲取一張表中幾行數據的排它鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。
3、事務的隔離性
事務的隔離性。當多個線程開啓事務操作數據庫中的數據時,數據庫要能進行隔離操作,以保證各個線程獲取數據的準確性; 如果不考慮事務的隔離性,會發生以下幾個問題;
3.1、髒讀
髒讀是指一個事務在處理過程中讀取了另一個事務未提交的數據。比如,A向B轉賬
update account set money = money + 100 where name = 'B';
update account set money = money - 100 where name = 'A'
當只執行第一條SQL時,A通知B查看賬戶,B發現確實錢已到賬(此時即發生了髒讀),
而之後無論第二條SQL是否執行,只要該事務不提交,則所有操作都將回滾,那麼當B以後再次查看賬戶時就會發現錢其實並沒有轉
3.2、不可重複讀
- 不可重複讀是指在對於數據庫中的某個數據,一個事務範圍內多次查詢卻返回了不同的數據值,這是由於在查詢間隔,被另一個事務修改並提交了。
- 例如事務T1在讀取某一數據,而事務T2立馬修改了這個數據並且提交事務給數據庫,事務T1再次讀取該數據就得到了不同的結果,發生了不可重複讀。
- 不可重複讀和髒讀的區別是,髒讀是某一事務讀取了另一個事務未提交的髒數據,而不可重複讀則是讀取了前一事務提交的數據。
- 在某些情況下,不可重複讀並不是問題,比如我們多次查詢某個數據當然以最後查詢得到的結果爲主。但在另一些情況下就有可能發生問題,例如對於同一個數據A和B依次查詢就可能不同,A和B就可能打起來了……
3.3、幻讀
幻讀是事務非獨立執行時發生的一種現象。
例如事務T1對一個表中所有的行的某個數據項做了從“1”修改爲“2”的操作,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值還是爲“1”並且提交給數據庫。而操作事務T1的用戶如果再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀。
幻讀和不可重複讀都是讀取了另一條已經提交的事務(這點就髒讀不同),所不同的是不可重複讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)
4、MySQL的隔離級別
隔離級別的概念,就是爲了解決髒讀、幻讀和不可重複讀的問題(這些問題,其實就是導致無法保證一致性的幾種情況)
4.1、未提交讀(read uncommited)
在這個隔離級別中,在一個事務執行的操作就算不提交也能被其他的事務看到。在這個級別中一個事務可能讀到其他事務還沒提交的髒數據,即可能出現髒讀。
4.2、讀已提交(Read committed )
在一個事務提交之後,其他事務纔可以看到事務的修改。此隔離級別可能會出現同一個事務中執行相同的查詢卻讀到不同的數據, 即不可重複讀(
nonrepeatable read
),另未提交讀也可能出現不可重複讀。
4.3、可重複讀(repeatable read)
這是
MySQL
的默認隔離級別,在事務開始的時候會保存此刻的一個快照(這裏囉嗦一下,實際上是開啓事務後執行第一條語句的時 候準備的快照,準備快照的方法則是記錄當前事務的版本號,沒有進行數據的複製,不明白事務版本號或隱藏字段的可以看 看MySQL
的MVCC
),然後接下來這個事務的所有數據讀取都是從這個快照讀,所以不會出現不可重複讀的情況,但是還是有可能 出現幻讀。
4.4、可串行化(serializable)
意思就是事務要一個一個來,如果在一個事務中進行讀操作,那麼其他事務在該事務完成前只能進行讀操作;如果進行寫操作,那麼其他事務的操作都進入等待(直到當前事務提交)。這種級別就可以防範目前出現的髒讀、不可重複讀、幻讀等現象。
文章參考自:
https://www.cnblogs.com/lmj612/p/10579475.html
https://www.cnblogs.com/zhangweicheng/p/12273797.html