事务记录怎么写
Spring的事务:
Spring的事务是数据库对事务的支持,数据库不支持Spring也是无法做到的,Spring能操作事务也是因为控制了和数据库的连接。纯JDBC用事务步骤:
1、获取连接 Connection con = Connection()
2、开启事务con.setAutoCommit(true/false);
3、执行CRUD
4、提交事务/回滚事务 conmit() / llback();
5、关闭连接 conn.close();
使用Spring也只是可以让我们省去步骤2和4,在我们加上注解后,用代理控制的,即在前后加上了开启事务和提交/回滚的代码。
事务的四个特性ACID:
A:原子性(Atomicity):事务是一个不可分割的整体,事务中要么都执行,要么都不执行。
C:一致性(Consistency):事务的前后有一致性,比如事务前A有100,B有200,事务后A有50,B有250,前后状态要一致。
I:隔离性(Isolation):多个事务执行时,一个事务的执行不影响另外的事务。
D:持久性(Durability):一个事务只要被提交了,那对数据库的修改就是永久的。
可以这样说:保证了ACID就保证了是一个正确的事务操作,那怎么来保证ACID呢?突然感觉这个问题好大,哈哈。根据一些资料,实现ACID的核心技术是并发控制和日志技术。
并发控制:MVCC,2PL,OCC 保证并发操作的正确性。
日志技术:Undo/Redo,WAL协议 保证故障场景下可以恢复。
保证原子性:并发控制+日志
查询和事务执行的之间状态别人看不到,事务中断,可以回退,消除影响。
保证一致性:应用层定义的完整性约束
保证隔离性:并发控制
调度并发的事务对数据库的操作,消除事务之间的干扰造成的异常结果。
保证持久性:日志技术
保证系统crash时,可以通过日志来恢复数据。
何为并发控制:2PL,MVCC,OCC都能实现正确的并发执行,各适合不同的场景。
MVCC:事务不持有锁,但是可以保证在执行过程中这个事务只能看到整个数据库过去的某个时刻的一致性状态,即使在事务执行过程中,别的事务更改了这个事务需要操作的数据。乐观锁。
2PL:两阶段封锁,事务在读、写每个数据对象前需申请持有共享锁/互斥锁,所持有的锁直到事务结束时才释放,事务申请不到锁时,需排队等待。悲观锁。
OCC:多个事务并发执行的时候,互相不阻塞。执行过程中,记录每个事务读写的历史,在事务提交前,检查事务的读写历史是否会造成不可串行化调度,如果是,挑选某个冲突的事务回滚。
2PL是最经典的并发控制方法,MVCC对于短事务和长查询混合负载通常有更好的并发度,OCC方法适合于读写冲突较少的场景,否则大量的事务回滚会造成性能下降。
首先是一致性:
初次看到ACID就感觉,一致性应该是最重要的,因为不管怎样,最终不就是想要得到这种一致性的效果嘛。spring roll怎么读
那怎么来保证一致性呢?
原子性:
实现原子性,需要通过数据库的日志,如果事务中操作失败(断电、软硬件问题),就可以通过回溯日志,将已经执行成功的操作撤销,从而实现回滚。
recovery的过程:读取日志的REDO,将已经执行成功但未写入磁盘的操作真正的写入磁盘,保证持久性。读取所有的UNDO,撤销所有执行了一部分但未提交的操作。
原子性实现了,可以保证A:100元,B:200元,A转B50元,B是250元,但是在这期间有另外的事务最终完成,是B转C100元,覆盖了A转B事务的结果,B变为了100元,咋办?为此引入了隔离性。
隔离性:即每一个事务看到的数据总是一致的,就好像其他并发事务不存在一样,即多个事务并发的结果,和他们串行执行的结果一样,怎么控制?就是上面的三种方式了MVCC,2PL,OCC,比如最经典的2PL,两阶段锁。
先来过一遍隔离性的理论知识,为了解决隔离性的问题,sql标准定义了四个隔离级别:
1、读未提交:(Read Uncommitted) 可导致脏读。
2、读已提交:(Read Committed)避免脏读,可导致不可重复读和幻读,大多数数据库默认的隔离级别。
3、可重复读:(Repeatable-Read) 避免脏读和不可重复读,可导致幻读,mysql数据库所默认的级别。
4、序列化:(serializable)避免脏读、不可重复读和幻读,串行化,效率低。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
大多数的数据库默认隔离级别为 Read Commited,比如 SqlServer、Oracle
少数数据库默认隔离级别为:Repeatable Read 比如: MySQL InnoDB
用户连接到数据库时,可以修改隔离模式,为了便于我们代码中修改,所以Spring也定义了隔离级别,便于我们操作数据库的隔离级别。
Spring的隔离级别:
ISOLATION_DEFAULT:这是PlatfromTransactionManager 默认的隔离级别,即使用数据库默认的事务隔离级别。另外四个与 JDBC 的隔离级别相对应。
ISOLATION_READ_UNCOMMITTED:对应数据库的读未提交。
ISOLATION_READ_COMMITTED:对应数据库的读已提交。
ISOLATION_REPEATABLE_READ:对应数据库的可重复读
ISOLATION_SERIALIZABLE:对应数据库的序列化
脏读:一事务对数据进行了增删改,但未提交,另一事务可以读取到未提交的数据。如果第一个事务这时候回滚了,那么第二个事务就读到了脏数据。
不可重复读:一个事务中发生了两次读操作,第一次读操作和第二次操作之间,另外一个事务对数据进行了修改,这时候两次读取的数据是不一致的。
幻读:第一个事务对一定范围的数据进行批量修改,第二个事务在这个范围增加一条数据,这时候第一个事务就会丢失对新增数据的修改。
事务嵌套:如Service A 的method A()调用Service B的method B(),就有如下四种方案:
PROPAGATION_REQUIRED:(spring 默认) 如果hodB() 的事务级别定义为 PROPAGATION_REQUIRED,那么执行 hodA() 的时候spring已经起了事务,
这时调用 hodB(),hodB() 看到自己已经运行在 hodA() 的事务内部,就不再起新的事务。 假如 hodB() 运行的时候发现自己没有在事务中,他就会为自己分配一个事务。 这样,在 hodA() 或者在 hodB() 内的任何地方出现异常,事务都会被回滚。
PROPAGATION_REQUIRES_NEW:比如我们设计hodA()的事务级别为PROPAGATION_REQUIRED,hodB() 的事务级别为 PROPAGATION_REQUIRES_NEW。那么当执行到 hodB() 的时候,hodA() 所在的事务就会挂起,hodB() 会起一个新的事务,等待 hodB() 的事务完成以后,它才继续执行。他与 PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为 hodB() 是新起一个事务,那么就是存在两个不同的事务。如果 hodB() 已经提交,那么 hodA() 失败回滚,hodB() 是不会回滚的。如果 hodB() 失败回滚,如果他抛出的异常被 hodA() 捕获,hodA() 事务仍然可能提交(主要看B抛出的异常是不是A会回滚的异常)。
PROPAGATION_SUPPORTS:假设hodB() 的事务级别为 PROPAGATION_SUPPORTS,那么当执行到hodB()时,如果发现hodA()已经开启了一个事务,则加入当前的事务,如果发现hodA()没有开启事务,则自己也不开启事务。这种时候,内部方法的事务性完全依赖于最外层的事务。
PROPAGATION_NESTED:现在的情况就变得比较复杂了, hodB()的事务属性被配置为 PROPAGATION_NESTED, 此时两者之间又将如何协作呢? hodB如果rollback, 那么内部事务(即 hodB) 将回滚到它执行前的 SavePoint 而外部事务(即 ServiceA#methodA) 可以有以下两种处理方式:
a、捕获异常,执行异常分支逻辑
void methodA() { try { hodB(); } catch (SomeException) { // 执行其他业务, 如 hodC(); } }
这种方式也是嵌套事务最有价值的地方, 它起到了分支执行的效果, 如果 hodB
失败, 那么执行 hodC(), 而 hodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点。
b、外部事务回滚/提交 代码不做任何修改, 那么如果内部事务(hodB) rollback, 那么首先 hodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此), 外部事务(即 hodA) 将根据具体的配置决定自己是 commit 还是 rollback

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。