数据库基础知识——事务与恢复系统

事务

什么是事务?
是由多个操作封装起来的一个执行单元,这个执行单元内的操作要么都做,要么都不做

事务的ACID特性

  • 原子性(Atom) 事务内的操作要么都做,要么都不做。
  • 一致性(Consistency) 事务执行后应保证数据库的一致状态,一致性保证事务的执行会将数据从一个正确状态变为另一个用户状态。
  • 隔离性(Isolated) 事务的执行过程中不会被其它事务所影响。
  • 持久性(Duration) 事务执行完后,对数据的改变是永久的。

事务并发执行时会产生的问题

  • 脏读 事务A读取了另一个事务未提交的数据

    事务A 事务B
    began transaction began transaction
    金额=200+100
    - 读取金额=300
    金额=300-100 commit
    commit
    结果 金额=200 结果金额=300
  • 不可重复读 事务重复读取了另一个事务已提交修改的数据(同一事物内两次读取的数据不一致)

    事务A 事务B
    began transaction began transaction
    - 读取金额=200
    金额=200+100
    commit
    - 读取金额=300
    - commit
    结果 金额=300 结果金额=300
  • 幻读 事务重复读取了另一事务已插入或删除的数据

    事务A 事务B
    began transaction began transaction
    - 读取行数=200
    删除一行
    commit
    - 读取行数=199

数据库为了避免以上问题的发生,有了事务的隔离级别的设置,通过不同级别的隔离设置可以避免不同的问题,事务的隔离是通过锁来实现的,下面介绍一下数据库中锁的知识

数据库中的锁机制

下面是数据库中各种锁及他们之间的关系(来自:数据库的锁机制及原理):
数据库基础知识——事务与恢复系统

  • 悲观锁和乐观锁
    • 乐观锁假定事务的执行不会出现问题,每次拿数据的时候不会去上锁,拿到数据后才会判断数据是否修改了,如果修改了就让用户自己去处理。适用于读操作比较多的场景
    • 悲观锁假定事务的执行会出现问题,因此每进行一个操作都会对数据进行加锁,传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。

    乐观锁和悲观锁的实现:面试必备之乐观锁与悲观锁

  • 意向锁 当有一个事务A获得了某行记录的读锁时,若另一个事物可以获得表的写锁,那么他就可以修改这个表内的所有行,这时事务A加的锁就没有用了。为了解决这个问题,加入了意向锁,即事务为某行数据添加行锁时会为整个表添加一个意向锁,当另一个事务想为这个表加表锁时,必须保证这个表没有意向锁。
  • 共享锁(读锁) 加锁后其它事务只能读不能写
  • 排他锁(写锁) 加锁后其它事务不能读和写
  • 更新锁 更新数据包含两个操作,读数据和写数据,如果用先加共享锁再加写锁的方法,可能会导致死锁的产生,因此引入了更新锁。更新锁再读的阶段时共享锁,在写的阶段自动变为排他的。

事务的隔离级别

  • 读不提交 不做任何检查,可能导致脏读。
  • 读已提交 加一个写锁,这样可以避免脏读,但会导致不可重复读
  • 可重复读(MySql的默认事务隔离级别) 读的操作加读锁,写的操作加写锁,可以避免不可重复读,但会导致幻读。
  • 串行化 将事务进行强行的排序,从而避免并行导致的脏读、不可重复读、幻读的问题。

恢复系统

故障分类

  • 事务故障 事务在运行过程中由于种种原因,如输入数据的错误、运算溢出、违反了某些完整性限制、某些应用程序的错误,以及并发事务发生死锁等,使事务未运行至正常终止点就被撤销,这种情况称为“事务故障”。
  • 系统故障 系统故障是指系统在运行过程中,由于某种原因(如操作系统或数据库管理系统代码错误、操作员操作失误、特定类型的硬件错误(如 CPU 故障)、突然停电等造成系统停止运行),致使事务在执行过程中以非正常方式终止,这时内存中的信息丢失,但存储在外存储设备上的数据不会受影响。
  • 介质故障 统在运行过程中,由于某种硬件故障,如磁盘损坏、磁头碰撞或由于操作系统的某种潜在的错误、瞬时强磁场干扰,使存储在外存上的数据部分损失或全部损失,称为“介质故障”。这类故障比前两类故障的可能性虽然小得多,但破坏性却最大。
  • 计算机病毒

日志系统

故障恢复策略

  • 事务故障的恢复
    事务故障是指事务未运行至正常终止点前被撤销,这时恢复子系统应对此事务做撤销处理。事务故障的恢复是由系统自动完成的,不需要用户干预,步骤如下:
    • 反向扫描日志文件,查找事务的更新操作
    • 对事务的更新操作进行逆操作
    • 继续反向扫描日志文件
    • 处理到事务的开始标记
  • 系统故障的恢复
    系统故障发生时,造成数据库不一致状态的原因有两个:一是由于一些未完成事务对数据库的更新已写入数据库;二是由于一些已提交事务对数据库的更新还留在缓冲区没来得及写入数据库。系统故障的恢复是在重新启动时自动完成的,不需要用户干预,步骤如下:
    • 正向扫描日志文件,找出在故障发生前已经提交的事务,将其事务标识记入重做(Redo)队列。同时找出故障发生时尚未完成的事务,将其事务标识记入撤销(Undo)队列。
    • 反向扫描日志文件,对撤销队列中的各个事务进行撤销处理:对每个 Undo 事务的更新操作执行逆操作。
    • 正向扫描日志文件,对重做队列中的各个事务进行重做处理:对每个 Redo 事务重新执行日志文件登记的操作。

从头开始扫描日志文件太慢,由此提出了基于检查点的日志恢复系统。

基于检查点的日志恢复系统

  • 基于检查点的日志系统恢复步骤
    • 在重新开始文件中找到最近一次日志中检查点的位置,并通过检查点找到相应的检查点记录
    • 由检查点记录找到检查点建立时的事务清单
    • 建立REDO队列和UNDO队列,将找到的事务清单内的事务放入REDO队列
    • 由检查点正向扫描日志,碰到开始事务的标记就将事务放入UNDO队列,碰到提交事务的标记就将相应的事务放回REDO队列
    • 重做REDO队列中的事务,撤销UNDO队列中的事务