Skip to content

Review 10 | Transactions

:material-circle-edit-outline: 约 2700 个字 :fontawesome-solid-code: 10 行代码 :material-clock-time-two-outline: 预计阅读时间 9 分钟

Transaction

Transaction Concept

并发控制与恢复由事务管理模块实现,即其操作对象是事务

事务是一串SQL语句的执行序列,可以包含多条语句,用于完成一个目的,是要commit的

要完整描述一个事务,我们需要用到ACID Properties 这个概念

  • Atomicity (原子性)
    • 构成事务的操作被看成一个整体看待,要么全部成功,要么全不执行
    • 数据库恢复功能能实现原子性
  • Consistency (一致性)
    • 不会错误地修改数据库,相互关联的数据应该进行更新。比如转账转完总的钱应该不变
    • consistency requirements include
      • Explicitly (显式) specified integrity constraints : primary keys , foreign keys
        • 就是定义表时人为定义的
      • Implicit (隐式)integrity constraints, e.g.– sum of balances of all accounts minus sum of loan amounts must equal value of cash-in-hand
  • Isolation (隔离性)
    • 要求各并行事务相互之间不能影响,就像虚拟内存的分块、
    • 会影响的就不要并行了,改串行
  • Durability (持久性)
    • 对数据库的成功修改是永久性的,除非被其它事务再次修改
    • even if there are software or hardware failures
    • 出故障时,数据库恢复功能能实现持久性,比如用日志

Transaction State

  • Active
    • 初始状态,事务正在执行
  • Partially committed
    • statement 执行完了,此时修改还在缓冲区没提交,可能会fail
  • Failed
    • 发现问题了
  • Aborted
    • 回滚之后就是这个状态,之后要么清除事务,要么重新执行
  • Committed

image-20240521135411583

Concurrent Executions

并行处理会遇到以下问题

  • Lost Update( 丢失修改 )
    • 出现了交叉修改,其中一个被覆盖了
    • 比如两个人同时买票image-20240521140839789
  • Dirty Read (读脏数据)
    • 比如一个人预定票结果没钱退了,另一个人正好在买票

image-20240521140958282

  • Unrepeatable Read(不可重复读)
    • 我们要求事务的隔离性,这里T1的第二次read被T2影响了,这是我们不希望出现的image-20240521141140280
  • Phantom Problem (幽灵问题)
    • 和上一个类似,不过这里是指T1里面的第二次select由于收到T2影响,多了一个entity

image-20240521141453651

Schedules(调度)

就是一组排好序以防止出现并行问题的事务,注意是一组事务

串行调度(serial schedule),先执行一个再执行另一个,肯定能保证正确

image-20240521142040285

交叉调度,与Schedule 1等价

image-20240521142300185

这个交叉调度等价于串行调度

这种能等价变化到串行调度的调度叫 Serializability( 可串行化 )

Serializability

  • Conflicting Instructions
    • 操作对象相同,只要有写,就是冲突
    • 操作对象不相同肯定不冲突

Conflict Serializability (冲突可串行化)

能通过交换非冲突指令变成另一个调度,就说这两调度是冲突等价的

Precedence graph(前驱图)有向边指示两者有一对冲突的操作,被指向事务指令时间在后

image-20240521151723629

冲突的操作是不能交换次序的,所以如果两个事务能形成一个有向闭环,那肯定不能转化为串行

image-20240521153135595

注意T5没有冲突,但是也要放进图里,可以放在串行调度里的任意位置(见右下角示意图)

Different forms of Serializability

view serializability 要求两个调度只要读操作获得的数值一致,且写操作写出去的数值也是一样的即可

Recoverable Schedules

  • Recoverable schedule (可恢复调度)
    • 比如,T1先写了A,T2再读了A,那么如果T1的commit先于T2,这就是可恢复调度
    • 如果T2先commit,那么T1如果rollback就会导致T2读的是不一致的

Cascading rollback 即多个事务都一个接一个读和写,第一个错了就会导致后面的全部都要rollback

image-20240521201024303

一个简单的解决办法就是规定不读脏数据,我们管采用这种规则的调度叫 Cascadeless schedules (无级联调度),脏数据即改了还没commit的数据

Transaction Isolation Levels

  1. Serializable
  2. Repeatable read
    • only committed records to be read, repeated reads of same record must return same value.
    • 不关心幽灵问题
  3. Read committed
    • 不读脏数据
  4. Read uncommitted
    • 能读脏数据

Concurrency Control Protocols

  • Lock-Based Protocols
    • 访问数据前需要申请相应的锁,一个数据同一时间只有一个锁
  • Timestamp-Based Protocols
    • 给每个事务一个时间戳,类似出生日期,且十分精确
    • 根据时间戳决定次序

上面两种都是悲观的,即针对会出现问题的情况进行设计

下面这个是乐观的

  • Validation-Based Protocols
    • Each transaction must go through 3 phases:
      •  Read phase  Validation phase  Write phase

Concurrency Control

Lock-Based Protocols

basic two-phase locking(基本两阶段封锁),可串行,但不保证可恢复

可恢复的要求是不读脏数据

下面的两种加强版保证了可恢复性

  • Strict two-phase locking(严格两阶段封锁)
    • a transaction must hold all its exclusive locks till it commits/aborts
  • Rigorous two-phase locking(强两阶段封锁)
    • a transaction must hold all locks till commit/abort
    • 很影响并发度

我们下面讲的是 basic 2PL protocol

事务访问数据前需要申请相应的锁,锁加给被操作的数据

  • exclusive (X)
    • Data item can be both read as well as written.
  • shared (S)
    • Data item can only be read.

管理器通过相容矩阵判断一个事务能不能提供锁

image-20240527134657907

The Two-Phase Locking Protocol

这些协议是针对一个事务,而不是一个事务集

例如,一个事务满足下面两个先后阶段即可,不用所有事务都需同时在一个phase里,一个事务集里事务的lock point可以不同

  • Phase 1: Growing Phase
    • 加锁阶段
  • Phase 2: Shrinking Phase
    • 解锁阶段

image-20240527134953027

-- Growing Phase
lock-S(A);
read (A);
lock-S(B);  -- lock point
read (B);

-- Shrinking Phase
unlock(A);
unlock(B);
display(A+B)

这种协议保证了可串行性,且串行顺序为LP顺序,当然不是只能按照LP顺序串行

  • delete就往要删的tuple加X锁
  • insert就往刚插入的tuple加一个X锁

Lock Conversions

U-lock :update-lock,先表现为S,后面可升级为X,这种相互转化叫做Lock Conversions

  • lock-upgrade:S->X
  • lock-downgrade:X->S

以此我们得出新的可串行化协议:

  • Two-phase locking with lock conversions
    • 第一阶段只能update
    • 第二阶段只能downgrade

image-20240529124151066

Implementation of Locking

lock manager:独立的进程,管理所有的锁,所有的锁都集中在一个地方,而不是真的物理上放在数据上

Lock Table

锁通过哈希表进行锁操作(就是标记一下这地方被锁了)

每个数据都会有一个id之类的标识,如果要被锁,就将其id交给lock manager,后者会将这个id映射到哈希表上,然后根据规则加锁标记

image-20240529125248935

Deadlock Handling

死锁,就多个事务相互等待,陷入死循环了

image-20240529125708719

Deadlock prevention protocols

  1. 一个事务开始前给其打上所有今后需要的锁 (predeclaration)
  2. 给数据访问操作进行合理的排序(graph-based protocol)
  3. Timeout-Based Schemes
    • 等半天都等不到对方释放锁,就别等了,tnnd直接整个事务回滚重开
    • 可能会 starvation,就在同一个地方反复回滚反复卡

Deadlock Detection

Deadlocks can be described as a wait-for graph

就是画等待关系,等待中的事务指向被等待的事务,有环就死锁了,即出现了相互等待的情况

显然这个图在不断变化,所以系统是定期来检测一次

image-20240529131940702

Deadlock Recovery

选一个事务(victim)杀了(回滚),打破这个环

怎么选?

  1. 杀最年轻的(最晚开始执行的可能亦执行的操作比较少)
  2. 杀最老的(执行的操作很多,可能造成了很多死锁)
  3. 杀当前手上的锁最多的
  4. ......

当然,也可能出现starvation的情况,总是同一个人被杀,最好是均摊一下死亡次数

image-20240529134504590

Graph-Based Protocols

另一种锁协议

下面这个是 database graph,结点为数据

有向边表示有事务需要访问边两端的结点,而且有先后顺序,先访问指向后访问

如果没有环,就能设计出很好的协议,下面我们讲基于特殊的database graph设计的协议

image-20240529140715068

Tree Protocol

如果 database graph是树,即只有一个根,那就能用这个协议

  1. 该协议只有exclusive locks ,没有S锁
  2. 对于一个事务来说,first lock 可以放在任意 data item 上
    1. 后续如果要加锁,数据的父节点必须有锁,否则该数据不能加锁
  3. 任意锁可在任意时间解锁
  4. 对于一个事务,每个数据只能锁一次,unlock之后不能再lock了

image-20240529141234817

这样能保证冲突可串行,防止死锁,但不保证恢复性,加的锁会更多

Multiple Granularity(多粒度)

把每种数据看成一个颗粒,有粗有细,以此分为不同等级(hierarchy of data granularities),一个等级即一个粒度

锁可以加在任意需要的颗粒上,比如锁一张表,锁一个B+树

  • fine granularity(细粒度)
    • lower in tree
    • high concurrency, high locking overhead
  • coarse granularity(粗粒度)
    • higher in tree
    • low locking overhead, low concurrency

image-20240529144127343

三个新锁对于基粒(最小的颗粒)不作用

  • intention-shared (IS)
    • 一个事务意向给基粒加S锁,就给其所有祖父结点加IS锁,表有事务有意向访问该子树里的数据并给其加S锁
  • intention-exclusive (IX)
    • 同IS,不过既可以加S也可以加X
  • shared and intention-exclusive (SIX)
    • 表示里面的颗粒不需要加S锁也可以读,但是写还是要X锁

image-20240529145437163

Multiple Granularity Locking Scheme

Transaction Ti can lock a node Q, using the following rules:

  1. 严格遵循相容矩阵
  2. 根需要第一个被锁,随便选一种锁就行
  3. 父子关系:IX/IS -> S/IS
  4. 父子关系:IX/SIX -> X/SIX/IX
  5. 要求事务遵循两阶段锁协议
  6. 加锁从上往下,解锁从下往上

image-20240529150143142

Handling Phantoms

Insertions and deletions can lead to the phantom phenomenon.我们可以用索引等方式高效解决

Index Locking Protocol To Prevent Phantoms

通过索引用S锁住所有与要读的数据集有相同key的数据,防止出现X锁

image-20240529152349666

Multiversion Concurrency Control Schemes(MVCC)

之前都是单版本并发控制,一个信息只有一份,修改只能覆盖原来的

我们可以多版本,修改后产生新版本,而不是进行覆盖

image-20240529153133000

  • Q1, Q2,...是数据,右边的数字是时间戳,反映新旧程度,越大表示越新
  • 一个数据有多个版本,这些版本共同组成一个整体,锁只能加在这个整体上

这个协议需要先声明一个事务是 read-only transactions 还是 update transactions,即只读和允许编辑

只读不用加锁,直接读最新的

写需要先申请S锁,然后复制最新的数据,时间戳设置为无穷大,表示尚未提交,不能被其它指令修改;修改好后计数器+1,更新新版本的时间戳,最后才解锁

这是严厉的两阶段锁协议