数据库的并发问题

1.脏读(dirty read)

   A事务读取了B事务尚未提交的更改数据,并且在这个数据基础上进行操作。如果此时恰巧B事务进行回滚,那么A事务读到的数据是根本不被承认的。
以下是一个取款事务和转账事务并发时引起的脏读场景。

时间 转账事务A 取款事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 取出500元,把余额改为500元
T5 查询账户余额为500元(脏读)
T6 撤销事务,余额恢复为1000元
T7 汇入100元,余额改为600元
T8 提交事务

   在这个场景中,B希望取款500元,而后有撤销了动作,而A往同一个账户转账100元,因为A事务读取了B事务尚未提交的数据,因而导致了账户白白丢失了500元。在Oracle数据中,不会发生脏读的情况。

2.不可重复读(unrepeatable read)

   不可重复读是指A事务读取了B事务已经提交的更改数据。假设A在取款事务的过程中,B往该账户转账100元,A两次读取账户的余额发生不一致

时间 取款事务A 转账事务B
T1 开始事务
T2 开始事务
T3 查询账户余额为1000元
T4 查询账户余额为1000元
T5 取出100元,把余额改为900元
T6 提交事务
T7 查询账户余额为900元

   在同一个事务中T4和T7时间点读取的账户存款余额不一致

3.幻象读(phantom read)

    A事务读取B提交的新增数据,这时A事务将出现幻想读的问题。幻读一般发生在计算统计数据的事务中。举个例子,假设银行系统在同一个事务中两次统计存款的总金额,在两次统计过程中,刚好新增了一个存款账户,并存入100元,这时两次统计的总金额将不一致。

时间 统计金额事务A 转账事务B
T1 开始事务
T2 开始事务
T3 统计存款总金额为10000元
T4 新增一个存款账户,存款为100元
T5 提交事务
T6 再次统计存款总金额为10100元(幻象读)

    如果新增的数据刚好满足事务的查询条件,那么这个新数据就会进入事务的视野,因而导致两次统计结果不一致的情况。
   幻读和不可重复读是两个容易混淆的概念,前者是指读到了其他事物已经提交的新增数据,而后者是读到了已经提交事务的更改数据(更改或删除)。为了避免这两种情况,采取的策略是不同的:防止读到更改数据,只需对操作的数据添加行级锁,阻止操作过程中的数据发送变化,而防止读到新增数据,则往往需要添加一个表级锁–将整张表锁定,防止新增数据(Oracle使用多版本数据的方式实现)

4.第一类丢失更新

    A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错可能会造成很严重的问题。通过下面的账号取款转账就可以看出来。

时间 取款事务A 转账事务B
T1 开始事务
T2 开始事务
T3 查询账号余额为1000元
T4 查询余额为1000元
T5 汇入100元,把余额改为1100元
T6 提交事务
T7 取出100元,把余额改为900元
T8 撤销事务
T9 余额恢复为1000元(丢失更新)

    A事务在撤销时,“不小心”将B事务已经转入账号的金额给抹去了。

5.第二类丢失更新

    A事务覆盖B事务已经提交的数据,造成B事务所操作丢失。

时间 转账事务A 取款事务B
T1 开始事务
T2 开始事务
T3 查询账号余额为1000元
T4 查询余额为1000元
T5 取出100元,把余额改为900元
T6 提交事务
T7 汇入100元,把余额改为1100元
T8 提交事务
T9 把余额改为1100元(丢失更新)

在上面的例子,由于支票转账事务覆盖了取款事务对存款余额所做的更新,导致银行最后损失了100元,相反如果转账事务先提交,那么用户损失了100元。

数据库的锁

insert, update, delete, select for update 语句会隐式采用必要的锁定。
1.行共享锁: SELECT FOR UPDATE 语句隐式获取行共享锁
2.行独占锁: INSERT,UPDATE,DELETE 语句隐式获取,或者通过LOCK TABLE IN ROW EXCLUSIVE MODE 获取行独占锁
3.表共享锁: LOCK TABLE IN SHARE MODE 获取,防止其他独占锁获取,但是允许在表内拥有多个行共享锁和表共享锁
4.表共享行独占锁:LOCK TABLE IN SHARE ROW EXCLUSIVE MODE 获取
5.表独占锁 : LOCK TABLE IN EXCLUSIVE MODE

Spring 事务

不能被spring AOP 事务增强的方法

动态代理策略 不能被事务增强的方法
基于接口的动态代理(JDK) 除了public方法均不能,此外public static 也不能增强
CGLib 动态代理 private,static ,final 方法

   对于private方法,由于最终会被public方法封装后再开放给外部调用者,而public方法是可以事务增强的,所以基本没有什么问题。在实际开发中,最容易造成隐患的基于CGlib代理的 public static 和 public final 方法。原因是它们本身是public的,因此可以直接被外部调用,只要调用方没有事务上下文,这些方法就会以无事务的方式运行。

你可能感兴趣的:(数据库的并发问题)