数据库之事务管理

本篇介绍数据库的事务管理,内容皆是笔者学习《数据库系统概念》总结摘抄而来,仅作学习笔记。

事务概念

事务是访问并可能更新各种数据项的一个程序执行单元。通常由高级数据操纵语言或编程语言通过JDBC或ODBC嵌入式数据库访问书写的用户程序的执行所引起。事务用形如begin transaction和end transaction语句来界定,由这两个语句之间执行的全体操作完组成。

事务具有原子性(atomicity)。如果一个事务开始执行,但由于某些原因失败,则事务对数据库造成的任何可能的修改都要撤销。无论事务本身是否失败,或者操作系统崩溃,或者计算机本身停止运行,这项要求都成立。事务是不可分割的,要么执行其全部内容,要么就根本不执行。

事务具有隔离性(isolation)。数据库系统必须才去特殊处理来确保事务正常执行而不被来自并发执行的数据库语句所干扰。

事务具有持久性(durability)。即使系统崩溃,事务的操作也必须是持久的。

事务具有一致性(consistency)。如果一个事务作为原子从一个一致的数据库状态开始独立的运行,则事务结束时数据库也必须再次是一致的。

将以上内容更简明的重新描述如下:

  • 原子性:事务的所有操作在数据库中要么全部正确反映出来,要么完全不反映。
  • 一致性:隔离执行事务时保持数据库的一致性。
  • 隔离性:尽管多个事务可能并发执行,但系统保证,对于任何一对事务T1和T2,在T1看来,T2或者在T1开始之前已经完成执行,或者在T1完成之后开始执行。每个事务都感觉不到系统中有其他事务在并发的执行。
  • 持久性:一个事务成功完成后,它对数据库的改变必须是永久的,即使出现系统故障。

这些性质统称为ACID特性。

我们将采用一个由几个账户和一个访问和更新账户的事务集合构成的简单的银行应用来阐明事务的概念。事务运用以下两个操作访问数据:

  • read(x):从数据库把数据项x传送到执行read操作的事务的主存缓冲区的一个也称为x的变量中。
  • write(x):从执行write的事务的主存缓冲区的变量x中把数据项x传回数据库中。

设t是从账户A过户100块到账户B的事务,该事务可以定义为:

read(A);
SET A = A-50;
write(A);
read(B);
SET B = B+50;
write(B);

以此事务为例,逐个讲解ACID特性。

  • 一致性:在此处一致性指的是事务的执行不改变账户A和B的余额之和。如果没有一致性要求,金额可能被事务凭空创造或销毁。
  • 原子性:假设事务t执行前账户A余额为1000元,账户B余额为2000元,事务t在执行时系统出现故障,导致事务t没有成功完成,我们进一步假设故障发生在write(A)之后write(B)之前。这种情况下,数据库反映出来的账户A余额为900元,账户B有2000元。这种由于故障,系统的状态不再反映数据库本应描述的现实世界的真实状态。把这种状态称为不一致状态。我们必须保证这种不一致性在数据库系统中是不可见的。

        保证原子性的基本思路如下:对于事务要执行写操作的数据项,数据库系统在日志文件中记录其旧值。如果事务没能成功执行,数据库系统从日志中回复旧值,使得看上去事务从未执行过。这项工作是由称作恢复系统的一个数据库组件处理。

  • 持久性:一旦事务成功完成执行,该事务对数据库做的所有更新就都是持久的。假设计算机系统的故障导致内存中的数据丢失,但已写入磁盘的数据不会丢失。可以确保以下两条中的任意一条来达到持久性:
  1. 事务做的更新在事务结束前已经写入磁盘;
  2. 有关事务已执行的更新信息已写到磁盘,且能让数据库在系统重新启动后重新构造更新。

上面提到的恢复系统除了保证原子性之外还保证持久性。

  • 隔离性:如果几个事务并发的执行,即使每个事务都能确保一致性和原子性,它们的操作会以人们所不希望的某种方式交叉执行,这也会导致不一致。例如在事务t执行过程中,账户A的金额减去转账金额并写回A,而账户B的金额还未写回B时,数据库暂时是不一致的。如果此时另一个事务在这个中间时刻读取A和B的值并计算,将会得到不一致的值。事务的隔离性确保事务并发执行后的系统状态与这些事务以某种次序一个接一个的执行后的状态是等价的。数据库系统中的并发控制系统确保隔离性。

事务原子性和持久性

事务并非总能成功的执行完成,称为事务中止(aborted)了。中止事务对数据库所做过的任何改变必须撤销以保证原子性,一旦中止事务造成的变更被撤销,我们就说事务已回滚(rolled back)。

成功完成执行的事务称为已提交(commited)。一个对数据库进行过更新的已提交事务使数据库进入一个新的一致状态。

一旦事务已提交,我们不能通过中止它来撤销其造成的影响。撤销已提交事务所造成影响的唯一方法时执行一个补偿事务(compensating transaction)。例如如果一个事务给账户A增加了20块,补偿事务是从该账户中减去20块。

我们需要更准确的定义一个事务成功完成的含义,为此我们建立了一个简单的抽象事务模型,事务必须出于以下状态之一:

  • 活动的(active):初试状态,事务执行时就处于这个状态。
  • 部分提交的(partially committed):最后一条语句执行后。
  • 失败的(failed):发现正常的执行不能继续后。
  • 中止的(aborted):事务回滚并且数据库已恢复到事务开始执行前的状态后。
  • 提交的(committed):成功完成后。

只有在事务进入提交状态,我们才说事务已提交。类似的,当事务进入中止状态后,我们才说事务已中止。如果事务是提交的或者中止的,它称为已结束的(terminated)。事务相应的状态图如下:

数据库之事务管理_第1张图片

事务从活动状态开始,当完成它的最后一条语句时就进入了部分提交状态,此刻事务已经完成执行。当数据库系统向磁盘上写入足够的信息,确保即使出现故障事务所做的更新也能在系统重启后重新创建,事务就进入了提交状态。但在部分提交状态的事务也可能由于实际输出可能临时驻留在主存中,因此一个硬件故障可能阻止其成功完成,于是事务可能必须中止。

系统判定事务不能继续正常执行后,事务就进入失败状态。这种事务必须回滚。事务就进入中止状态。此刻,系统有两种选择:

  1. 重启事务:仅当引起事务中止的是硬件错误而不是由事务的内部逻辑所产生的软件错误,重启的事务被看成是一个新事务。
  2. 杀死事务:这样做通常是由于事务的内部逻辑造成的错误,只有重写应用程序才能改正,或者由于输入错误等等。

事务的并发问题

事务并发可能会导致多种问题,这些问题可以归结为两类,分别为数据读问题和数据更新问题。数据读问题包括脏读、不可重复读和幻读。数据更新问题包括第一类丢失更新和第二类丢失更新。

脏读

脏读是指事务A读到了事务B未提交的事务。

例如事务A是一个转账事务,事务B将此账户金额修改为500元但没有提交事务,此时事务A读取账户余额500元准备转账500元,此时事务B因为一些原因回滚了事务,此时账户余额为0,在事务A中就会发生明明余额有500元却转账失败的问题。此例子中事务A读到的账户余额就是一个脏数据。

不可重复读

不可重复读是指在事务A内多次读取的数据不一致,原因是在多次读取期间事务B修改了数据并提交了事务。

例如事务A读取到了账户余额为1000元,然后事务A去做其他事情,此时事务B修改账户余额为500元并提交了事务,事务A再次读取账户余额为500元。因此称为不可重复读。

幻读

幻读是指在事务A内多次读取的数据不一致,原因是在多次读取期间事务B增加或删除了数据并提交了事务。

例如事务A读取到公司员工数量为100个,然后事务A去做其他事情,此时事务B新增加了一个叫赵彦祖的员工并提交了事务,事务A再次读取到的员工数量为101个。

第一类丢失更新

第一类丢失更新是指事务A在撤销时把事务B所做的更新覆盖了。

例如事务A在转账前查询账户余额为1000元,此时事务B向账户中转了500元并提交了事务,然后事务A转账100元,发现转错了账,回滚事务又将账户金额改为1000元。事务B向账户中转的500元丢失了。

第二类丢失更新

第二类丢失更新是指事务A在提交事务时覆盖了事务B的更新。

例如事务A在转账前查询账户余额为1000元,此时事务B向账户中转了500元并提交了事务,然后事务A转账100元,提交事务将账户余额改为900元。事务B向账户中转的500元丢失了。

事务的隔离级别

MySQL数据库有四种隔离级别,分别为Read uncommitted、Read committed、Repeatable read和Serializable。这四种隔离级别随着级别的递增可以解决更多的上面提到的并发问题。

Read uncommitted

读未提交,一个事务可以读取到另一个事务未提交的数据。数据库处于此级别,当一个事务写数据时,不允许另一个事务执行写操作,但其他事务可以读取此数据。这个级别防止了丢失更新问题,但无法防止读取问题。

Read committed

读已提交,一个事务不能读到另一个事务未提交的数据。数据库处于此级别,当一个事务写数据时,不允许其他事务执行读写操作。当一个事务读数据时,其他事务可以执行读写操作。这个级别防止了丢失更新和脏读,但无法防止不可重复读和幻读。

Repeatable read

可重复读取,在一个事务内多次读取一个数据都应是相同的。当一个事务写数据时,其他事务不能执行读写操作。当一个事务读数据时,其他事务不能执行写操作。这个级别防止了丢失更新、脏读和不可重复读,但无法防止幻读。

Serializable

可序化,要求事务序列化执行,不能并发执行,为隔离最高级别。由于不能并发执行,性能最低。防止了丢失更新、脏读、不可重复读和幻读(都不能并发执行了,并发引起的问题当然不会存在了)。

你可能感兴趣的:(数据库)