事务处理

試著忘記壹切 提交于 2020-02-04 09:50:46

事务处理

一、事务四特性(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:取消监控锁的监控。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!