写在前面
本文是一篇CockroachDB官方博客的译文,主要阐述数据库实现串行化隔离的必要性。关于事务隔离性,Ivan曾经在“分布式数据库之事务隔离性”中从理论方面进行过系统的介绍,本文则是从数据库厂商的角度来阐述对隔离性的理解,大家可以将两篇文章结合起来,对隔离性有更加全面客观的理解。CockroachDB的理念是首先保证安全性而后追求高性能,所以花了很大精力实现Serializable Snapshot Isolation
,是目前极少的有实用价值的SERIALIZABLE
实现。当然,业界也有厂商对可串行化方面投入的必要性持不同观点。Ivan猜测CockroachDB的理念可能是受到了PostgreSQL的影响,毕竟后者是率先支持Serializable Snapshot Isolation
的商业数据库,并且CockroachDB在SQL层面也是以兼容PostgreSQL为目标。
正文
大多数数据库都提供了事务隔离级别的选择,可以在正确性和性能之间进行权衡。然而,高性能的代价就是开发人员必须小心研究事务交互否则就会引入一些微妙的错误。CockroachDB 默认提供了强隔离(SERIALIZABLE
)可以确保你的应用总是看到期望的数据。在本文中我们将解释这意味着什么以及不充分的隔离在如何影响真实世界的应用。
SQL标准中的隔离性
SQL标准定义四个隔离级别
SERIALIZABLE
REPEATED READ
READ COMMITTED
READ UNCOMMITTED
SERIALIZABLE
事务运行时好像在同一时刻仅有一个事务运行;其他隔离级别允许出现SQL标准称作的“三种phenomena”脏读、不可重复读、幻读。后续的研究(此处指Critique,Ivan在文章“分布式数据库之事务隔离性”中已经进行了介绍)定义了额外的“phenomena”和隔离级别。
SERIALIZABLE
隔离级别时,你是在允许数据库返回错误答案,希望它能比正确答案更快。SQL标准认为这是危险的,需要SERIALIZABLE
置为默认的隔离级别。更弱的隔离级别只是为那些可以容忍“anomalies”的应用提供了潜在的优化手段。 真实数据库中的隔离性
多数的数据库忽略了将 SERIALIZABLE
作为默认隔离级别的规约,而是默认替换为更弱的RC
或RR
隔离级别,它们的性能优先于安全性。更令人担心的是,一些数据库(包括Oracle,PostgreSQL V9.1以前)根本不提供 SERIALIZABLE
级别的事务隔离。Oracle实现的 SERIALIZABLE
隔离级别实际上是更弱的“Snapshot Isolation
”。Snapshot Isolation
(快照隔离,简称SI
)的出现晚于SQL标准的制定,但是已经被多种数据库系统实现,因为它提供了很好的性能与一致性的平衡。它强于RC
但弱于 SERIALIZABLE
,很类似RR
但不完全等同(RR
允许幻读,但禁止写偏序,SI
更好相反)。实现SI
的数据库,在如何将其纳入到四个SQL标准隔离级别上有不同的选择。Oracle的选择最激进,直接将他们的SI
实现称为 SERIALIZABLE
。CockroachDB 和SQL Server则保守一些,将SI
作为独立的第五个隔离级别。PostgreSQL(9.1版本以后)介于两者之间,使用SI
替换了RR
。因为数据库很少使用 SERIALIZABLE
模式,而是默认采用更弱的隔离级别,所以它通常很少经过彻底的测试和优化。例如PostgreSQL有一个固定大小的内存池,用来跟踪可串行化事务间的冲突,但在高负载情况下会耗尽。
SI
替代 SERIALIZABLE
是安全有益的。 我们的哲学是从安全性出发向着高性能方向前进,这是比其他方式更优的。 ACIDRain:发现事务Bug
斯坦福最近的研究展示了弱隔离性对真实世界的影响程度。 Todd Warszawski and Peter Bailis测试了12个电子商务应用程序并发现了22个事务相关的Bug,其中5个在更高的隔离级别下可以避免。多数bug可以被简单得利用并造成财务方面的影响。例如,在5个被测试的应用程序中,当操作一个浏览器进行结算的同时,操作另一个浏览器向购物车增加一项商品,可能导致新增的商品在账单中免费。这些研究人员开发工具以半自动化的方式去确定这些脆弱点,为类似的更普遍的攻击(研究者将其称为ACIDRain “酸雨”)铺平了道路。
大多数默认弱隔离的数据库都提供了解决方法,例如FOR UPDATE
和 LOCK IN SHARE MODE
(非标准语法)作为SQL语句的修饰符。当正确使用时,即使在弱隔离级别下,这些修饰符也可以使事务安全。然而,这很容易出错,而且即使是使用这些扩展方式,也会同时引入 SERIALIZABLE
模式大多数的缺点。(事实上,在RC
事务中滥用 SELECT FOR UPDATE
可以导致比 SERIALIZABLE
更差的性能,因为在那些串行化操作的地方可以仅使用共享锁,却使用了排他锁) ACIDRain的研究显示了这种技术的局限性:3个应用程序中仅有一个正确使用了 SELECT FOR UPDATE
特性,其他两个都存在漏洞。 结论
鼓励弱隔离级别(性能优先于数据安全性)的数据库,让你去学习事务间细微的交互并实现易错的解决方法。CockroachDB默认提供了 SERIALIZABLE
事务,确保总能看到你所期望的事务数据库的一致性