本文内容前面配置從spring3.2.8參考文檔摘抄以及自己的了解,後面關于傳播級别和隔離的知識點參考自[url]http://www.blogjava.net/freeman1984/archive/2010/04/28/319595.html[/url]
[b][size=large]一、XML配置(推薦使用XML配置,一次配置可以多處使用,
注解需要每個方法寫 @Transactinal)[/size][/b]
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
<!-- don't forget the DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>
</bean>
<!-- similarly, don't forget the PlatformTransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
注:根據spring參考文檔,可以對不同的bean設定不同的事務級别
[b][size=medium]關注點:[/size][/b]
1.通過<tx:advice>标簽裡的<tx:method>指定需要進行事務處理的類。
2.<tx:method>包含屬性如下:
name:方法名稱。如“get*”代表以get開始的方法。
propagation:事務傳播級别
isolation:隔離級别
timeout:事務逾時時間
read-only:隻讀。如配置為true,主要是spring用來優化性能,
或者防止某些方法進行寫操 作;不配置為true也能正常工作。
rollback-for:配置事務需要復原的異常。
如:<tx:method name="get*" rollbackfor="NoProductInStockException"/>
no-rollback-for:配置事務不需要復原的異常。
如:<tx:method name="updateStock" no-rollbackfor="InstrumentNotFoundException"/>
[b][size=large]二、注解[/size][/b]
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/>
<!-- a PlatformTransactionManager is still required -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
[b][size=medium]關注點:[/size][/b]
1.在類或者方法上加上@Transactional,即可加入spring的事務管理,兩者可以同時加,但方法上的事務優于類的事務。@Transactional的屬性與基于XML配置的<tx:method>包含的屬性差不多,如下兩個屬性新增:
rollbackForClassName:需要復原的類名
noRollbackForClassName:不需要復原的類名集
預設屬性:
a.Propagation setting is PROPAGATION_REQUIRED.
b.Isolation level is ISOLATION_DEFAULT.
c.Transaction is read/write.
d.Transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.
e.Any RuntimeException triggers rollback, and any checked Exception does not.
2.<tx:annotation-driven/>屬性
transaction-manager:事務管理器名字;如果事務管理器bean的
名字預設為“transactionManager”,則此屬性預設就會關聯;
如果名字不是“transactionManager”,則需要指定。
mode:代理模式,預設“proxy”,直接使用spring aop方式處理;
也可以選擇“aspectj”,則用aspectj方式處理,具體參考文檔。
(基于XML的配置不可以選擇。)
proxy-target-class:預設false,基于JDK動态代理,隻适用于面向接口程式設計;
當有些特殊情況不是面向接口程式設計時,可以使用CGLIB等基于繼承的代理方式,
值為true。基于XML方式的配置也可以指定此值,在<aop:config>有此屬性。
order:當有多個advise存在時,可以通過此屬性指定執行順序。
基于XML方式的配置可以參考文檔。
[b][size=large]三、事務傳播與隔離級别。(TransactionDefinition中定義)[/size][/b]
/**
* Support a current transaction; create a new one if none exists.
*/
int PROPAGATION_REQUIRED = 0;
/**
* Support a current transaction;
* execute non-transactionally if none exists.
*/
int PROPAGATION_SUPPORTS = 1;
/**
* Support a current transaction;
* throw an exception if no current transaction.
*/
int PROPAGATION_MANDATORY = 2;
/**
* Create a new transaction,
* suspending the current transaction if one exists.
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* Do not support a current transaction;
* rather always execute non-transactionally.
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* Do not support a current transaction;
* throw an exception if a current transaction
*/
int PROPAGATION_NEVER = 5;
/**
* Execute within a nested transaction if a current transaction
*/
int PROPAGATION_NESTED = 6;
/**
* Use the default isolation level of the underlying datastore.
*/
int ISOLATION_DEFAULT = -1;
/**
* Indicates that dirty reads, non-repeatable reads and phantom reads
* can occur.
*/
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
/**
* Indicates that dirty reads are prevented; non-repeatable reads and
* phantom reads can occur.
*/
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
/**
* Indicates that dirty reads and non-repeatable reads are prevented;
* phantom reads can occur.
*/
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
/**
* Indicates that dirty reads, non-repeatable reads and phantom reads
* are prevented.
*/
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
[b][size=large]四、事務傳播執行個體[/size][/b]
ServiceA {
/**
* 事務屬性配置為 PROPAGATION_REQUIRED
*/
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
/**
* 事務屬性配置為 PROPAGATION_REQUIRED
*/
void methodB() {
}
}
[b][size=medium]1: PROPAGATION_REQUIRED [/size][/b]
加入目前正要執行的事務不在另外一個事務裡,那麼就起一個新的事務 比如說,ServiceB.methodB的事務級别定義為PROPAGATION_REQUIRED, 那麼由于執行 ServiceA.methodA的時候,ServiceA.methodA已經起了事務,這時調用ServiceB.methodB,ServiceB.methodB看到自己已經運作在ServiceA.methodA 的事務内部,就不再起新的事務。而假如ServiceA.methodA運作的時候發現自己沒有在事 務中,他就會為自己配置設定一個事務。這樣,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出現異常,事務都會被 復原。即使ServiceB.methodB的事務已經被 送出,但是ServiceA.methodA在接下來fail要復原,ServiceB.methodB也要復原
[b][size=medium]2: PROPAGATION_SUPPORTS[/size] [/b]
如果目前在事務中,即以事務的形式運作,如果目前不再一個事務中,那麼就以非事務的 形式運作
[b][size=medium]3: PROPAGATION_MANDATORY[/size][/b]
必須在一個事務中運作。也就是說,他隻能被一個父事務調用。否則,他就要抛出異常
[b][size=medium]4: PROPAGATION_REQUIRES_NEW[/size][/b]
這個就比較繞口了。 比如我們設計ServiceA.methodA的事務級别PROPAGATION_REQUIRED,ServiceB.methodB的事務級别為PROPAGATION_REQUIRES_NEW,那麼當執行到ServiceB.methodB的時候,ServiceA.methodA所在的事務就會挂起,ServiceB.methodB會起一個新的事務,等待ServiceB.methodB的事務完成以後, 他才繼續執行。他PROPAGATION_REQUIRED 的事務差別在于事務的復原程度了。因為ServiceB.methodB是新起一個事務,那麼就是存在兩個不同的事務。如果ServiceB.methodB已經送出,那麼ServiceA.methodA失敗復原,ServiceB.methodB是不會復原的。如果ServiceB.methodB失敗復原, 如果他抛出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務仍然可能送出。
[b][size=medium]5: PROPAGATION_NOT_SUPPORTED[/size][/b]
目前不支援事務。比如ServiceA.methodA的事務級别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務級别是PROPAGATION_NOT_SUPPORTED,那麼當執行ServiceB.methodB時,ServiceA.methodA的事務挂起,而他以非事務的狀态運作完,再繼續ServiceA.methodA的事務。
[b][size=medium]6: PROPAGATION_NEVER[/size][/b]
不能在事務中運作。假設ServiceA.methodA的事務級别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務級别是PROPAGATION_NEVER , 那麼ServiceB.methodB就要抛出異常了。
[b][size=medium]7: PROPAGATION_NESTED[/size][/b]
了解Nested的關鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的差別是PROPAGATION_REQUIRES_NEW另起一個事務,将會與他的父事務互相獨立, 而Nested的事務和他的父事務是相依的,他的送出是要等和他的父事務一塊送出的。也就是說,如果父事務最後復原,他也要復原的。 而Nested事務的好處是他有一個savepoint。
[size=medium]如下所示:[/size]
ServiceA {
/**
* 事務屬性配置為 PROPAGATION_REQUIRED
*/
void methodA() {
try {
//savepoint
ServiceB.methodB(); //PROPAGATION_NESTED 級别
} catch (SomeException) {
// 執行其他業務, 如 ServiceC.methodC();
}
}
}
也就是說ServiceB.methodB失敗復原,那麼ServiceA.methodA也會復原到savepoint點上,ServiceA.methodA可以選擇另外一個分支,比如ServiceC.methodC,繼續執行,來嘗試完成自己的事務。 但是這個事務并沒有在EJB标準中定義。
[b][size=large]五、Isolation Level(事務隔離等級)[/size][/b]
1、Serializable:最嚴格的級别,事務串行執行,資源消耗最大;
2、REPEATABLE READ:保證了一個事務不會修改已經由另一個事務讀取但未送出(復原)的資料。避免了“髒讀取”和“不可重複讀取”的情況,但是帶來了更多的性能損失。
3、READ COMMITTED:大多數主流資料庫的預設事務等級,保證了一個事務不會讀到另一個并行事務已修改但未送出的資料,避免了“髒讀取”。該級别适用于大多數系統。
4、Read Uncommitted:保證了讀取過程中不會讀取到非法資料。隔離級别在于處理多事務的并發問題。
我們知道并行可以提高資料庫的吞吐量和效率,但是并不是所有的并發事務都可以并發運作,這需要檢視資料庫教材的可串行化條件判斷了。
我們首先說并發中可能發生的3中不讨人喜歡的事情
1: Dirty reads--讀髒資料。也就是說,比如事務A的未送出(還依然緩存)的資料被事務B讀走,如果事務A失敗復原,會導緻事務B所讀取的的資料是錯誤的。
2: non-repeatable reads--資料不可重複讀。比如事務A中兩處讀取資料-total-的值。在第一讀的時候,total是100,然後事務B就把total的資料改成200,事務A再讀一次,結果就發現,total竟然就變成200了,造成事務A資料混亂。
3: phantom reads--幻象讀資料,這個和non-repeatable reads相似,也是同一個事務中多次讀不一緻的問題。但是non-repeatable reads的不一緻是因為他所要取的資料集被改變了(比如total的資料),但是phantom reads所要讀的資料的不一緻卻不是他所要讀的資料集改變,而是他的條件資料集改變。比如Select account.id where account.name="ppgogo*",第一次讀去了6個符合條件的id,第二次讀取的時候,由于事務b把一個帳号的名字由"dd"改成"ppgogo1",結果取出來了7個資料。
參考自:[url]http://www.cnblogs.com/digdeep/p/4947694.html[/url]
一緻性讀,又稱為快照讀。使用的是MVCC機制讀取undo中的已經送出的資料。是以它的讀取是非阻塞的。
相關文檔:http://dev.mysql.com/doc/refman/5.6/en/innodb-consistent-read.html
A consistent read means that InnoDB uses multi-versioning to present to a query a snapshot of the database at a point in time. The query sees the changes made by transactions that committed before that point of time, and no changes made by later or uncommitted transactions. The exception to this rule is that the query sees the changes made by earlier statements within the same transaction.
一緻性讀肯定是讀取在某個時間點已經送出了的資料,有個特例:本事務中修改的資料,即使未送出的資料也可以在本事務的後面部分讀取到。
[b]1. RC 隔離 和 RR 隔離中一緻性讀的差別[/b]
根據隔離級别的不同,一緻性讀也是不一樣的。不同點在于判斷是否送出的“某個時間點”:
[b]1)對于RR隔離:[/b]
If the transaction isolation level is REPEATABLE READ (the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction.
文檔中說的是:the first such read in that transaction。實際上實驗的結果表明,這裡的 the first such read指的是:對同一個表或者不同表進行的第一次select語句建立了該事務中一緻性讀的snapshot. 其它update, delete, insert 語句和一緻性讀snapshot的建立沒有關系。在snapshot建立之後送出的資料,一緻性讀就讀不到,之前送出的資料就可以讀到。
事務的起始點其實是以執行的第一條語句為起始點的,而不是以begin作為事務的起始點的。
[b]實驗1:[/b]
sesseion A
session B
mysql> set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
mysql> set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t1;
Empty set (0.00 sec)
mysql> insert into t1(c1,c2) values(1,1);
Query OK, 1 row affected (0.01 sec)
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 1 |
+----+------+
1 row in set (0.00 sec)
上面的實驗說明:RR隔離級别下的一緻性讀,不是以begin開始的時間點作為snapshot建立時間點,而是以第一條select語句的時間點作為snapshot建立的時間點。
[b]實驗2:[/b]
session A
session B
mysql> set tx_isolation='repeatable-read';
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.00 sec)
mysql> begin;
mysql> select * from t;
mysql> insert into t1(c1,c2) values(1,1);
Query OK, 1 row affected (0.01 sec)
mysql> select * from t1;
Empty set (0.00 sec)
該使用說明:RR隔離級别下的一緻性讀,是以第一條select語句的執行點作為snapshot建立的時間點的,即使是不同表的select語句。這裡因為session A在insert之前對 t 表執行了select,是以建立了snapshot,是以後面的select * from t1 不能讀取到insert的插入的值。
[b]實驗3:[/b]
session A
session B
mysql> set tx_isolation='repeatable-read';
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.00 sec)
mysql> begin;
mysql> select * from t1;
Empty set (0.00 sec)
mysql> select * from t1;
Empty set (0.00 sec)
mysql> insert into t1(c1,c2) values(1,1);
mysql> select * from t1;
Empty set (0.01 sec)
該實驗中:session A 的第一條語句,發生在session B的 insert語句送出之前,是以session A中的第二條select還是不能讀取到資料。因為RR中的一緻性讀是以事務中第一個select語句執行的時間點作為snapshot建立的時間點的。而此時,session B的insert語句還沒有執行,是以讀取不到資料。
[b]實驗4:[/b]
session A
session B
mysql> set tx_isolation='repeatable-read';
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.00 sec)
mysql> select * from t1;
Empty set (0.00 sec)
mysql> insert into t1(c1,c2) values(1,1),(2,2);
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 1 |
| 2 | 2 |
+----+------+
2 rows in set (0.01 sec)
mysql> select * from t1;
Empty set (0.00 sec)
mysql> update t1 set c2=100 where c1=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 100 |
+----+------+
1 row in set (0.00 sec)
該實驗說明:本事務中進行修改的資料,即使沒有送出,在本事務中的後面也可以讀取到。update 語句因為進行的是“目前讀”,是以它可以修改成功。
[b]2)對于RC隔離就簡單多了:[/b]
With READ COMMITTED isolation level, each consistent read within a transaction sets and reads its own fresh snapshot.
事務中每一次讀取都是以目前的時間點作為判斷是否送出的實際點,也即是 reads its own fresh snapshot.
RC是語句級多版本(事務的多條隻讀語句,建立不同的ReadView,代價更高),RR是事務級多版本(一個ReadView);
[b]3. MySQL 中事務開始的時間[/b]
一般我們會認為 begin/start transaction 是事務開始的時間點,也就是一旦我們執行了 start transaction,就認為事務已經開始了,其實這是錯誤的。上面的實驗也說明了這一點。事務開始的真正的時間點(LSN),是 start transaction 之後執行的第一條語句,不管是什麼語句,不管成功與否。
但是如果你想要達到将 start transaction 作為事務開始的時間點,那麼我們必須使用:
START TRANSACTION WITH consistent snapshot
它的含義是:執行 start transaction 同時建立本事務一緻性讀的 snapshot . 而不是等到執行第一條語句時,才開始事務,并且建立一緻性讀的 snapshot .
The WITH CONSISTENT SNAPSHOT modifier starts a consistent read for storage engines that are capable of it. This applies only to InnoDB. The effect is the same as issuing a START TRANSACTION followed by a SELECT from any InnoDB table. See Section 14.2.2.2, “Consistent Nonlocking Reads”. The WITH CONSISTENT SNAPSHOT modifier does not change the current transaction isolation level, so it provides a consistent snapshot only if the current isolation level is one that permits a consistent read. The only isolation level that permits a consistent read is REPEATABLE READ. For all other isolation levels, the WITH CONSISTENT SNAPSHOT clause is ignored. As of MySQL 5.7.2, a warning is generated when the WITH CONSISTENT SNAPSHOT clause is ignored.
http://dev.mysql.com/doc/refman/5.6/en/commit.html
效果等價于: start transaction 之後,馬上執行一條 select 語句(此時會建立一緻性讀的snapshot)。
If the transaction isolation level is REPEATABLE READ (the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction. You can get a fresher snapshot for your queries by committing the current transaction and after that issuing new queries. (RR隔離級别中的一緻性讀的snapshot是第一條select語句執行時建立的,其實應該是第一條任何語句執行時建立的)
http://dev.mysql.com/doc/refman/5.6/en/innodb-consistent-read.html
我們在 mysqldump --single-transaction 中使用的就是該語句:
SET session TRANSACTION isolation LEVEL REPEATABLE read
START TRANSACTION
是以事務開始時間點,分為兩種情況:
1)START TRANSACTION 時,是第一條語句的執行時間點,就是事務開始的時間點,第一條select語句建立一緻性讀的snapshot;
2)START TRANSACTION WITH consistent snapshot 時,則是立即建立本事務的一緻性讀snapshot,當然也開始事務了;
[b]實驗1:[/b]
session A
session B
mysql> set tx_isolation='repeatable-read';
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.01 sec)
mysql> start transaction;
mysql> insert into t1(c1,c2) values(1,1);
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 1 |
+----+------+
1 row in set (0.00 sec)
[b]實驗2:[/b]
mysql> set tx_isolation='repeatable-read';
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.01 sec)
mysql> start transaction with consistent snapshot;
mysql> insert into t1(c1,c2) values(1,1);
mysql> select * from t1;
Empty set (0.00 sec)
上面兩個實驗很好的說明了 start transaction 和 start tansaction with consistent snapshot的差別。第一個實驗說明,start transaction執行之後,事務并沒有開始,是以insert發生在session A的事務開始之前,是以可以讀到session B插入的值。第二個實驗說明,start transaction with consistent snapshot已經開始了事務,是以insert語句發生在事務開始之後,是以讀不到insert的資料。
[b]3. Oracle中的一緻性讀[/b]
Oracle讀一緻性是指一個查詢所獲得的資料來自同一時間點。
Oracle讀一緻性分為語句級讀一緻性和事務級讀一緻性。
語句級讀一緻性:Oracle強制實作語句級讀一緻性。一個查詢語句隻讀取語句開始之前送出的資料。
事務級讀一緻性:隔離級别為SERIALIZABLE和read only的事務才支援事務級讀一緻性。事務中的所有查詢語句隻讀取 事務開始之前送出的資料。
Oracle隻實作了RC和serializable,沒有實作Read uncommitted 和 RR。其實Oracle的serializable級别才實作了可重複讀。
[b]4. 目前讀(current read) 和 一緻性讀[/b]
一緻性讀是指普通的select語句,不帶 for update, in share mode 等等子句。使用的是undo中的送出的資料,不需要使用鎖(MDL除外)。而目前讀,是指update, delete, select for update, select in share mode等等語句進行的讀,它們讀取的是資料庫中的最新的資料,并且會鎖住讀取的行和gap(RR隔離時)。如果不能獲得鎖,則會一直等待,直到獲得或者逾時。RC隔離級别的目前讀沒有gap lock,RC的update語句進行的是“半一緻性讀”,和RR的update語句的目前讀不一樣。
[b]5. 一緻性讀與 mysqldump --single-transaction[/b]
我們知道 mysqldump --single-transaction的原理是:設定事務為RR模式,然後利用事務的特性,來獲得一緻性的資料,但是:
--single-transaction
Creates a consistent snapshot by dumping all tables in a
single transaction. Works ONLY for tables stored in
storage engines which support multiversioning (currently
only InnoDB does); the dump is NOT guaranteed to be
consistent for other storage engines. While a
--single-transaction dump is in process, to ensure a
valid dump file (correct table contents and binary log
position), no other connection should use the following
statements: ALTER TABLE, DROP TABLE, RENAME TABLE,
TRUNCATE TABLE, as consistent snapshot is not isolated
from them. Option automatically turns off --lock-tables.
在mysqldump運作期間,不能執行 alter table, drop table, rename table, truncate table 等等的DDL語句,因為一緻性讀和這些語句時無法隔離的。
那麼在mysqldump --single-transaction 執行期間,執行了上面那些DDL,會發生什麼呢?
mysqldump --single-transaction 的執行過程是:設定RR,然後開始事務,對應了一個LSN,然後對所有選中的表,一個一個的執行下面的過程:
save point sp; --> select * from t1 --> rollback to sp;
save point sp; --> select * from t2 --> rollback to sp;
... ...
1> 那麼如果對t2表的DDL發生在 save point sp 之前,那麼當mysqldump處理到 t2 表時,mysqldump 會立馬報錯:表結構已經改變......
2> 如果對t2表的DDL發生在 save point sp 之後,rollback to sp 之前,那麼要麼DDL被阻塞,要麼mysqldump被阻塞,具體誰被阻塞,看誰先執行了。
被阻塞額原因是:DDL需要t2表的 MDL 的互斥鎖,而select * from t1 需要MDL的共享鎖,是以阻塞發生。
3> 如果對t2表的DDL發生在 rollback to sp 之後,那麼因為對 t2 表的dump已經完成,不會發生錯誤或者阻塞。
那麼為什麼: 對t2表的DDL發生在 save point sp 之前,那麼當mysqldump開始處理 t2 表時,mysqldump 立馬報錯呢?
其原因就是 一緻性讀的胳膊拗不過DDL的大腿:
Consistent read does not work over certain DDL statements:(一緻性讀的胳膊拗不過DDL的大腿)
Consistent read does not work over DROP TABLE, because MySQL cannot use a table that has been dropped and InnoDB destroys the table.
Consistent read does not work over ALTER TABLE, because that statement makes a temporary copy of the original table and deletes the original table when the temporary copy is built. When you reissue a consistent read within a transaction, rows in the new table are not visible because those rows did not exist when the transaction's snapshot was taken. In this case, the transaction returns an error as of MySQL 5.6.6: ER_TABLE_DEF_CHANGED, “Table definition has changed, please retry transaction”.
原因:ALTER TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE 這些DDL語句的執行,會導緻無法使用undo構造出正确的一緻性讀,一緻性讀和它們是無法隔離的。