ACID:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)
原子性:是指事務(wù)包含的所有操作要么全部成功,要么全部失敗回滾,因此事務(wù)的操作如果成功就必須要完全應(yīng)用到數(shù)據(jù)庫,如果操作失敗則不能對數(shù)據(jù)庫有任何影響。
一致性:是指事務(wù)必須使數(shù)據(jù)庫從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài),也就是說一個(gè)事務(wù)執(zhí)行之前和執(zhí)行之后都必須處于一致性狀態(tài)。
隔離性:是當(dāng)多個(gè)用戶并發(fā)訪問數(shù)據(jù)庫時(shí),比如操作同一張表時(shí),數(shù)據(jù)庫為每一個(gè)用戶開啟的事務(wù),不能被其他事務(wù)的操作所干擾,多個(gè)并發(fā)事務(wù)之間要相互隔離。
持久性:是指一個(gè)事務(wù)一旦被提交了,那么對數(shù)據(jù)庫中的數(shù)據(jù)的改變就是永久性的,即便是在數(shù)據(jù)庫系統(tǒng)遇到故障的情況下也不會(huì)丟失提交事務(wù)的操作。
個(gè)人理解據(jù)庫事務(wù)主要做了兩件事情:
一是保證一致性結(jié)果,能在發(fā)生異常的時(shí)候快速恢復(fù),也就是回滾
二是并發(fā)訪問的時(shí)候提供隔離
| 隔離級別 | 臟讀 | 不可重復(fù)讀 | 幻讀 |
|---|---|---|---|
| 未提交讀Read Uncommitted | 是 | 是 | 是 |
| 已提交讀Read Committed | 否 | 是 | 是 |
| 可重復(fù)讀Repeatable Read | 否 | 否 | 是 |
| 可串行化Serializable | 否 | 否 | 否 |
讀未提交:就是一個(gè)事務(wù)可以讀取另一個(gè)未提交事務(wù)的數(shù)據(jù)。
讀提交:就是一個(gè)事務(wù)要等另一個(gè)事務(wù)提交后才能讀取數(shù)據(jù)。若有事務(wù)對數(shù)據(jù)進(jìn)行更新(UPDATE)操作時(shí),讀操作事務(wù)要等待這個(gè)更新操作事務(wù)提交后才能讀取數(shù)據(jù),可以解決臟讀問題。
重復(fù)讀:就是在開始讀取數(shù)據(jù)(事務(wù)開啟)時(shí),不再允許修改操作。重復(fù)讀可以解決不可重復(fù)讀問題。寫到這里,應(yīng)該明白的一點(diǎn)就是,不可重復(fù)讀對應(yīng)的是修改,即UPDATE操作。但是可能還會(huì)有幻讀問題。因?yàn)榛米x問題對應(yīng)的是插入INSERT操作,而不是UPDATE操作。
Serializable:是最高的事務(wù)隔離級別,在該級別下,事務(wù)串行化順序執(zhí)行,可以避免臟讀、不可重復(fù)讀與幻讀。但是這種事務(wù)隔離級別效率低下,比較耗數(shù)據(jù)庫性能,一般不使用。
mysql 對應(yīng)的InnoDB默認(rèn)隔離級別是 重復(fù)讀
明細(xì)可以參考:https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
隔離怎么實(shí)現(xiàn)?
隔離的實(shí)現(xiàn)也是依賴加鎖機(jī)制, InnoDB存在兩種鎖:
共享鎖(S鎖) :(插入/修改/刪除)資源獲取S鎖之后,能加S鎖,不能加X鎖
排它鎖(X鎖) : 資源加上X鎖之后,不能加S鎖,也不能加X鎖
S/X鎖兼容表
| 鎖定類型 | 讀鎖 | 寫鎖 |
|---|---|---|
| 讀鎖 | Compatible | Conflict |
| 寫鎖 | Conflict | Conflict |
具體的四種隔離級別鎖實(shí)現(xiàn):
| 隔離級別 | 操作 | 鎖 | 生命周期 |
|---|---|---|---|
| 讀未提交 | 讀 | 否 | |
| 讀未提交 | 寫 | 行級排它鎖 | 立即釋放 |
| 讀已提交 | 讀 | 行級共享鎖 | 立即釋放 |
| 讀已提交 | 寫 | 行級排它鎖 | 事務(wù)結(jié)束 |
| 可重復(fù)讀 | 讀 | 行級共享鎖 | 事務(wù)結(jié)束 |
| 可重復(fù)讀 | 寫 | 行級排它鎖 | 事務(wù)結(jié)束 |
| 可串行化 | 讀 | 范圍鎖/表級別鎖 | 事務(wù)結(jié)束 |
| 可串行化 | 寫 | 行級排它鎖 | 事務(wù)結(jié)束 |
讀未提交
為了解決丟失更新問題,需要對寫操作加 X 鎖
讀已提交
為了保證讀已提交,讀操作加上 S 鎖,這樣如果其他事務(wù)有正在寫的操作,必須等待寫操作提交之后才能讀,因?yàn)?S 和 X 互斥,如果在讀的過程中其他事務(wù)想寫,也必須等事務(wù)讀完之后才可以。這里的 S 鎖是一個(gè)臨時(shí) S 鎖,表示事務(wù)讀完之后立即釋放該鎖,可以讓其他事務(wù)繼續(xù)寫,如果事務(wù)再讀的話,就可能讀到不一樣的記錄,這就是 不可重復(fù)讀 了。
可重復(fù)讀
為了讓事務(wù)可以重復(fù)讀,加在讀操作的 S 鎖變成了持續(xù) S 鎖,也就是直到事務(wù)結(jié)束時(shí)才釋放該鎖,這可以保證整個(gè)事務(wù)過程中,其他事務(wù)無法進(jìn)行寫操作,所以每次讀出來的記錄是一樣的。
可串行化
序列化隔離級別下單純的使用行鎖已經(jīng)實(shí)現(xiàn)不了,因?yàn)樾墟i不能阻止其他事務(wù)的插入操作,這就會(huì)導(dǎo)致幻讀問題,這種情況下,我們可以把鎖加到表上或者使用范圍鎖(間隙鎖)。
通過對鎖的類型(讀鎖還是寫鎖),鎖的粒度(行鎖還是表鎖),持有鎖的時(shí)間(臨時(shí)鎖還是持續(xù)鎖)合理的進(jìn)行組合,就可以實(shí)現(xiàn)四種不同的隔離級別.這四種不同的加鎖策略實(shí)際上又稱為 封鎖協(xié)議(Locking Protocol)
Spring事務(wù)隔離級別
- ISOLATION_DEFAULT這是一個(gè)PlatfromTransactionManager默認(rèn)的隔離級別,使用數(shù)據(jù)庫默認(rèn)的事務(wù)隔離級別。另外四個(gè)與JDBC的隔離級別相對應(yīng) 。默認(rèn)采用的是重復(fù)讀,除了insert幻讀外基本的場景都能cover
- ISOLATION_READ_UNCOMMITTED 這是事務(wù)最低的隔離級別,它充許別外一個(gè)事務(wù)可以看到這個(gè)事務(wù)未提交的數(shù)據(jù)。這種隔離級別會(huì)產(chǎn)生臟讀,不可重復(fù)讀和幻像讀
- ISOLATION_READ_COMMITTED 保證一個(gè)事務(wù)修改的數(shù)據(jù)提交后才能被另外一個(gè)事務(wù)讀取。另外一個(gè)事務(wù)不能讀取該事務(wù)未提交的數(shù)據(jù)。這種事務(wù)隔離級別可以避免臟讀出現(xiàn),但是可能會(huì)出現(xiàn)不可重復(fù)讀和幻像讀。
- ISOLATION_REPEATABLE_READ 這種事務(wù)隔離級別可以防止臟讀,不可重復(fù)讀。但是可能出現(xiàn)幻像讀。它除了保證一個(gè)事務(wù)不能讀取另一個(gè)事務(wù)未提交的數(shù)據(jù)外,還保證了避免下面的情況產(chǎn)生(不可重復(fù)讀)。
- ISOLATION_SERIALIZABLE 這是花費(fèi)最高代價(jià)但是最可靠的事務(wù)隔離級別。事務(wù)被處理為順序執(zhí)行。除了防止臟讀,不可重復(fù)讀外,還避免了幻像讀。
Spring事務(wù)傳播級別
- PROPAGATION_REQUIRED 如果存在一個(gè)事務(wù),則支持當(dāng)前事務(wù)。如果沒有事務(wù)則開啟一個(gè)新的事務(wù)。
- PROPAGATION_SUPPORTS 如果存在一個(gè)事務(wù),支持當(dāng)前事務(wù)。如果沒有事務(wù),則非事務(wù)的執(zhí)行。但是對于事務(wù)同步的事務(wù)管理器,PROPAGATION_SUPPORTS與不使用事務(wù)有少許不同。
- PROPAGATION_MANDATORY 如果已經(jīng)存在一個(gè)事務(wù),支持當(dāng)前事務(wù)。如果沒有一個(gè)活動(dòng)的事務(wù),則拋出異常。
- PROPAGATION_REQUIRES_NEW 總是開啟一個(gè)新的事務(wù)。如果一個(gè)事務(wù)已經(jīng)存在,則將這個(gè)存在的事務(wù)掛起。當(dāng)數(shù)據(jù)庫分庫分表存在跨庫的時(shí)候需要啟用
- PROPAGATION_NOT_SUPPORTED 總是非事務(wù)地執(zhí)行,并掛起任何存在的事務(wù)。
- PROPAGATION_NEVER 總是非事務(wù)地執(zhí)行,如果存在一個(gè)活動(dòng)事務(wù),則拋出異常。
-
PROPAGATION_NESTED 如果一個(gè)活動(dòng)的事務(wù)存在,則運(yùn)行在一個(gè)嵌套的事務(wù)中. 如果沒有活動(dòng)事務(wù), 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執(zhí)行。
demo如下
<!--事務(wù)模板 -->
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager">
<ref local="transactionManager" />
</property>
<!--ISOLATION_DEFAULT 表示由使用的數(shù)據(jù)庫決定 -->
<property name="isolationLevelName" value="ISOLATION_DEFAULT"/>
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
</bean>