代码改变世界

事务原理-1

2018-01-11 21:18  Gizing  阅读(572)  评论(0编辑  收藏  举报

1.事务模型解决的问题

由于多会话可能访问同一行数据,因此多会话施加操作的顺序和事务原子性将对数据产生影响:

  • 读读操作:不影响数据一致性,可以并发
  • 读写操作:可细分为读写和写读操作,顺序对产生异常有影响
  • 写写操作:通常不允许并发操作,不做并发控制会带来数据异常

1.1读数据异常

SQL2003标准定义的三种读数据异常,针对的是某个事务的读操作而言

  • 脏读(dirty read),对于T2来说就是脏读异常

    T1 modify a row, T2 then reads that row before T1 performs a commit, if T1 then performs a rollback, T2 will hava read a row that was never commit

  • 不可重复读(non-repeatable read),对于T1来说

    T1 reads a row, T2 then modifies or deletes that row and performs a commit, if T1 then attempts to reread the row, it may receive the modified value or discover that row has been deleted

  • 幻读(phantom),对于T1来说。

    T1 reads the set of rows N that satisfy some , T2 generate one or more rows that satisfy the used by T1, if T1 repeats the initial read with same , it obtains a different set of rows

脏读和不可重复读针对的是row(一行)为单位,幻读针对的是行集(0或多行)

数据库系统采取分级控制(不同的隔离级别)手段来消除这三种读异常,读已提交read committed、可重复读repeatable read和可串行化serializable

ACID + 可串行化serializable隔离级别才能保证数据一致性

1.2写写操作引发的数据异常

  • 脏写(dirty write)

    T1在t0时刻进row进行修改,T2在t1时刻对row进行修改,如果没有并发控制,T1在t3时刻回滚使得T2对row的修改失效。对T1而言,回滚掉了不是自己修改的数据,T1发生了脏写

  • 丢失更新(lost update)

    T2在t1时刻对row进行修改,T1在t2时刻对row进行修改,T1在t3时刻提交会使得T2对row的修改失效。对于T1而言,覆盖了不是自己修改的数据,T1引发了丢失更新

1.3语义约束引发的数据异常

尽管有多种并发控制技术可以解决上述异常。但是快照隔离技术会引发新的异常。

快照隔离(Snapshot Isolation)技术引发的数据异常,有别于上述异常,其异常现象是业务的逻辑语义引发的,即除了读写操作,数据间还应该满足一定语义约束。

快照隔离技术中并发事务因不能满足约束而发生的异常,称为写偏序

  • 两个事务引发(简单写偏序,simple write skew)

    T1事务在 t0 时刻读取了在打电话的值班医生个数,T2 事务在 t1时刻也读取了在打电话的值班医生个数。事务 T1 在 t2 时刻进行判断:如果在打电话的值班医生个数大于等于 2 人则请 Alice 停止打电话。事务 T2 在 t3 时刻进行判断:如果在打电话的值班医生个数大于等于 2 人则请 Bob 停止打电话。然后事务 T1 和 T2 分别提交。如果在这种并发的情况下,允许事务 T1 和 T2 都提交成功,则 t6 时刻, Alice 和 Bob 都停止了打电话。如果串行执行事务,先执行事务 T1 后执行事务 T2 , Alice 会停止打电话但 Bob 不会停止,这与前一种情况的结果不同;如果先执行事务 T2 后执行事务 T1 , Bob 会停止打电话但 Alice 不会停止,这与前一种情况的结果也不同;这表明前一种并发执行是非序列化的,即事务 T1 、 T2 并发时违反了约束( 约束为 :如果同时打电话的人数大于等于 2 人则请 Alice 或 Bob 其中一个人停止打电话直到同时打电话的人数少于 2 人)发生了写偏序异常现象。

由于每个事务在更新过程中无法看到其他事务的更改结果,导致各个事务提交之后的最终结果违反了数据一致性

1.4总结

  • 在自己的事务中设置想要的隔离级别,使得本事务不受其他事务的影响

2.事务特性和属性

事务特性:ACID

事务属性:可串行化(serializability),可恢复性(recoverability)。这里的可串行化和隔离级别中的可串行化不是一个概念,隔离级别是为了提高并发度而弱化数据一致性而提出的数据一致性层级,只有最高级的serializable才能做到隔离性(Isolation)

2.1原子性(Atomicity)

事务要么成功要么失败,对应事务的"committed"或"aborted"状态

2.2一致性(Consistency)

定义是“from one valid state to another”。有两层含义:

  • 符合用户限定的数据一致性约束
  • 符合可串行化和可恢复性

2.2.1可恢复性

定义:已经提交的事务未曾读过被回滚的事务写过的数据,即不会发生脏读。

这个属性保证的是多个并发事务调度后期的提交顺序对数据一致性没有影响

2.3隔离性(Isolation)

强调并发执行,且并发执行之后数据的最终状态应当是有效的,看起来就像是“串行”执行的结果。

2.3.1可串行化

可串行化是一个调度,对于存在共同操作对象的并发事务,如果其执行结果等价于某个串行化调度,则这个调度是可串行化调度。

这个属性保证的是多个并发事务的调度顺序要对数据的一致性没有影响,还能提供执行效率

  • 冲突动作,满足三个条件

    • 两个动作属于不同事务
    • 至少一个动作是写
    • 动作操作在同一个对象上
  • 冲突等价,对于不同的事务调度S1和S2,满足两个条件,则S1和S2是等价的:

    • S1和S2包含相同的事务集合
    • S1和S2包含相同的冲突动作集合
  • 冲突可串行化,当某个调度是一个“冲突等价”于一个或多个“串行调度”,则这个调度是冲突可串行化。

    冲突可串行示例,调度等价于<T1, T2>

T1 T2
R(x)
R(x)
W(y)
commit
W(x)
commit

基于锁的并发控制方法通过阻塞其他产生冲突的事务来保证可串行化;基于时间戳的方法通过回滚产生冲突的事务保证可串行化

2.4持久性(Durability)

对于committed状态的数据要能永久保存,对于aborted状态的数据则不关注

3.并发控制技术的简单概述

乐观和悲观的并发控制思路区别在于事后检查还是提前预防

  • 乐观(Optimistic concurrency control, OCC),在事务提交时刻进行隔离性和完整性约束检查,并发冲突少的场景此方法适合。

  • 悲观(Pessimistic concurrency control, PCC),从一开始就检查每一项操作是否违反隔离性和完整性,违反则阻塞。

3.1 基于时间戳排序(timestamp ordering)

  • 每个事务有一个时间戳作为标识。
  • 数据项上有读、写两个时间戳,读时间戳记录读取该数据项的最大事务的时间戳,写时间戳记录写入该数据项当前值的事务的时间戳。
  • 检查Ti事务的时间戳和Tj事务数据项上的时间戳以确定两个事务的先后关系,如果 Ti<Tj 则需保证并发调度等价于Ti先于Tj的某个串行调度
  • 确保在访问冲突的情况下,多个事务按照时间戳顺序访问数据

3.2 基于提交排序(commit ordering)

  • 对提交操作的顺序进行排序
  • 事务结束前,读或写不相互阻塞,只在提交阶段发生阻塞,属于OCC
  • 不具有可恢复性

3.3 串行化图形检测

  • 调度S中,每个已提交事务都是一个节点
  • 如果事务Ti优先且冲突于事务Tj,则存在Ti到Tj的一条边(有向图)
  • 如果图中存在环则违反冲突可串行化

3.4 两阶段封锁(two-phase locking, 2PL)

  • 同一个事务内加锁和解锁不能交叉进行,分为增长阶段和缩减阶段
  • 读读允许并发,读写不允许并发
  • 共享锁允许向排它锁升级,排它锁允许向共享锁降级。升级只能发生在增长阶段,降级只能发生在缩减阶段

3.5 MVCC

  • 为写操作生成一个数据项的新版本,读操作则根据所在事务开始阶段获得的事务快照,读取该数据项的相应版本
  • 多种并发技术协同使用效果更好

以上学习笔记参考自《数据库事务处理的艺术:事务管理与并发控制》李海翔等著