昨天谈到 write skew 和 phantoms ,是 2 种特别难重现的 竞争条件 (race condition) 情况,也就代表无法针对这些情况做测试,这些只有在你衰小要找奇怪的 bug 时才会遇到,这是个老问题了,而这一切的解法也就显而易见,使用 序列化隔离 (serializable isolation) 。
序列化隔离是最强的隔离等级,尽管 transaction 能并发执行,但它保证了最终的执行结果等同一次只执行一个 transaction,重点是 连续 (serially) 这个字,表示无并发性 (without concurrency),所以它就能避免所有的竞争条件。
序列化隔离的实现方法有 3 种:
(1) 字面上的,连续 (serially) 执行 transactions。
(2) 二阶段锁 (Two-Phase Locking 2PL),主宰数十年的实作方式。
(3) 优化并发控制技术,就像有了序列化能力的快照隔离。
最简单暴力直觉的实作方法,每一个时间点只会有一个 transaction 执行在单一执行绪上。
为什麽这方法变的可行了呢?归功於越来越便宜和越来越强的 RAM,让一切在 RAM 上执行变的可行;再加上 OLTP transaction 都是短小精干的读取跟写入,长时间写的读取要用 OLAP + 快照隔离来区分。
但是!这就代表了这个实作方法的吞吐量很吃单一 CPU 核心效能,为了让单一执行绪用的更有效率,书中建议我们可以将数个 transaction 会做的事合并成一个 预存程序 (stored procedures) 来执行,就是要减少网路 IO,尽量避免 交互式多语句 (interactive multi-statement) transaction。使用 Day 5 图 7-8 医生排班案例
所以 连续执行 (serial execution) 要可行,最好还是不要违反以下几点的限制:
主宰了序列化隔离 30 几年的实作算法,其实我们在 Day 3 - No Dirty Write 小节看到资料库是怎麽用锁去避免 Drity Write,二阶段锁 (Two-Phase Locking) 也是类似的概念,但锁更强大,当没有 transaction 正在写入时,多个 transaction 允许并发读取同一个物件,一旦有 transaction 想写入物件,它会:
在二阶段锁中,写入只会阻档读取,反之亦然,这跟快照隔离 (Day 4) 的写入跟读取不会互相影响 的核心精神很不同,所以二阶段锁能避免所有的竞争条件写入、昨天提的更新遗失 (lost update) 和 write skew。
为了实现这个写入跟读取互相阻档的资料库 全物件锁,锁的状态可以是 共享模式 (shared mode) 或者是 互斥模式 (exclusive mode),该锁跟随以下规则:
用这麽多锁免不了可能会发生 死结 (deadlock),也就是 2 个 transaction 彼此等待互相释放锁,幸运的是现在的资料库会自动侦测死结,然後 abort,之後靠应用程序做重试。
二阶段锁最大的缺点就是效能啦,transaction 的吞吐量和回应时间比起 弱等级隔离 (Weak Isolation Levels) 要糟上许多,其最大的原因就是要等待 互斥模式 锁的释放,如果你的 transcation 又执行的稍稍久一点,回应时间就烂掉了,所以 2PL 会有非常不稳定的延迟 (lantency),会有非常慢或非常快的回应时间百分位 (2020 Day 3) 发生,还有刚刚讲的死结问题也需要时间解决。
第 (3) 项就明天再讲啦!
<<: IOS、Python自学心得30天 Day-3 TensorFlow 模组
终於跨入第 11 天,今天要来了解在 Go 里面我很不理解的一个型别 -- Pointer。 话不多...
New Soundcloud function: artists from the streamin...
🌵 建立 Dealer Manager - Content Page 後台页面 - 代理商功能。 ...
function eatBreakfast () { console.log('吃早餐'); } f...
在设定完帐户後,我们就可以实际收取报价资料以及下单了。 如果要收取商品报价资料,我们会使用subsc...