數據庫事務的四大特性以及事務的隔離級別

數據庫事務的四大特性以及事務的隔離級別

本篇講述數據庫中事務的四大特性(ACID),而且將會詳細地說明事務的隔離級別。spring

若是一個數據庫聲稱支持事務的操做,那麼該數據庫必需要具有如下四個特性:數據庫

⑴ 原子性(Atomicity)性能優化

原子性是指事務包含的全部操做要麼所有成功,要麼所有失敗回滾,這和前面兩篇博客介紹事務的功能是同樣的概念,所以事務的操做若是成功就必需要徹底應用到數據庫,若是操做失敗則不能對數據庫有任何影響。微信

⑵ 一致性(Consistency)session

一致性是指事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態,也就是說一個事務執行以前和執行以後都必須處於一致性狀態。多線程

拿轉帳來講,假設用戶 A 和用戶 B 二者的錢加起來一共是 5000,那麼無論 A 和 B 之間如何轉帳,轉幾回帳,事務結束後兩個用戶的錢相加起來應該還得是 5000,這就是事務的一致性。架構

⑶ 隔離性(Isolation)併發

隔離性是當多個用戶併發訪問數據庫時,好比操做同一張表時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做所幹擾,多個併發事務之間要相互隔離。分佈式

即要達到這麼一種效果:對於任意兩個併發的事務 T1 和 T2,在事務 T1 看來,T2 要麼在 T1 開始以前就已經結束,要麼在 T1 結束以後纔開始,這樣每一個事務都感受不到有其餘事務在併發地執行。微服務

關於事務的隔離性數據庫提供了多種隔離級別,稍後會介紹到。

⑷ 持久性(Durability)

持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即使是在數據庫系統遇到故障的狀況下也不會丟失提交事務的操做。

例如咱們在使用 JDBC 操做數據庫時,在提交事務方法後,提示用戶事務操做完成,當咱們程序執行完成直到看到提示後,就能夠認定事務以及正確提交,即便這時候數據庫出現了問題,也必需要將咱們的事務徹底執行完成,不然就會形成咱們看到提示事務處理完畢,可是數據庫由於故障而沒有執行事務的重大錯誤。

以上介紹完事務的四大特性 (簡稱 ACID),如今重點來講明下事務的隔離性,當多個線程都開啓事務操做數據庫中的數據時,數據庫系統要能進行隔離操做,以保證各個線程獲取數據的準確性,在介紹數據庫提供的各類隔離級別以前,咱們先看看若是不考慮事務的隔離性,會發生的幾種問題:

1  髒讀

髒讀是指在一個事務處理過程裏讀取了另外一個未提交的事務中的數據。

當一個事務正在屢次修改某個數據,而在這個事務中這屢次的修改都還未提交,這時一個併發的事務來訪問該數據,就會形成兩個事務獲得的數據不一致。例如:用戶 A 向用戶 B 轉帳 100 元,對應 SQL 命令以下

 
  1. update account set money=money+100 where name=’B’;  (此時A通知B)  
  2. update account set money=money - 100 where name=’A’;  

當只執行第一條 SQL 時,A 通知 B 查看帳戶,B 發現確實錢已到帳(此時即發生了髒讀),而以後不管第二條 SQL 是否執行,只要該事務不提交,則全部操做都將回滾,那麼當 B 之後再次查看帳戶時就會發現錢其實並無轉。

2 不可重複讀

不可重複讀是指在對於數據庫中的某個數據,一個事務範圍內屢次查詢卻返回了不一樣的數據值,這是因爲在查詢間隔,被另外一個事務修改並提交了。

例如事務 T1 在讀取某一數據,而事務 T2 立馬修改了這個數據而且提交事務給數據庫,事務 T1 再次讀取該數據就獲得了不一樣的結果,發送了不可重複讀。

不可重複讀和髒讀的區別是,髒讀是某一事務讀取了另外一個事務未提交的髒數據,而不可重複讀則是讀取了前一事務提交的數據。

在某些狀況下,不可重複讀並非問題,好比咱們屢次查詢某個數據固然以最後查詢獲得的結果爲主。但在另外一些狀況下就有可能發生問題,例如對於同一個數據 A 和 B 依次查詢就可能不一樣,A 和 B 就可能打起來了……

3 虛讀 (幻讀)

幻讀是事務非獨立執行時發生的一種現象。例如事務 T1 對一個表中全部的行的某個數據項作了從 「1」 修改成 「2」 的操做,這時事務 T2 又對這個表中插入了一行數據項,而這個數據項的數值仍是爲 「1」 而且提交給數據庫。而操做事務 T1 的用戶若是再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務 T2 中添加的,就好像產生幻覺同樣,這就是發生了幻讀。

幻讀和不可重複讀都是讀取了另外一條已經提交的事務(這點就髒讀不一樣),所不一樣的是不可重複讀查詢的都是同一個數據項,而幻讀針對的是一批數據總體(好比數據的個數)。

如今來看看 MySQL 數據庫爲咱們提供的四種隔離級別:

① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。

② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。

③ Read committed (讀已提交):可避免髒讀的發生。

④ Read uncommitted (讀未提交):最低級別,任何狀況都沒法保證。

以上四種隔離級別最高的是 Serializable 級別,最低的是 Read uncommitted 級別,固然級別越高,執行效率就越低。像 Serializable 這樣的級別,就是以鎖表的方式 (相似於 Java 多線程中的鎖) 使得其餘的線程只能在鎖外等待,因此平時選用何種隔離級別應該根據實際狀況。在 MySQL 數據庫中默認的隔離級別爲 Repeatable read (可重複讀)。

在 MySQL 數據庫中,支持上面四種隔離級別,默認的爲 Repeatable read (可重複讀);而在 Oracle 數據庫中,只支持 Serializable (串行化) 級別和 Read committed (讀已提交) 這兩種級別,其中默認的爲 Read committed 級別。

在 MySQL 數據庫中查看當前事務的隔離級別:

 
  1. select @@tx_isolation; 

在 MySQL 數據庫中設置事務的隔離 級別:

 
  1. set  [glogal | session]  transaction isolation level 隔離級別名稱;  
  2. set tx_isolation=’隔離級別名稱;’ 

例 1:查看當前事務的隔離級別:

例 2:將事務的隔離級別設置爲 Read uncommitted 級別:

或:

記住:設置數據庫的隔離級別必定要是在開啓事務以前!

若是是使用 JDBC 對數據庫的事務設置隔離級別的話,也應該是在調用 Connection 對象的 setAutoCommit(false) 方法以前。調用 Connection 對象的 setTransactionIsolation(level) 便可設置當前連接的隔離級別,至於參數 level,可使用 Connection 對象的字段:

在 JDBC 中設置隔離級別的部分代碼:

後記

隔離級別的設置只對當前連接有效。對於使用 MySQL 命令窗口而言,一個窗口就至關於一個連接,當前窗口設置的隔離級別只對當前窗口中的事務有效;對於 JDBC 操做數據庫來講,一個 Connection 對象至關於一個連接,而對於 Connection 對象設置的隔離級別只對該 Connection 對象有效,與其餘連接 Connection 對象無關。

注:關注做者微信公衆號,瞭解更多分佈式架構、微服務、netty、MySQL、spring、、性能優化、等知識點。

公衆號:《 Java大蝸牛 

相關文章
相關標籤/搜索