数据库事务 - Transaction

时间:2021-02-09 16:20:24

一. 定义

Transaction 是用户定义的一系列操作,要么完全执行,要么完全不执行。是数据库的一个逻辑工作单位,由DBMS的事务子系统处理。

3条相关语句:

  • BEGIN TRANSACTION
  • COMMIT
  • ROLLBACK

二. 目的

一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:

  1. 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
  2. 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。

事务存在的目的是:提供一个从失败恢复的机制,和一个并发隔离方法。

当事务被提交给了DBMS,则DBMS需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要被回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。

但在现实情况下,失败的风险很高。
在一个数据库事务的执行过程中,有可能会遇上事务操作失败、数据库系统/操作系统失败,甚至是存储介质失败等情况。这便需要DBMS对一个执行失败的事务执行恢复操作,将其数据库状态恢复到一致状态(数据的一致性得到保证的状态)。为了实现将数据库状态恢复到一致状态的功能,DBMS通常需要维护事务日志以追踪事务中所有影响数据库数据的操作。

三. ACID

1. ACID是指事务的特性
并非任意的对数据库的操作序列都是数据库事务。数据库事务拥有以下四个特性,习惯上被称之为ACID特性。

  1. Atomicity - 原子性:事务本身必须是原子工作单位,事务的操作要么全部成功,要么全部失败。

  2. Consistency 一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。

    一致性的含义是:数据库中的数据应满足完整性约束,相当于数据库只包含成功事务提交的结果。

  3. Isolation 隔离性:隔离性对并发执行而言,一个事务的执行不能被其他事务干扰。一个事务内部的操作及使用的数据对其他并发事务是隔离的。并发执行的各个事务之间不能互相干扰。

  4. Durability持续性:事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。(也称为:Permanence)


四. 事务隔离 - 并发控制

1. 事务隔离:
Isolation - 隔离性,几乎是ACID中最值得注意的特性了。因为真实环境中一定有并发,所以一定有并发情况下的隔离性级别控制。

2. 并发控制 - Concurrency control:
两段锁是关系数据库中最常见的并发控制机制。

3. 隔离级别 - Isolation levels
√: 可能出现 ×: 不会出现

- 脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable read × ×
Serializable × × ×
  1. Read uncommitted:性能第1,但会脏读,也就是说经常读出错误数据;

  2. Read committed(default):只读被commit的数据,不会脏读,但不保证重新读取时能读到相同数据。

  3. Repeatable read:可以重复读,事务必须同时拥有read lock和write lock,直到事务完成才释放。在读的行上保证不能被其它事务修改这些行。

  4. Serializable:序列化,不仅有read、write锁还有range lock范围锁(没有where锁全表,有where锁where范围);对一张表的所有增删改操作必须顺序执行,性能最差;

4. 并发操作带来的数据不一致性包括
一个修改问题(L):

  1. 丢失修改 - lost update:当两个或多个事务选择同一行,基于其最初选定的值进行更新数据时,会发生丢失修改问题。最后的更新将重写由其它事务所做的更新。

    由于RDBMS都有锁机制,所以在并发事务中Lost update不存在


三个读问题(DNP):
  1. 脏读 - dirty reads:事务T2读取了事务T1已经修改但还未commit的数据,事务T1回滚了数据;

    默认隔离级别read committed下也不会发生

  2. 不可重复读 - non-repeatable reads:(事务T1在一次事务中两次读取同一行数据)事务T1读取数据后,事务T2对数据进行了更新,当T1再次读取数据后,得到与前一次不同的值;

    通常这个实际中应该允许。默认隔离级别下可能发生,启用repeatable read可以防止。

  3. 幻影读 - phantom read:(事务T1在一次事务中两次读取同一行数据)事务T1读取了某些(多条记录)记录,事务T2删除/插入了一些记录,当T1再次读相同条件的记录时,发现少了一些记录/多了一些记录。

    通常这个实际中应该允许。你不能老锁表啊,实在有需求就设置Serializable吧。

白话再翻译一遍:

  1. lost update:事务T1和T2同事修改相同的数据,T1先提交T2后提交,T2覆盖了T1的结果;

  2. dirty reads:(T1读1次)事务T1读取了事务T2更新但未提交的数据,最终T2回滚了数据;

  3. non-repeatable reads:(T1读2次,记录中的具体数据不同)事务T1先读取数据,事务T2更新了数据并提交,事务T1再次读取数据,结果与第一次读到的不同;

  4. 幻影读 reads:(T1读2次,记录的数量不同)事务T1按条件读取一定记录,事务T2删除/插入了一些记录,T1再次读取记录,结果少了/多了一些记录;