Transactions (3-1) - Weak Isolation Levels - Read Committed

前言

如果两个 transaction 没有接触到相同的资料,则它们可以很愉快的 并发 (concurrent) 执行,因为他们彼此不依赖。

并发的问题只会发生在当某个 transaction 要读取的资料正在被另一个并发执行的 transaction 修改,或者两个 transaction 同时修改一样的资料。

并发的 bug 很难测试或重现,所以资料库才会透过 ACID,向我们保证不会发生并发的 bug,就理论上来说,隔离性应该能保护工程师远离并发的恶梦,所以最好就是不要用并发啦,也就是实施 序列化隔离 (serializable isolation), 一次只有一个 transaction 在执行完全符合隔离性保证。

一次只能让一个 transaction 执行,这就代表序列化隔离会有很严重的效能问题,因此在现实世界中,许多支援 ACID 的资料库,大多数的预设都是使用 弱等级的隔离 (Weak Isolation Levels),因为隔离性等级较弱,所以会比较难理解,也代表还是有可能会出现并发资料问题或并发 bug,所以就让我们来好好了解各个弱等级隔离的差异,然後搭配应用程序开发达成尽量减少并发 bug 吧!

Read Committed

等级 1 的隔离是 read committed ,它保证了以下两点:

  1. 读取资料时,你只会读取到已经被 commit 的资料(无 dirty read)。
  2. 写入资料时,你只会写入至己经被 commit 的资料(无 dirty write)。

让我们研究研究这 2 点。

No Dirty Read

dirty read 就是在你的 transaction 里读到另一个 transaction 还没 commit 的资料。

无 dirty read 的结果如下图,User 2 只会读取到已被 commit 的资料(等於是 User 1 的多次写入在 commit 後会一起成为可视)。

No Dirty Write

dirty write 就是一个 transaction 在还没 commit 的情况下,其值被另一个 transaction 给覆写了。

read committed 等级的隔离该如何避免 dirty write 呢?一般的做法就是延迟第二个 transaction 的写入,直到第一个的 transaction 写入 commit 或中断 (aborted)。

如果 transaction 是需要更新多个物件,如下图,dirty write 就会导致汽车是 Bob 买到,但发票是寄给 Alice 了。

然而,read committed 无法避免如下图针对 计数器 (counter) 资源的 竞争条件 (race condition) 写入 , User 2 的写入是发生在 User 1 已经 commit 之後的动作,所以符合 read committed 的保证,无 dirty write。但它的值依旧不正确,我们会在 Day 5 讨论如何安全的在 counter 累加 1。

实现 Read Committed

read committed 是最流行、常用的隔离等级,也是许多资料库的预设隔离等级。

避免 dirty write 的实现可以使用 row-level 的锁,当 transaction 想要修改特定物件 (row 或 document) 时,它必须取得该物件的锁,持有该锁直到该 transaction commit 或中断 (aborted),一次只有一个 transaction 能取得物件锁。

而避免 dirty read 的实现呢?一个选项是采用避免 dirty write 的方法,有物件锁才能读取资料;但倘若此时有个跑很久的写入 transaction 正在写入资料呢?所有的读取就得排队等 transaction 结束才能作业了,如此就会严重伤害那些 read-only transaction 的回覆时间了,显然这不是个好方法。

基於以上理由,大多数的资料库实现 避免 dirty read 的方法就是像上图 7-4 那样,在 Usesr 1 执行资料写入时,除了要取得物件锁之外,资料库也会先记住旧的资料让其他的 transaction 读取,commit 後就替换成新的资料。


明天讲另一个 Weak Isolation Levels - 快照隔离 (Snapshot Isolation)。


<<:  [Day 3] Atomic Operation

>>:  Day03,Model摸象

每日挑战,从Javascript面试题目了解一些你可能忽略的概念 - Day10

tags: ItIron2021 Javascript 前言 终於迈入第10天啦! 我们昨天讲完资料...

Day10 HTML表单元素

表单是什麽? 表单是用来收集使用者输入的资料,而这些输入的资料通常会被送到网页服务器来处理或储存,...

【PHP Telegram Bot】Day09 - 用 PHP 主动接收和发送讯息吧!

前置作业 复制程序码 还记得前天最後建立的资料夹吗,把它用 VS code 打开,再建立一个 php...

金钥丛集(Key clustering)

哈希上下文中的碰撞通常是指哈希函数从两个不同的输入消息生成相同哈希值的情况。有些,例如维基百科,可能...

Day 27 利用transformer自己实作一个翻译程序(九) Point wise feed forward network

Point wise feed forward network 在两层全连阶层中加入一个relu的激...