今天看啥  ›  专栏  ›  DoBest

Spring 事务处理超级详细详解

DoBest  · 掘金  ·  · 2019-07-12 03:19
阅读 47

Spring 事务处理超级详细详解

 事务是数据库逻辑上的一组操作,一个事务中的一组操作,要么都执行,要么都不执行。
复制代码

事务的四大特性(ACID)

Atomicity原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

Consistency一致性:整个事务执行前后数据库是保持一致的,保证数据库数据的完整性和正确性。

Isolation隔离性:各个并发事务之间不会互相干扰,多个并发事务之间互相隔离。(4个隔离级别)

Durability持久性:持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

redo,undo,binlog二进制

事务日志

redolog 重做日志,用来保证事务的持久性,在Innodb存储引擎下,Insert,Delete,Update操作记录的都是redo物理日志,记录的是数据页的物理变化,主要用于数据库的崩溃恢复。 Redo日志可以分为两部分,一个是内存中存储的redo日志缓存(redo log buffer),是容易丢失的,一个是存储在本地磁盘的redo日志文件,是持久的。 redo日志整个产生流程:

  • 第一步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝

  • 第二步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值

  • 第三步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式

  • 第四步:定期将内存中修改的数据刷新到磁盘中(innodb_flush_log_at_trx_commit设置策略0,1,2)

innodb_flush_log_at_trx_commit: 0: 每秒刷新到磁盘log(在mysql故障时可能会丢失最后1秒的数据) 1:每次提交都刷新日志到磁盘log(mysql默认) 2:每次提交都刷新到osbuffer(系统缓存),但每秒才刷新到磁盘log(在mysql故障时不影响,操作系统故障丢失最后1s数据)

undo log日志

undo主要记录的是数据的逻辑变化,为了在发生错误时回滚到之前的状态,需要把之前的操作记录下来。

undo日志,只将数据库逻辑地恢复到原来的样子,在回滚的时候,它实际上是做的相反的工作,比如一条INSERT ,对应一条 DELETE,对于每个UPDATE,对应一条相反的 UPDATE,将修改前的行放回去。undo日志用于事务的回滚操作进而保障了事务的原子性。

在InnoDB存储引擎中,undo存储在回滚段(Rollback Segment)中,每个回滚段记录了1024个undo log segment,而在每个undo log segment段中进行undo 页的申请,在5.6以前,Rollback Segment是在共享表空间里的,5.6.3之后,可通过 innodb_undo_tablespace设置undo存储的位置。

undo的类型 在InnoDB存储引擎中,undo log分为:

insert undo log

update undo log

insert undo log是指在insert 操作中产生的undo log,因为insert操作的记录,仅对事务本身可见,对其他事务不可见,故该undo log可以在事务提交完成后直接删除。

update undo log是指在update和delete操作中产生的undo log,该undo log可能需要提供mvcc机制(多版本并发控制),不能在事务提交后就删除undo log,等待purge线程进行最后的删除。(purge线程是指在Innodb存储引擎中,delete操作并不是直接删除数据,而是在要删除的数据上标识Delete_Bit,也就是平时所说的逻辑删除,purge线程会去清除带有Delete_Bit标识的数据)

undo日志并不是redo日志的逆过程,redo日志记录的是物理日志,是持久存在的,而undo日志是逻辑日志,对事物回滚时,只是把数据库恢复到之前的状态,每个undo的生命周期只是从事务开始到事务结束。

假设有A、B两个数据,值分别为1,2.

1. 事务开始
2. 记录A=1到undo log
3. 修改A=3
4. 记录A=3到 redo log
5. 记录B=2到 undo log
6. 修改B=4
7. 记录B=4到redo log
8. 将redo log写入磁盘
9. 事务提交
复制代码

binlog二进制日志

二进制日志是在存储引擎之上的层面,redo和undo是Innodb引擎中的操作日志,而binlog二进制日志是在引擎之上,因此不管数据库采用Innodb默认存储引擎还是其他存储引擎,都会产生binlog。 虽然binlog和redolog都是记录对数据库的操作,但是两者却不一样: 1.binlog记录不管是什么引擎,都会记录对数据库的操作,而redolog记录的是InnoDB引擎下对表的操作,并且binlog先于redolog记录。 2.binlog在commit后一次性写入缓存中的日志文件,而redolog则在数据准备修改前写入redobufferlog缓存中,写入完成后才会执行数据修改操作,待事务完成后会刷新到持久性redolog磁盘文件中。 3.事务日志是记录的是物理页的变化,具有幂等性,记录方式比较简洁。比如在一个事务中进行了某行数据的添加,删除,又添加,最终事务日志记录的只是最后添加的记录,也就是物理页的变化。而二进制日志则会把这几次操作全部记录下来,记录比较多。

sync_binlog:sync_binlog 是 MySQL 的二进制日志(binlog)同步到磁盘的频率。MySQL server 在 binary log 每写入 sync_binlog 次后,刷写到磁盘。 如果 autocommit 开启,每个语句都写一次 binary log,否则每次事务写一次。默认值是 0,不主动同步,而依赖操作系统本身不定期把文件内容 flush 到磁盘。设为 1 最安全,在每个语句或事务后同步一次 binary log,即使在崩溃时也最多丢失一个语句或事务的日志,但因此也最慢。

大多数情况下,对数据的一致性并没有很严格的要求,所以并不会把 sync_binlog 配置成 1. 为了追求高并发,提升性能,可以设置为 100 或直接用 0. 而和 innodb_flush_log_at_trx_commit 一样,对于支付服务这样的应用,还是比较推荐 sync_binlog = 1.

源码解析

Spring支持两种的事务使用:编程式事务与声明式事务。
复制代码

编程式事务:通过在业务代码中对事务做手动回滚,对代码入侵性强,不推荐使用.(TransactionAspectSupport,TransactionTemplate) 声明式事务:使用@Transactional注解。 Spring事务中,主要包含TransactionDefinition,TransactionStatus,PlatformTransactionManager,所谓的事务管理,其实就是"按照给定的事务规则执行事务的提交或回滚操作",TransactionDefination就表示给定的事务规则,TransactionStatus表示运行着的事务状态,PlatformTransactionManager用来执行事务操作。

TransactionDefinition

TransactionDefinition用于定义一个事务,包括事务的传播熟悉,隔离级别,超时时间,只读等属性,默认使用DefaultTransactionDefinition,也支持自定义配置。

PlatformTransactionManager

PlatformTransactionManager用于事务的执行操作,接口定义如下:

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}
复制代码

根据底层所使用的不同的持久化 API 或框架,PlatformTransactionManager 的主要实现类大致如下:

  • DataSourceTransactionManager:适用于使用JDBC和iBatis进行数据持久化操作的情况。

  • HibernateTransactionManager:适用于使用Hibernate进行数据持久化操作的情况。

  • JpaTransactionManager:适用于使用JPA进行数据持久化操作的情况。

  • 另外还有JtaTransactionManager JdoTransactionManager、JmsTransactionManager等等。

TransactionStatus

TransactionStatus表示事务的状态,通过PlatformTransactionManager.getTransaction()获取,TransactionStatus接口提供了一个简单的事务控制和事务查询的方法。

public  interface TransactionStatus{
   boolean isNewTransaction();
   void setRollbackOnly();
   boolean isRollbackOnly();
}
复制代码

声明式事务主要是通过Spring AOP实现的,对使用@Transactional注解的方法进行拦截,AOP通过代理方式执行目标方法,

根据invokeWithinTransaction方法创建PlatformTransactionManager,TransactionStatus,TransactionDefinition,从源码中可以看到TransactionInfo,表示事务对象,对前面几个进行封装。

class TransactionInfo{
    private final PlatformTransactionManager transactionManager;

    private final TransactionAttribute transactionAttribute;

	private final String joinpointIdentification;

	private TransactionStatus transactionStatus;

	private TransactionInfo oldTransactionInfo;
}
复制代码

,通过代理执行目标方法,如果方法执行完毕成功则执行commitTransactionAfterReturning进行事务提交,通过TransactionInfo.getTransactionManager().cimmit(),如果方法执行过程中出现异常并捕获到,则执行completeTransactionAfterThrowing进行事务回滚,TransactionInfo.getTransactionManager().rollback();

通过ThreadLocal本地线程变量管理TransactionInfo,表示Spring事务是线程安全的。




原文地址:访问原文地址
快照地址: 访问文章快照