数据库系统原理

[TOC]

数据库系统原理

事务

事务指满足ACID特性的一组操作,可以通过commit 提交,也可以通过rollback回滚

事务的ACID特性

  1. 原子性(Atomicity)

    事务被视为不可分割的最小单元,事务的所有操作要么全部成功提交,要么全部失败回滚,回归可以使用回滚日志来实现,回滚事务记录着执行的修改操作,回滚时反向执行即可

  2. 一致性(Consistency)

    数据在事务的执行前后都保持一致性状态,在一致性状态下,所有事务对一个数据的读取结构都是相同的

  3. 隔离性(Isolation)

    一个事务所做的修改在最终提交以前对其他事务不可见

  4. 持久性(Durability)

    一旦事务提交,则其所做修改会永远保存到数据库中,即使系统崩溃事务执行结果也不能丢失,使用重做日志来保障持久性

事务的ACID特性比较简单,但是并不容易理解,他们并不平级

  • 只有满足一致性,事务的执行结果才是正确的的
  • 在无并发的情况下,事务串行执行,隔离性一定能满足。此时只要满足原子性,一致性即可满足
  • 并发的情况下,多个事务并行执行,事务不仅满足原子性,还要满足隔离性 才能满足一致性
  • 事务满足持久性是为了维护数据库崩溃的情况

事务特性

事务的自动提交 AUTO_COMMIT

MySQL默认采用自动的提交模式,即,如果不显示使用START TRANSACTION来开始一个事务,每个查询都会被当做一个事务自动提交

事务的模型抽象

成功完成的事务称为已提交,而非成功完成的事务被中止了,为了确保数据的原子性,中止事务对数据库状态不可以造成影响,因此这些影响必须被撤销。一旦中止事务的影响被撤销,我们称事务已回滚 ,数据库通过日志来支持回滚操作

一个简单事务抽象模型包括:

  • 活动的:初始状态,事务执行时处于这个状态
  • 部分提交的:最后一条语句执行后
  • 失败的:发现正常的执行不能继续后
  • 中止的:事务回滚并恢复到事务开始执行的前后
  • 提交的:成功完成后

事务

事务的隔离性

事务处理系统通常允许多个事务并发的执行,并发有两条无法拒绝的理由

  • 提高吞吐量和资源利用率
  • 减少等待时间

数据库中并发的动机和多道程序设计的动机是相同的,而为提升效率的同时,我们必须控制事务的交互,来保证数据库的一致性,这被称为系统的并发控制

在并发执行时,通过保证所执行的任何调度效果都与没有并发效果一样,我们可以保持数据库的一致性。调度在某种意义上等价于一个串行调度,这种调度被称为可串行化调度

事务的并发

可串行化

串行化顺序可以通过拓扑排序得到。

对于优先图来说,读写、写读、写写被称为一条边,考察这个有向图中是否有环,无环的优先图被称为可串行化调度

并发一致性问题

在并发环境下,事务的隔离性很难保证,因此可能出现并发一致性问题

问题 原因 图例
丢失修改 T1和T2 两个事务都对一个事务进行修改,T1先修改T2随后修改,T2的修改覆盖了T1的修改 事务
读脏数据 T1修改了一个数据,T2最后读取了这个数据。如果T1撤销了这次修改,那么T2读取的数据是脏数据 事务
不可重复读 T2读取了一个数据,T1对其进行了修改,如果T2再次读取这个数据,此时读取结果和第一次不同 事务
幻影读 T1读取某个范围内的数据,T2 在这个范围内插入新的数据,T1再次读取这个范围内的数据,此时读取的结果和第一次读取的不同 事务

事务的隔离级别

隔离级别 简介
可串行化 即可串行化调度
可重复读 只允许读取已提交数据,事务两次读取的间隙其他事务不得更新
已提交读 只允许读取已提交的数据,允许同一事务读数据的前后不一致
未提交读 允许读取未提交数据

事务的隔离级别与对应的并发问题

隔离级别/并发问题 脏读 不可重复读 幻影读 加锁读
未提交读 ×
提交读 × ×
可重复读 × × ×
可串行化 × × ×

隔离级别的实现

通过封锁来实保证事务的可串行化。通过共享、排他锁及两阶段封锁协议来保证串行化下的并发读

时间戳

另一类用来实现隔离性的技术为为每一个事务分配一个时间戳,系统维护两个时间戳来保证冲突情况下按照顺序访问数据项

多版本和快照隔离

快照隔离中,我们可以想象每个事务开始时尤其自身的数据库版本或快照,它从这个私有的版本中读取数据,因此它和其他事务的更新隔开。事务的更新只在私有数据库中进行,只有提交时才将信息保存,写入数据库。

并发控制

读写锁

锁一般被分为两种

  • 共享锁:简称为S锁,又称为读锁
  • 排他锁:简称为X锁,又称为写锁

这两种锁有以下规定

  • 一个事务数据对象加了X锁,就可以对数据A进行读取和更新,加锁期间其他事务不能获得A的锁
  • 一个事务数据对象加了S锁,可以对A进行读取操作,加锁期间其他事务可以对其加S锁,但是不能加X锁

意向锁

使用意向锁来支持多粒度的封锁

在行级锁、表级锁的情况下,事务想要对表A加X锁,就要检测其他事务是否对表A和表A的任意一行加了锁,那么就需要对A的每一行都检测,这非常耗时

在X/S锁之外引入了IX、IS,IX和IS都是表锁,用来表示一个事务想在某个表上加X或S锁,有以下规定:

  • 一个事务在获得某个数据行的S锁之前,必须先获得表的IS锁或更强的锁
  • 一个数据在获得某个数据航的X锁之前,必须获得表的IX锁

通过引入意向锁,输入想要对某个表A加锁,只需检测事务是否对表A加了x/Ix/S/IS锁

- X IX S IS
X × × × ×
IX × ×
S × ×
IS ×
  • 任意IX/IS 之间都是相容的,因为它只表示要加锁,并没有真正的加锁
  • S锁只与S和IS兼容

两阶段封锁协议

保证事务可串行化的一个协议是两阶段封锁协议,该协议分两个阶段

  • 增长阶段:事务可以获得锁,但是不能释放锁
  • 缩减阶段:事务可以释放锁,但是不能获得新锁