目录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