事务处理
一、事务四特性(ACID)
1、原子性:一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。也就是说如果事务中的某一步出错,所有对数据进行的
2、一致性:事务必须是使数据库从一个状态变到另一个状态。总的状态要一致。
3、隔离性:一个事务的执行不会被其他事务所干扰。
4、持久性:一个事务一旦提交,它对数据改动将是持久性的,其他操作和故障不应使其数据丢失。
二、mysql数据库事务
1、事务的语句
使用START TRANSACTION或者BEGIN来开启一个新事务。
使用COMMIT来提交一个事务,使其操作产生持久性的影响。
使用ROOLBACK来撤回这次事务,取消其产生的影响。
使用SET autocommit来设置是否自动提交事务。
2、默认事务
mysql默认使用autocommit,这意味着每一个语句都是原子性的,其效果相当于把该语句环绕一个begin和commit语句的事务,并且不能使用rollback语句。当该语句执行过程中出现错误时,该语句则被ROOLBACK。
3、事务隔离级别
读未提交:为事务之间提供最小的保护,它使用了特定的锁定策略,允许一个事务在不等待另一个事务结束的情况下继续执行。这种性能的提升降低了一致性,导致了脏读(读取其他事务未提交的数据)等出现。一般在只执行查询操作的情况下使用。
读已提交:采用某种锁定策略,一定程度放松了事务之间的保护。它使得事务不能读取其他事务未提交的数据,但是可以读取当前事务启动后由其他事务提交的数据。这就可能出现不可重复读(一个事务中两次相同的查询产生了不同的结果)。
可重复读:这是InnoDB的默认隔离级别,它能够防止被查询的任何行被其他事务更改,从而避免不可重复读的问题,但是不能够防止幻读。
串行化:这是最保守的策略,防止其他事务更改或插入当前事务正在读取的数据,同一个查询可以在事务中反复运行,并确保每次都检索相同的结果集。自当前事务开始以来,任何更改其他事务提交的数据的尝试都会导致当前事务等待。
一致性读(快照读):读已提交和可重复读都默认使用了一致性读的方式,即采用了mvcc的技术,可以在其他事务未结束前继续执行查询操作,读取数据库的快照,即读取数据库的历史数据。对于读已提交和可重复读的隔离级别的区别就是,在读已提交的隔离级别下,一个事务可以读取到其他事务提交后的数据。这就导致了不可重复读的问题。在可重复读的隔离方式下,当进行一致性读的时候,InnoDB会给定一个时间点(一般为第一次查询的时间点),该时间点之前的数据都可读取到,如果另外一个事务在该时间点之后删除并提交,则不会被感知到,插入和删除也是同样,这就导致了幻读。
而且因为一致读不会对其访问的表设置任何锁,所以在对表执行一致读时,其他事务可以自由修改这些表。
*加锁读:*对于特殊的的select语句(select 。。。 for update),Innodb使用了加锁读的方式。对于隔离级别为串行化的情况下,也会使用加锁读。
三、spring事务
1、作用
spring事务的本质就是数据库对事务的支持,Spring封装了所有对事务处理的功能,包括异常时回滚,操作成功时数据提交等复杂操作,大大减少了程序员的工作量,使得开发逻辑更为清晰,代码耦合度降低。
spring并不直接管理事务,而是提供多种事务管理器,这些事务管理器通过将事务的职责委托给持久化机制框架来实现,Spring事务管理器的接口是PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器。
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
void commit(TransactionStatus var1) throws TransactionException;
void rollback(TransactionStatus var1) throws TransactionException;
}
PlatformTransactionManager接口主要提供了两个抽象方法,即提交和回滚,不同的事务管理器根据自身环境实现该两大方法。
2、属性
在使用spring声明式事务中,必须注意事务的属性。事务的属性由事务的传播行为,事务的隔离级别,事务超时设置和事务的只读属性组成。Spring在TransactionDefinition接口中定义这些属性,以供PlatfromTransactionManager使用。
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 = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;
default int getPropagationBehavior() {
return 0;
}
default int getIsolationLevel() {
return -1;
}
default int getTimeout() {
return -1;
}
default boolean isReadOnly() {
return false;
}
@Nullable
default String getName() {
return null;
}
static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
}
3、传播行为
TransactionDefination接口中定义了七个传播行为。spring保证一个事务中都使用同一个数据库连接。传播行为只有在public的方法中才能使用。
PROPOGATION_REQUIRED:这通常是默认的传播行为。如果当前事务存在,支持当前事务。如果当前不存在事务就创建一个新事务。
@Transactional
@Service
public class TranServiceImpl {
public void methodA(){
//do something
methodB();
}
public void methodB(){
}
}
例如methodA中调用了methodB,如果调用A的时候不存在事务,则创建一个事务,在methodA中调用methodB时,因为当前已经存在一个事务,所以methidB则加入当前事务。
PROPOGATION_SUPPORTS: 如果当前存在事务,则支持当前事务,如果当前没有事务,则非事务的执行。
@Service
public class TranServiceImpl {
@Transactional(rollbackFor = Exception.class)
public void methodA(){
//do something
methodB();
}
@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
public void methodB(){
}
}
例如,如果只执行methodB,这不创建事务,methodB在没有事务的情况下执行。若调用methodA,methodA调用了methodB,那么methodB加入methodA创建的事务。
PROPOGATION_MANDATORY: 如果存在一个事务,则支持当前事务,如果不存在事务,则抛出IllegalTransactionStateException异常。
@Service
public class TranServiceImpl {
@Transactional(rollbackFor = Exception.class)
public void methodA(){
//do something
methodB();
}
@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
public void methodB(){
}
}
例:如果单独执行methodB,因为没有事务存在,所以会抛出异常,如果执行methodA,因为methodB在methodA中调用,则加入methodA的事务。
PROPOGATION_REQUIRES_NEW:创建一个新的事务,如果当前事务存在,就将其挂起。需要使用 JtaTransactionManager作为事务管理器。
@Service
public class TranServiceImpl {
@Transactional(rollbackFor = Exception.class)
public void methodA(){
//do something
methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
public void methodB(){
}
}
例:methodA中调用了methodB,但是methodA和methodB是使用了两个不同的事务,互不影响,即使methodA回滚了,也不会回滚methodB提交过的内容。
PROPOGATION_NOT_SUPPORTED:总是非事务的运行,并挂起当前事务。同样需要使用JtaTransactionManager作为事务管理器。
PROPOGATION_NEVER:总是非事务的运行,如果存在当前事务,则抛出异常。
PROPOGATION_NESTED:如果存在事务,就按照嵌套事务执行,如果不存在事务,就按照PROPOGATION_REQUIRED传播行为执行。
嵌套事务一个非常重要的特点就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。
4、只读属性
如果事务只对数据库做读操作,可以设置该属性,这样数据库可以进行适当优化。
5、超时
为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
四、redis数据库事务
1、概念
Redis事务本质上是一组命令的集合,事务支持一次执行多个命令,事务中的所有命令都会被序列化,redis按照顺序串行化执行事务中的命令。其他客户端的命令不会插入事务执行命令的序列中来。
2、隔离级别
redis的批量操作在发送EXEC之前都是放在缓存队列中的,并没有被实际执行。因此不会存在能否看到其他事务的更新等问题。
3、原子性
在redis中,单条命令是原子性的,不存在回滚。因此可能会出现一个事务的命令序列部分成功的情况。
若在事务队列中存在命令性错误(类似于java编译性错误),则执行EXEC命令时,所有命令都不会执行。
若在事务队列中存在语法性错误(类似于java的1/0的运行时异常),则执行EXEC命令时,其他正确命令会被执行,错误命令抛出异常。
4、相关命令
watch key…: 监视一个或多个key,当监视的key值发生变化时,则事务被打断。即乐观锁。
multi:标记一个事务块的开始。
discard:取消事务,放弃事务块中的所有命令.
exec:提交事务。监控锁此时会被取消掉。
unwatch:取消监控锁的监控。
来源:CSDN
作者:我家璨璨真可爱
链接:https://blog.csdn.net/weixin_42379740/article/details/103680452