数据库事务

四大特性

原子性

  表示组成一个事务的多个数据库操作是一个不可分割的原子单元,只有所有###操作执行成功,整个事务才提交。事务中的任何一个数据库操作失败,已经执行的任何操作都必须撤销,让数据库返回到初始状态。

一致性

  事务操作成功后,数据库所处的状态和它的业务规则是一致的,即数据库不会破坏。如从A账户转账100元到B账户,不管操作成功与否,A账户和B账户的存款总额是不变的。

隔离性

  在并发数据操作时,不同的事务拥有各自的数据空间,它们的操作不会对对方产生干扰。准确地说,并非要求做到完全无干扰,数据库规定了多种事务的隔离级别,不同的隔离级别对应不同的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱。

持久性

  一旦事务提交成功后,事务中所有的数据操作都必须被持久化到数据库中。即使在提交事务后,数据库马上崩溃,在数据库重启时,也必须保证能够通过某种机制恢复数据。

数据库并发问题

  一个数据库可能拥有多个访问客户端,这些客户端都可用并发的方式访问数据库。数据库中的相同数据可能同时被多个事务访问,如果没有采取必要的隔离措施,就会导致各种并发问题,破坏数据的完整性。这些问题可用归结为5类。

脏读(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数据库中,不会发生脏读。

不可重复读(unrepeatable read)

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

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

幻想读(phantom read)

  A事务读取B事务提交的新增数据,这时A事务将出现幻象读现象。幻象读一般发生在计算统计数据的事务中。

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

  如果新增的数据刚好满足事务的查询条件,那么这个新数据就进入了事务的视野,因此产生了两次统计结果不一致的情况。
  幻想读和不可重复读是两个容易混淆的概念,前者是指读到了其他已经提交事务的新增数据,而后者是指读到了已经提交事务的更改数据(更改或删除)。

第一类丢失更新(lost update)

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

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

A事务在撤销时,“不小心”将B事务的转入金额抹除了。

第二类丢失更新(second lost update)

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

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

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

数据库锁机制

  数据库b并发会引发很多问题,在一些场合下有些问题是允许的,但在另一些场合下可能是致命的。数据库通过锁机制解决并发访问的问题。虽然不同的数据库在实现细节上存在差别,但原理基本上是一致的。

  • 行共享锁定
  • 行独占锁定
  • 表共享锁定
  • 表共享独占锁定
  • 表独占锁定

事务隔离级别

​  尽管数据库为用户提供了锁的DML操作方式,但直接使用锁管理是非常麻烦的,因此数据库为用户提供了自动锁的机制。只要用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,然后自动为事务操作的数据库资源添加合适的锁。
  ANSI/ISO SQL92标准定义了4个等级的事务隔离级别,在相同的数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可能导致不同的结果。

  • 事务隔离级别分类
隔离级别 脏读 不可重复读 幻象读 第一类丢失更新 第二类丢失更新
READ UNCOMMITED 允许 允许 允许 不允许 允许
READ COMMITTED 不允许 允许 允许 不允许 允许
REPEATABLE READ 不允许 不允许 允许 不允许 不允许
SERIALIZABLE 不允许 不允许 不允许 不允许 不允许

  SQL 92推荐使用REPEATABLE READ以保证数据的读一致性,不过用户可以根据应用的需要选择适合的隔离等级。