1、存储结构
很多时候,我们为了实现原子性和持久性,都需要采用不同的存储器
具体如下图:
2、事务原子性
事务并非总是能如愿以偿,有的时候,事务会中止,为了保证原子性,同时对数据库不产生影响。因此 中止事务做的改变必须撤销。
如果事务未提交 那么我们可以 维护一个日志。数据库的修改都先记录到日志上。回滚的时候基于日志去取消。
如果事务已提交,由于提交事务是立即持久化的,所以我们只能去做一个补偿事务(如果原事务 是让账户扣款20元,那么补偿事务就是让账户增加20元,做一个反操作,这个是用户自己做的)。
事务大概分为以下几个状态:
1.活动的(active):初始状态,执行时的状态
2.部分提交的(partially committed):最后一条语句提交后,但是没用完全提交完成时
3.失败(failed):无法正常往后执行
4.中止的(aborted):事务回滚并且数据库已经恢复到事务开始之前的状态。
5.提交的(commited):成功完成后
活动状态,部分提交状态 都有可能失败。系统判定事务不能正常执行后,事务进入失败状态 此时有两种选择:
1.硬件错误的情况下 数据库会重启事务, 重启事务被认为是一个新事务.
2.事务内部逻辑错误的情况下,数据库会中止事务。
3、事务隔离性
数据库一般允许多个事务并发执行,但是 并发执行会有许多一致性问题造成。但是我们又有必须允许并行的理由:
提高吞吐量和资源利用率:现代计算机都是多核心cpu,并且cpu与磁盘io可以并行运作。因此当几个事务在执行磁盘读写时,另外几个事务可以cpu运行。这些技术可以大大提高数据库服务器吞吐量。同时 资源利用率也可以大大利用。
减少响应时间: 额系统中有各种各样的事务,有的执行时间长,有的执行时间短。如果事务串行地执行,短事务可能得等他前面很长的事务完成,这可能导致长延迟。如果各事务可以利用数据库的不同部分操作,那么等待时间,响应时间会大大降低。
当多事务并发执行的时候,我们又是如何保证隔离性的呢? -数据库并发控制部件。
3.1、可串行化
我们采用事务调度(决定任务执行先后顺序) 来保证资源利用率最大化,(数据库完成这个任务的是并发控制系统)。
并发执行中,通过保证执行的任何调度的效果都和串行化调度的效果一样,我们可以保证数据库的一致性。并发调度在某种意义上等于一个串行调度(如果在一个调度中,属于同一个事务的指令紧挨在一起,我们就称这个调度是串行的)。这种调度 成为可串行化调度.
此时引入一个概念 事务冲突 事务冲突就是说 ,两个事务对同一个事务进行操作 且至少有一个事务进行的是write操作,那么 这两个事务就是冲突的。
如果冲突事务可以通过调度变化为 可串行化调度,那么就是冲突可串行化。 并且 S通过可用调度变为串行化调度S1,那么我们会说事务S与S'是冲突等价的。
那么如何判定调度的可串行化呢? 调度规则是什么呢?
我们采用DAG有向无环图。对于同一个数据项目 只有read是可以相互等价替换先后顺序的。
调度1: S1=r2(A),r1(B),w2(A),r3(A),w1(B),w3(A),r2(B),w2(B)
对于数据A 顺序是 2->3 对于数据项 B 顺序是1->2
所以 此时的调度顺序图如下,为有向无环图,那么可以认为冲突可串行化。
调度2: S1=r2(A),w2(A),r1(A),w1(A),w1(B),r1(B),r2(B),w2(B)。
该调度中,数据A 顺序是 2->1 对于数据项 B 顺序是1->2
所以 此时的调度顺序图如下,存在环,那么可以认为不可串行化。
3.2、可恢复调度
上面3.1中 调度顺序1:S1=r2(A),r1(B),w2(A),r3(A),w1(B),w3(A),r2(B),w2(B)
虽然该调度是可串行化调度的 ,但是 很明显 事务3依赖于事务2 事务2依赖于事务1. 为了防止事务1 影响事务2 事务2影响事务3.所以 我们需要让事务提交顺序为 1->2 ->3 这样可以保证事务的恢复性,防止1发生故障时,事务2已经无法回滚。
可恢复调度 要求的就是 对于每个事务 如果t1依赖于t2,那么 t2 应该先于t1提交。
3.3、无级联调度
同样采用上面 3.1中 调度例子1中来说,假如我们现在已经控制了事务的提交顺序为1->2->3。但是当事务1出现问题的时候,事务2,事务3很明显 都需要回滚,因此 单个事务导致多个事务回滚的现象 称为 级联回滚(cascading rollback).
因为一个事务失败,需要去回滚多个事务是我们需要避免的。因为 我们需要的是 无级联调度(cascadeless schedule) 无级联调度。无级联调度需要的是 对于每个事务调度中,事务读取的值必须是一件提交的值(读已提交)。就拿调度3.1为例 我们就需要 事务3执行r3(B)之前 事务2已经提交。事务2执行r2(B)之前 事务1已提交。
3.4、事务隔离级别
未提交读:事务可以读取其他事务未提交的数据
已提交读:只允许读已提交数据,但是不要求可重复读。例如 在事务A有两次执行读取数据操作,但是在此期间,事务Bupdate 了数据,那么 事务两次读取结果不同。
可重复读:并且当事务A执行读取任务期间,数据无法更新。 例如 在事务A有两次执行读取数据操作,但是在此期间,事务B 想update 数据,那么 事务B就需要等待或者被拒绝。
可串行化:调度必须可串行化。
3.5、隔离级别的实现
锁:一个事务可以封锁其访问的数据,而不是整个数据库。在这种策略下,事务必须在足够长的时间内持有锁来保证可串行化。但是这个周期过长就影响性能。因此 锁配合两阶段封锁协议的话 就是一个阶段只获取锁,后面半个阶段只释放锁。
同时 锁大的可以分为两种:共享锁和排他锁,共享锁是读操作时占有的。排他锁是写操作时占有的。
时间戳:基于时间戳的隔离级别实现是 在读操作时,选取最新记录时间戳的数据,并且校验。写操作时,先读取最新时间戳的数据,校验操作,如果成功会更新那个记录时间戳。
多版本和快照隔离: 多版本就是 每个事务允许读取一个旧版本的数据,而不是从一个未提交或者老事务中读取数据。为了完成这个目标 有许多技术,其中有一种叫快照隔离。在快照隔离中,我们可以想象每个事务开始时都有其数据库版本或者快照。它从这个快照中读数据。如果事务更新数据,那么我们只更新该快照中的数据,只有当事务提交了,那么数据库会更新该数据。
参考
<<数据库基础概念>>