數(shù)據(jù)庫事務(wù)實(shí)現(xiàn)原理

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>  

           


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容