事务是什么?
事务:指单个逻辑操作单元的集合。
在操作数据库时(增删改),如果同时操作多次数据,我们从业务希望,要么全部成功,要么全部失败。这种情况称为事务处理。
例如:A转账给B。
第一步,扣除A君账号要转的金额。
第二步,增加B君账号的金额。
这两个步骤,要么都成功,要么都失败,这就是事务。
Spring事务控制我们要明确的
1.JavaEE体系进行分层开发,事务处理位于业务层,所以,一般情况下我们使用的事务代理(事务管理器)一般放在分层设计的业务层。
2.spring框架为我们提供了一组事务控制的API。
3.spring的事务控制都是基于AOP的,它既可以使用编程的方式实现(编程式事务),也可以使用配置的方式实现(声明式事务),声明式事务又可分为XML配置和注解配置两种。
案例引出问题
需求:从ID为10086账户给ID为10010账户转账1000元钱。
数据准备:account表(账户):
CREATE TABLE `t_account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`balance` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10087 DEFAULT CHARSET=utf8;
编写转账案例
转账案例分析
结论:根据上述案例分析,事务管理应该是在Service层处理。
数据库并发问题
什么是数据库并发问题?
并发:多个客户端同时访问数据库中某一条数据(秒杀)。
数据库可以拥有多个访问客户端,若多个客户端并发地访问数据库中相同的资源,如果没有采取必要的隔离措施,则会导致各种并发问题,破坏数据的完整性、一致性。
这些问题归结为5类:
包括3类数据读问题(脏读,不可重复读,幻读)
和2类数据更新问题(第一类丢失更新,第二类丢失更新)。 看图
1.5.1. 第一类丢失更新
两个事务更新相同数据,如果一个事务提交,另一个事务回滚,第一个事务的更新会被回滚。
1.5.2. 第二类丢失更新
多个事务同时读取相同数据,并完成各自的事务提交,导致最后一个事务提交会覆盖前面所有事务对数据的改变。
1.5.3. 脏读
第二个事务查询到第一个事务未提交的更新数据,第二个事务根据该数据执行后续的操作,但第一个事务回滚,第二个事务操作的就是脏数据。
1.5.4. 幻读
一个事务查询到了另一个事务已经提交的新数据,导致多次查询数据不一致
1.5.5. 不可重复读
一个事务查询到另一个事务已经修改的数据,导致多次查询数据不一致
1.5.6. 小结
【1】并发事务带来哪些问题?
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
- 脏读: 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
- 幻读: 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
- 不可重复读: 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
【2】不可重复读和幻读区别
不可重复读的重点是修改,比如多次读取一条记录发现其中某些列的值被修改。
幻读的重点在于新增或者删除,比如多次进行条件查询发现记录增多或减少了。
数据库事务的隔离级别
问题:上述问题理论上如果出现了应该如何解决?
答:一般情况,数据库都会处理一些事务并发的问题,数据库提供了不同的事务隔离级别来处理不同的事务并发问题,事务隔离级别定义如下:
针对不同隔离级别可以解决的的如下五类问题
解决丢失更新的方案
数据库表数据加锁
1,悲观锁
(1) 在操作当前数据的事务开启事务就使用for update
锁住当前数据。
(2) Hibernate和MyBatis都有悲观锁对应的解决方案。
2,乐观锁
(1) 为表添加一个version字段。当前事务操作的时候都会比对当前事务的多次操作的版本号是否一致,如果不一致认为数据已经被更新,事务进行回滚。
(2) Hibernate和MyBatis都有乐观锁对应的解决方案。