事务
事务及数据库事务,是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
ACID特性
数据库事务拥有四个特性,称之为ACID特性。
- 原子性(
Atomicity
):事务作为一个整体被执行,事务中的所有操作要么全部完成,要么全部不完成。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态。 - 一致性(
Consistency
):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。在事务开始之前和事务结束以后,数据库的完整性没有被破坏。 - 隔离性(
Isolation
):当两个或者多个事务并发访问数据库的同一数据时所表现出的相互关系。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read commited
)、可重复读(repeatable read) 和串行化(Serializable) - 持久性(Durability):在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库中,并且是完全的。
事务并发问题
分布式系统中,通常会有多个线程连接到数据库中同时对一个表进行操作,这种情况下如果会话的事务设置不当,就会导致数据混乱,经常会出现以下三种情况
- 脏读(Dirty Read)
- 不可重复读(Unrepeatable Read)
- 换读(Phantom Read)
事务隔离级别
针对事务的隔离性,分为四个隔离级别
- 串行化(Serializable):可避免脏读、不可重复读、幻读情况发生
- 可重复读(repeatable read): 可避免脏读、不可重复读情况发生
- 读提交(read commited): 可避免脏读情况发生
- 读未提交(Read uncommitted):级别最低,上面三种情况都无法保证
Spring 事务管理
spring 事务属性
在Spring中,事务是通过 TransactionDefinition
接口来定义的,包含了与事务属性相关的方法。
public interface TransactionDefinition{
int getIsolationLevel(); // 返回事务的传播行为
int getPropagationBehavior(); // 返回事务的隔离级别
int getTimeout(); // 返回事务在超时前能运行多久
boolean isReadOnly(); // 返回事务是否只读
}
事务隔离级别
TransactionDefinition
中还定义了五个表示隔离级别的常量:
-
ISOLATION_DEFAULT
: 默认值,表示使用底层数据库的默认隔离级别。其他四个与JDBC的隔离级别相对应 -
ISOLATION_READ_UNCOMMITTED
: 表示一个事务可以看到另一事物修改但还没有提交的数据。这种隔离级别会产生脏读,不可重复读和幻读,因此很少使用该级别。 -
ISOLATION_READ_COMMITTED
:表示一个事务只能读取另一个事务已经提交的数据。可以防止脏读。 -
ISOLATION_REPEATABLE_READ
:表示一个事务在整个过程中可以多次重复执行某个查询,并且返回的记录相同。即使在多次在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。 -
ISOLATION_SERIALIZABLE
: 级别最高,所有事务依次顺序执行,可以防止脏读、不可重复读以及幻读。但是开销太大影响程序性能,通常不会用该级别。
事务传播行为
事务传播行为是指,开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定当前事务的行为。TransactionDefinition
接口中定义了七个事务传播行为。
-
PROPAGATION_REQUIRED
:如果当前存在事务,则加入当前事务;不存在这新建一个事务 -
PROPAGATION_SUPPORTS
:如果当前存在事务,则加入当前事务;不存在则以非事务(non-transactionally
)方式执行 -
PROPAGATION_MANDATORY
:如果当前存在事务,则加入当前事务;不存在则抛出异常 -
PROPAGATION_REQUIRES_NEW
:如果当前存在事务,则将当前事务挂起;新创建一个事务 -
PROPAGATION_NOT_SUPPORTED
:以非事务(non-transactionally
)方式运行,如果当前存在事务,则把当前事务挂起 -
PROPAGATION_NEVER
:以非事务(non-transactionally
)方式运行,如果当前存在事务,则抛出异常 -
PROPAGATION_NESTED
:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务执行,如果当前没有事务,则等价于PROPAGATION_REQUIRED
//新开一个辅助线程,用于定时向ZK服务器汇报心路
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
String heartBeat = robotIdValue + DateUtil.formatDate(new Date()) + "#" + servletContext.getServerInfo();
logger.debug(uuid + " => " + heartBeat); // /robot/{env}/{robotId}/uuid
String heartBeatNode = TargetNodes.APP_ROOT_NODE + "/" + env + "/" + appId + "/" + uuid;
if (!zkClient.exists(heartBeatNode)) {
zkClient.createEphemeral(heartBeatNode);
}
zkClient.writeData(heartBeatNode, heartBeat);
try {
Thread.sleep(10000);//10秒钟报告一次心跳
} catch (InterruptedException e)
{
logger.error(appId + " 发送心跳出错!", e);
}
}
}}).start();