目錄java
ACID,是指數據庫管理系統(DBMS)在寫入或更新資料的過程當中,爲保證事務(transaction)是正確可靠的,所必須具有的四個特性:原子性(atomicity,或稱不可分割性)、一致性(consistency)、隔離性(isolation,又稱獨立性)、持久性(durability)。spring
在數據庫系統中,一個事務是指:由一系列數據庫操做組成的一個完整的邏輯過程。例如銀行轉賬,從原帳戶扣除金額,以及向目標帳戶添加金額,這兩個數據庫操做的總和,構成一個完整的邏輯過程,不可拆分。這個過程被稱爲一個事務,具備ACID特性。sql
原子性(Atomicity)
:一個事務(transaction)中的全部操做,要麼所有完成,要麼所有不完成,不會結束在中間某個環節。事務在執行過程當中發生錯誤,會被恢復(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過同樣。。數據庫
Transactions are often composed of multiple statements. Atomicity guarantees that each transaction is treated as a single "unit", which either succeeds completely, or fails completely: if any of the statements constituting a transaction fails to complete, the entire transaction fails and the database is left unchanged. An atomic system must guarantee atomicity in each and every situation, including power failures, errors and crashes.編程
一致性(Consistemcy)
:在事務開始以前和事務結束之後,數據庫的完整性沒有被破壞。這表示寫入的資料必須徹底符合全部的預設規則,這包含資料的精確度、串聯性以及後續數據庫能夠自發性地完成預約的工做。數組
Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct.併發
隔離性(Isolation)
:數據庫容許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性能夠防止多個事務併發執行時因爲交叉執行而致使數據的不一致。事務隔離分爲不一樣級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和串行化(Serializable)。app
Transactions are often executed concurrently (e.g., reading and writing to multiple tables at the same time). Isolation ensures that concurrent execution of transactions leaves the database in the same state that would have been obtained if the transactions were executed sequentially. Isolation is the main goal of concurrency control; depending on the method used, the effects of an incomplete transaction might not even be visible to other transactions.less
持久性(Durability)
:事務處理結束後,對數據的修改就是永久的,即使系統故障也不會丟失。ide
Durability guarantees that once a transaction has been committed, it will remain committed even in the case of a system failure (e.g., power outage or crash). This usually means that completed transactions (or their effects) are recorded in non-volatile memory.
The following examples further illustrate the ACID properties. In these examples, the database table has two columns, A and B. An integrity constraint requires that the value in A and the value in B must sum to 100. The following SQL code creates a table as described above:
CREATE TABLE acidtest (A INTEGER, B INTEGER, CHECK (A + B = 100));
Atomicity failure
Say our Logical transaction consists of transferring funds from account A to account B. This Logical transaction may be composed of several Physical transactions consisting of first removing the amount from account A as a first Physical transaction and then, as a second transaction, depositing said amount in account B. We would not want to see the amount removed from account A before we are sure it has been transferred into account B. Then, unless and until both transactions have happened and the amount has been transferred to account B, the transfer has not, to the effects of the database, occurred.
Consistency failure
Consistency is a very general term, which demands that the data must meet all validation rules. In the previous example, the validation is a requirement that A + B = 100. All validation rules must be checked to ensure consistency. Assume that a transaction attempts to subtract 10 from A without altering B. Because consistency is checked after each transaction, it is known that A + B = 100 before the transaction begins. If the transaction removes 10 from A successfully, atomicity will be achieved. However, a validation check will show that A + B = 90, which is inconsistent with the rules of the database. The entire transaction must be cancelled and the affected rows rolled back to their pre-transaction state. If there had been other constraints, triggers, or cascades, every single change operation would have been checked in the same way as above before the transaction was committed. Similar issues may arise with other constraints. We may have required the data types of both A,B to be integers. If we were then to enter, say, the value 13.5 for A, the transaction will be cancelled, or the system may give rise to an alert in the form of a trigger (if/when the trigger has been written to this effect). Another example would be with integrity constraints, which would not allow us to delete a row in one table whose Primary key is referred to by at least one foreign key in other tables.
Isolation failure
To demonstrate isolation, we assume two transactions execute at the same time, each attempting to modify the same data. One of the two must wait until the other completes in order to maintain isolation.
Consider two transactions. T1 transfers 10 from A to B. T2 transfers 10 from B to A. Combined, there are four actions:
T1 subtracts 10 from A. T1 adds 10 to B. T2 subtracts 10 from B. T2 adds 10 to A.If these operations are performed in order, isolation is maintained, although T2 must wait. Consider what happens if T1 fails halfway through. The database eliminates T1's effects, and T2 sees only valid data.
By interleaving the transactions, the actual order of actions might be:
T1 subtracts 10 from A. T2 subtracts 10 from B. T2 adds 10 to A. T1 adds 10 to B.Again, consider what happens if T1 fails while modifying B (step 4). By the time T1 fails, T2 has already modified A; it cannot be restored to the value it had before T1 without leaving an invalid database. This is known as a write-write failure,[citation needed] because two transactions attempted to write to the same data field. In a typical system, the problem would be resolved by reverting to the last known good state, canceling the failed transaction T1, and restarting the interrupted transaction T2 from the good state.
Durability failure
Consider a transaction that transfers 10 from A to B. First it removes 10 from A, then it adds 10 to B. At this point, the user is told the transaction was a success, however the changes are still queued in the
disk buffer
waiting to be committed to disk. Power fails and the changes are lost. The user assumes (understandably) that the changes persist.
所謂事務超時,就是指一個事務所容許執行的最長時間,若是超過該時間限制但事務尚未完成,則自動回滾事務。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。默認設置爲底層事務系統的超時值,若是底層數據庫事務系統沒有設置超時值,那麼就是none,沒有超時限制。
只讀事務用於客戶代碼只讀但不修改數據的情形,只讀事務用於特定情景下的優化,好比使用Hibernate的時候。默認爲讀寫事務。
指示Spring事務管理器回滾一個事務的推薦方法是在當前事務的上下文內拋出異常。Spring事務管理器會捕捉任何未處理的異常,而後依據規則決定是否回滾拋出異常的事務。
默認配置下,Spring只有在拋出的異常爲運行時unchecked異常時纔回滾該事務,也就是拋出的異常爲RuntimeException的子類(Errors也會致使事務回滾),而拋出checked異常則不會致使事務回滾。
能夠明確的配置在拋出那些異常時回滾事務,包括checked異常。也能夠明肯定義那些異常拋出時不回滾事務。
還能夠編程性的經過setRollbackOnly()方法來指示一個事務必須回滾,在調用完setRollbackOnly()後你所能執行的惟一操做就是回滾。
spring-tx
包含接口org.springframework.transaction.TransactionDefinition
,它定義了spring
使用的事務屬性:
package org.springframework.transaction; import java.sql.Connection; public interface TransactionDefinition { int PROPAGATION_REQUIRED = 0; int PROPAGATION_SUPPORTS = 1; int PROPAGATION_MANDATORY = 2; int PROPAGATION_REQUIRES_NEW = 3; int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; int PROPAGATION_NESTED = 6; int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE; int TIMEOUT_DEFAULT = -1; int getPropagationBehavior(); int getIsolationLevel(); int getTimeout(); boolean isReadOnly(); String getName(); }
隔離級別是指若干個併發的事務之間的隔離程度。TransactionDefinition 接口中定義了五個表示隔離級別的常量:
TransactionDefinition.ISOLATION_DEFAULT
:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,一般這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED
:該隔離級別表示一個事務能夠讀取另外一個事務修改但尚未提交的數據。該級別不能防止髒讀,不可重複讀和幻讀,所以不多使用該隔離級別。好比PostgreSQL實際上並無此級別。
TransactionDefinition.ISOLATION_READ_COMMITTED
:該隔離級別表示一個事務只能讀取另外一個事務已經提交的數據。該級別能夠防止髒讀,這也是大多數狀況下的推薦值。
TransactionDefinition.ISOLATION_REPEATABLE_READ
:該隔離級別表示一個事務在整個過程當中能夠屢次重複執行某個查詢,而且每次返回的記錄都相同。該級別能夠防止髒讀和不可重複讀。
TransactionDefinition.ISOLATION_SERIALIZABLE
:全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀。可是這將嚴重影響程序的性能。一般狀況下也不會用到該級別。
所謂事務的傳播行爲是指,若是在開始當前事務以前,一個事務上下文已經存在,此時有若干選項能夠指定一個事務性方法的執行行爲。在TransactionDefinition定義中包括了以下幾個表示傳播行爲的常量:
TransactionDefinition.PROPAGATION_REQUIRED
:若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。這是默認值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW
:建立一個新的事務,若是當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_SUPPORTS
:若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
:以非事務方式運行,若是當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_NEVER
:以非事務方式運行,若是當前存在事務,則拋出異常。
TransactionDefinition.PROPAGATION_MANDATORY
:若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。
TransactionDefinition.PROPAGATION_NESTED
:若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
屬性 | 類型 | 描述 |
---|---|---|
value | String | 可選的限定描述符,指定使用的事務管理器 |
propagation | enum: Propagation | 可選的事務傳播行爲設置 |
isolation | enum: Isolation | 可選的事務隔離級別設置 |
readOnly | boolean | 讀寫或只讀事務,默認讀寫 |
timeout | int (in seconds granularity) | 事務超時時間設置 |
rollbackFor | Class對象數組,必須繼承自Throwable | 致使事務回滾的異常類數組 |
rollbackForClassName | 類名數組,必須繼承自Throwable | 致使事務回滾的異常類名字數組 |
noRollbackFor | Class對象數組,必須繼承自Throwable | 不會致使事務回滾的異常類數組 |
noRollbackForClassName | 類名數組,必須繼承自Throwable | 不會致使事務回滾的異常類名字數組 |
@Transactional 能夠做用於接口、接口方法、類以及類方法上。看成用於類上時,該類的全部 public 方法將都具備該類型的事務屬性,同時,咱們也能夠在方法級別使用該標註來覆蓋類級別的定義。
雖然 @Transactional 註解能夠做用於接口、接口方法、類以及類方法上,可是 Spring 建議不要在接口或者接口方法上使用該註解,由於這隻有在使用基於接口的代理時它纔會生效。另外, @Transactional 註解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。若是你在 protected、private 或者默承認見性的方法上使用 @Transactional 註解,這將被忽略,也不會拋出任何異常。
默認狀況下,只有來自外部的方法調用纔會被AOP代理捕獲,也就是,類內部方法調用本類內部的其餘方法並不會引發事務行爲,即便被調用方法使用@Transactional註解進行修飾。
ANSI/ISO SQL 92標準涉及三種不一樣的一個事務讀取另一個事務可能修改的數據的「讀現象」。
下面的例子中,兩個事務,事務1執行語句1。接着,事務2執行語句2而且提交,最後事務1再執行語句1. 查詢使用以下的數據表。
id | name | age |
---|---|---|
1 | Joe | 20 |
2 | Jill | 25 |
當一個事務容許讀取另一個事務修改但未提交的數據時,就可能發生髒讀。
髒讀和不可重複讀(non-repeatable reads)相似。事務2沒有提交形成事務1的語句1兩次執行獲得不一樣的結果集。在未提交讀隔離級別惟一禁止的是更新混亂,即早期的更新可能出如今後來更新以前的結果集中。
在咱們的例子中,事務2修改了一行,可是沒有提交,事務1讀了這個沒有提交的數據。如今若是事務2回滾了剛纔的修改或者作了另外的修改的話,事務1中查到的數據就是不正確的了。
事務 1 | 事務 2 |
---|---|
/* Query 1 */ SELECT age FROM users WHERE id = 1; /* will read 20 */ |
|
/* Query 2 */ UPDATE users SET age = 21 WHERE id = 1; /* No commit here */ |
|
/* Query 1 */ SELECT age FROM users WHERE id = 1; /* will read 21 */ |
|
ROLLBACK; /* lock-based DIRTY READ */ |
Non-repeatable reads are when your transaction reads committed UPDATES from another transaction. The same row now has different values than it did when your transaction began.
在一次事務中,當一行數據獲取兩遍獲得不一樣的結果表示發生了「不可重複讀」.
在基於鎖的併發控制中「不可重複讀」現象發生在當執行SELECT(SQL)) 操做時沒有得到讀鎖或者SELECT(SQL))操做執行完後立刻釋放了讀鎖; 多版本併發控制中當沒有要求一個提交衝突的事務回滾也會發生「不可重複讀」現象。
事務 1 | 事務 2 |
---|---|
/* Query 1 */ SELECT * FROM users WHERE id = 1; |
|
/* Query 2 */ UPDATE users SET age = 21 WHERE id = 1; COMMIT; /* in multiversion concurrency control, or lock-based READ COMMITTED */ |
|
/* Query 1 */ SELECT * FROM users WHERE id = 1; COMMIT; /* lock-based REPEATABLE READ */ |
在這個例子中,事務2提交成功,所以他對id爲1的行的修改就對其餘事務可見了。可是事務1在此前已經從這行讀到了另一個「age」的值。在可序列化(SERIALIZABLE)和可重複讀的隔離級別,數據庫在第二次SELECT請求的時候應該返回事務2更新以前的值。在提交讀和未提交讀,返回的是更新以後的值,這個現象就是不可重複讀。
有兩種策略能夠避免不可重複讀。一個是要求事務2延遲到事務1提交或者回滾以後再執行。這種方式實現了T1, T2 的串行化調度。串行化調度能夠支持可重複讀。
另外一種策略是多版本併發控制。爲了獲得更好的併發性能,容許事務2先提交。但由於事務1在事務2以前開始,事務1必須在其開始執行時間點的數據庫的快照上面操做。當事務1最終提交時候,數據庫會檢查其結果是否等價於T1, T2串行調度。若是等價,則容許事務1提交,若是不等價,事務1須要回滾並拋出個串行化失敗的錯誤。
使用基於鎖的併發控制,在可重複讀的隔離級別中,ID=1的行會被鎖住,在事務1提交或回滾前一直阻塞語句2的執行。在提交讀的級別,語句1第二次執行,age已經被修改了。
在多版本併發控制機制下,可序列化(SERIALIZABLE)級別,兩次SELECT語句讀到的數據都是事務1開始的快照,所以返回一樣的數據。可是,若是事務1試圖UPDATE這行數據,事務1會被要求回滾並拋出一個串行化失敗的錯誤。
在提交讀隔離級別,每一個語句讀到的是語句執行前的快照,所以讀到更新先後不一樣的值。在這種級別不會有串行化的錯誤(由於這種級別不要求串行化),事務1也不要求重試。
Phantom reads are similar but when reading from committed INSERTS and/or DELETES from another transaction. There are new rows or rows that have disappeared since you began the transaction.
在事務執行過程當中,當兩個徹底相同的查詢語句執行獲得不一樣的結果集。這種現象稱爲「幻影讀(phantom read)」
當事務沒有獲取範圍鎖的狀況下執行SELECT ... WHERE操做可能會發生「幻影讀」。
「幻影讀」是不可重複讀的一種特殊場景:當事務1兩次執行SELECT ... WHERE檢索必定範圍內數據的操做中間,事務2在這個表中建立了(如INSERT)了一行新數據,這條新數據正好知足事務1的「WHERE」子句。
事務 1 | 事務 2 |
---|---|
/* Query 1 */ SELECT * FROM users WHERE age BETWEEN 10 AND 30; |
|
/* Query 2 */ INSERT INTO users VALUES ( 3, 'Bob', 27 ); COMMIT; |
|
/* Query 1 */ SELECT * FROM users WHERE age BETWEEN 10 AND 30; |
須要指出的是事務1執行了兩遍一樣的查詢語句。若是設了最高的隔離級別,兩次會獲得一樣的結果集,這也正是數據庫在可序列化(SERIALIZABLE)隔離級別上須要知足的。可是在較低的隔離級別上,第二次查詢可能會獲得不一樣的結果集。 在可序列化隔離級別,查詢語句1在age從10到30的記錄上加鎖,事務2只能阻塞直至事務1提交。在可重複讀級別,這個範圍不會被鎖定,容許記錄插入,所以第二次執行語句1的結果中會包括新插入的行。
Isolation level | Lost updates | Dirty reads | Non-repeatable reads | Phantoms |
---|---|---|---|---|
Read Uncommitted | don't occur | may occur | may occur | may occur |
Read Committed | don't occur | don't occur | may occur | may occur |
Repeatable Read | don't occur | don't occur | don't occur | may occur |
Serializable | don't occur | don't occur | don't occur | don't occur |
https://zh.wikipedia.org/wiki/ACID
https://en.wikipedia.org/wiki/ACID
https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Non-repeatable_reads
https://stackoverflow.com/questions/11043712/what-is-the-difference-between-non-repeatable-read-and-phantom-read
https://www.quora.com/What-is-a-phantom-read-in-database-systems