spring事务管理配置:注解和XML

本文内容前面配置从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 /*!40100 WITH consistent snapshot */

所以事务开始时间点,分为两种情况:

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构造出正确的一致性读,一致性读和它们是无法隔离的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值