AICon 上海站|日程100%上线,解锁Al未来! 了解详情
写点什么

事务隔离级别和脏读的快速入门

  • 2016-11-14
  • 本文字数:4380 字

    阅读完需:约 14 分钟

关键要点

  • 仅从 ACID 或非 ACID 角度考虑问题是不够的,你应知道你的数据库支持何种事务隔离级别。
  • 一些数据库宣称自己具有“最终一致性”,但却可能对重复查询返回不一致的结果。
  • 相比于你所寻求的数据库,一些数据库提供更高的事务隔离级别。
  • 脏读可导致同一记录得到两个版本,或是完全地丢失一条记录。
  • 在同一事务中多次重新运行同一查询后,可能会出现幻读。

最近 MongoDB 登上了 Reddit 的头条,因为 MongoDB 的核心开发者 David Glasser 痛苦地认识到 MongoDB 默认会执行脏读

在本文中,我们将解释什么是事务隔离级别和脏读,并给出一些广受欢迎的数据库是如何实现它们的。

ANSI SQL 给出了四种标准的事务隔离级别:可序列化 (Serializable)、可重复读 (Repeatable reads)、提交读 (Read committed) 和未提交读 (Read uncommitted)。

许多数据库缺省是提交读的,这保证了在事务运行期间用户看不到转变中的数据。提交读的实现通过在读取时暂时性地获取锁,并持有写入锁直至事务提交。

如果在一个事务中需要多次重复同一读取,并想要“合理地确定”所有的读取总是会得到同样的结果,这要在整个过程期间持有读取锁。在使用可重复读事务隔离级别时,上述操作是自动完成的。

我们这里所说的“合理地确定”可重复读,是因为存在“幻读”(phantom reads)的可能性。当执行使用了 WHERE 语句的查询时,类似于“WHERE Status=1”,就有可能发生幻读。虽然所涉及的行将被锁上,但是这并不能阻止匹配 WHERE 条件的新行被添加进来。“幻”(phantom)一词指在查询第二次执行时所出现的行。

为确保在同一事务中的两次读取会返回同样的数据,可使用可序列化事务隔离级别。可序列化使用了“范围锁”,避免了匹配 WHERE 条件的新行添加到一个开放的事务中。

一般情况下,由于锁竞争的存在,事务隔离级别越高,性能越差。因此为了改进读取性能,一些数据库还支持未提交读。该事务隔离级别将无视锁的存在(事实上其在 SQL Server 中被称为“NOLOCK”),因此该级别下可执行脏读。

脏读所存在的问题

在探讨脏读问题之前,你必须要理解表并非是真实存在于数据库中的,表只是一个逻辑结构。事实上你的数据是按一个或多个索引进行存储的。主索引在大多数数据库中被称为“聚束索引”或“堆”(该术语在各 NoSQL 数据库中各不相同)。因而当执行插入操作时,需要在每个索引中插入一行。当执行更新操作时,数据库引擎仅需访问指到被改变列的索引。但更新操作常常必须要在每个索引上执行两个操作,即从旧的位置删除并在新的位置插入。

在下图中,你可看见一个普通的表,还有表中 IX_Customer_State 和 PK_Customer 对象更新操作的执行计划。鉴于表的 FullName 列并未改变,所以可以跳过 IX_Customer_FullName 索引。

(点击放大图像)

注意在SQL Server 中,PK 前缀指代主键,通常也是用于聚束索引的键。IX 用于指代非聚束索引。其它的数据具有它们自己的命名规范。

解决了上述问题,让我们看一下脏读导致不一致数据的多种途径。

未提交读问题易于理解。在事务被完全提交之前,如果无视写入锁的存在,使用“未提交读”的SELECT 语句就可以就看到新插入或更新的行。如果这些转变操作这时被回滚,从逻辑上说,SELECT 操作将返回并不存在的数据。

如果数据在更新操作过程中被移动了,这就产生了双重读取。例如,你正在读取所有的客户记录的状态。如果在你读取“California”记录和读取“Texas”记录之间,上面所说的更新语句被执行了,你就能看见“客户1253”记录两次。一次是旧值,一次是新值。

记录丢失发生的方式相同。如果我们提取“客户1253”记录并将其从“Texas”记录移动到“Alaska”记录,并再次使用状态去选择数据,你可能会完全地丢失该记录。这就是发生在David Glasser 的MongoDB 数据库中的事情。由于在更新操作期间读取了索引,查询丢失了记录。

脏读也会妨碍到排序操作,该问题的出现取决于数据库的设计方式及特定的执行计划。例如,脏读可能发生于执行计划对所有候选数据行采集指针信息时,如果在其后一行数据被更新了,但实际上执行引擎还是会使用已被采集的指针信息从原始位置拷贝数据。

快照隔离,或被称为“行级版本控制”

为在避免脏读问题的同时提供好的性能,许多数据库支持快照隔离语义。运行于快照隔离状态下,当前的事务不能看到任何先于其启动的其它事务的结果。

快照隔离的实现是通过做被改变行的临时拷贝,而非仅依靠于锁机制,因此它也常被称为“行级版本控制”。

很多支持快照隔离语义的数据库在被请求使用“提交读”事务隔离时,会自动使用快照隔离。

SQL Server 中的事务隔离级别

SQL Server 支持所有四种 ANSI SQL 事务隔离级别,外加一种显式的快照隔离级别。提交读可能也使用快照语义,这取决于数据库中 READ_COMMITTED_SNAPSHOT 选项的配置方式。

在开关该选项前,你的数据库需要做充分的测试。虽然提交读可以提升读取性能,但它也同时降低了写入性能。尤其是 tempdb 被部署在慢速磁盘上时,因为这存储了行的旧版本。

在 SELECT 语句中可以使用臭名昭著的 NOLOCK 指示符。NOLOCK 的作用等同于将事务运行设置为未提交读。这在 SQL Server 2000 及更早期的版本中被大量地使用,因为那时并没有提供行级版本控制。尽管现在不再必要或不建议这样做,但是该习惯仍然保留着。

更多信息参见“设置事务隔离级别 (Transact-SQL) ”.

PostgreSQL 中的事务隔离级别

虽然官方宣称 PostgreSQL 支持所有四种 ANSI 事务隔离级别,但事实上 PostgreSQL 中只有三种事务隔离级别。每当查询请求“未提交读”时,PostgreSQL 就默默地将其升级为“提交读”。因此 PostgreSQL 不允许脏读。

当你选取“未提交读”级别时,事实上你得到了“提交读”,在 PostgreSQL 对可重复读的实现中,脏读是不可能发生的,因此实际的事务隔离级别可能比你所选取的要更加严格。这是被 SQL 标准所允许的,因为四种事务隔离级别仅定义了事务中一定不能发生的现象,它们并未定义应该发生哪种现象。

PostgreSQL 并未显式地提供快照隔离。当然快照隔离是在使用提交读时自动发生的。这是因为 PostgreSQL 的设计从一开始就考虑了多版本并发控制

在9.1 版本之前,PostgreSQL 不提供可序列化事务,会将它们静默降级为可重复读。但当前所有仍在支持的PostgreSQL 版本中都不再有这个限制了。

更多的信息参见PostgreSQL 官方文档的 13.2 节,“ 事务隔离”.

MySQL 中的事务隔离级别

InnoDB 默认为可重复读,但是提供所有四种 ANSI SQL 事务隔离级别。提交读使用快照隔离语义。

更多 InnoDB 相关的信息,参见 MySQL 官方文档的 15.3.2.1 节“ 事务隔离等级”

事务在使用 MyISAM 存储引擎时是完全不被支持的,这里使用了表一级的单一读写锁(虽然在某些情况下,插入操作是可以绕过锁的。)

Oracle 中的事务隔离等级

Oracle 只支持三种事务隔离级别,即提交读、可序列化和只读。在 Oracle 中,提交读是默认的,它使用快照语义。

类似于 PostgreSQL,Oracle 并不提供未提交读,永不允许脏读。

可重复读并不在 Oracle 的支持列表中。如果你需要在 Oracle 中具有该行为,你的事务隔离级别需要被设置为可序列化。

只读是 Oracle 所独有的事务隔离级别。但是对此并没有很好的文档,手册中只有如下描述:

只读事务只能看见那些在事务开始阶段就被提交的改变,不允许 INSERT、UPDATE 和 DELETE 语言。

对其它两种事务隔离级别的更多信息,参见 Oracle 官方文档第13 章“数据并发和一致性”

DB2 中的事务隔离级别

DB2 具有四种隔离级别,分别称为可重复读、读稳定性、游标稳定性和未提交读。这四种级别并不与上述四种 ANSI 术语一一对应。

可重复读对应于 ANSI SQL 中的可序列化,意味着不可能存在脏读。

读稳定性对应于 ANSI SQL 中的可重复读。

游标稳定性用于提交读,是 DB2 的默认设置配置。对于 9.7 版快照语义生效。而在 9.7 的前期版本中,DB2 使用类似于 SQL Server 的锁机制。

未提交读在很大程度上类似于 SQL Server 中的未提交读,也允许脏读。手册中推荐仅在只读表上使用未提交读,或是用在“可以看到未被其它应用提交的数据时”。

更多信息参见“事务隔离级别”。

MongoDB 中的事务隔离级别

正如前文所提到的,MongoDB 不支持事务。在其手册中对此是这样描述的:

因为在 MongoDB 中对单一文档的操作是原子的,两阶段提交只能提供类事务语义。在两阶段提交或回滚期间,应用可在中间点返回中间数据。

事实上这意味着 MongoDB 使用脏读语义,具有双倍或丢失记录的可能性。

CouchDB 中的事务隔离等级

CouchDB 也不支持事务。但是不同于 MongoDB 的是,它使用了多版本并发控制去避免脏读。

读取请求将总是在请求开始时就能看到数据库的最新快照。

这所给予 CouchDB 的事务隔离等级,等价于具有快照语义的提交读。

更多的信息参见“最终一致性”。

Couchbase Server 的事务隔离级别

Couchbase Server 常被混淆为 CouchDB,但它是一种完全不同的产品。就索引而言,它并未提供任何形式的隔离。

当执行更新操作时,Couchbase Server 仅更新主索引,或称其为“真实的表”。所有的二级索引将被延迟更新。

虽然在 Couchbase Server 文档并没有明确说明,看上去它在构建索引时使用了快照,如果确是如此,脏读应该不成为问题。但是由于索引的延迟更新,在 Couchbase Server 中仍不能获得真正的提交读事务隔离级别。

和许多的 NoSQL 数据库一样,Couchbase Server 并不直接支持事务。但是你确实可以使用显式锁,但锁只能在被自动丢弃前维持 30 秒的时间。

更多的信息参见“对条目上锁”、“你所应知道的关于Couchbase 架构的所有事情”和“ Couchbase 视图引擎的内幕”。

Cassandra 中的事务隔离级别

Cassandra 1.0 隔离了甚至是对一行的写入操作。因为字段是被逐一更新的,所以可以终止对旧值和新值混合在一起的记录的读取。

从 1.1 版本开始,Cassandra 提供了“行级隔离”。这让 Cassandra 具有等同于其它的数据库中被称为“未提交读”的隔离级别。Cassandra 并未提供更高级别的隔离。

更多的信息参见“关于事务和并发控制”。

了解你的数据库的事务隔离级别

正如从上述实例中可看到的,仅从ACID 和非ACID 角度考虑你的数据库是不够的。你的确需要去知道你的数据库应在何种情况下支持何种的事务隔离级别。

关于作者

Jonathan Allen的首份工作是在上世纪九十年代末做诊所的 MIS 项目,Allen 将项目逐步由 Access 和 Excel 升级到企业级的解决方法。在从事为财政部门编写自动交易系统代码的工作五年之后,他成为项目顾问,参与了包括机器人仓库 UI、癌症研究软件中间层、主要房地产保险企业的大数据需求等在内的各种行业项目。在闲暇时间,他喜欢研究源于 16 世纪的武术,并为其撰写文章。

查看英文原文: A Quick Primer on Isolation Levels and Dirty Reads


感谢冬雨对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-11-14 17:585920
用户头像

发布了 227 篇内容, 共 78.9 次阅读, 收获喜欢 28 次。

关注

评论

发布
暂无评论
发现更多内容

下一代架构?从组装式企业到组装式应用

华为云开发者联盟

云计算 后端 数字化 华为云 12 月 PK 榜

省会城市昆明分布式光伏项目落地 引领低碳化转型实践

极客天地

了不起的程序员们,瞧,你的 2023 年度惊喜终于来了!

图灵社区

程序员

ClickHouse 挺快,esProc SPL 更快

王磊

低碳正在成为春城的新名片

极客天地

如何在滑至页面底端添加提示?

Towify

微信小程序 无代码

如何使用 Towify 在小程序中实现勾选用户协议后登录?

Towify

微信小程序 无代码

Flutter for Web 首次首屏优化——JS 分片优化

阿里巴巴终端技术

flutter 前端 Web 客户端

从数据治理到数据应用,制造业企业如何突破数字化转型困境丨行业方案

袋鼠云数栈

数字化转型

2023年中国企业数字化技术应用十大趋势

易观分析

企业 数字化

Tapdata 携手阿里云,实现数据平滑上云以及毫秒级在线查询和检索能力

云布道师

阿里云

人工智能顶会AAAI 2023放榜!网易伏羲7篇论文入选

网易伏羲

人工智能

火山引擎DataTester:无需研发人力,即刻开启企业A/B实验

字节跳动数据平台

A/B测试

【服务故障问题排查心得】「内存诊断系列」Docker容器经常被kill掉,k8s中该节点的pod也被驱赶,怎么分析?

码界西柚

Docker Linux 12 月 PK 榜 容器内存问题

2023年ha软件采购就选Skybility HA!6大优势看这里!

行云管家

高可用 ha 双机热备

选择合适的BI工具,解决中国式报表难题

对不起该用户已成仙‖

IAA品类洞察:扫描品类加快变现,如何抓住增长机遇?

易观分析

广告业 IAA

VoneBaaS与飞腾CPU完成产品兼容性互认证

旺链科技

区块链 产业区块链 VoneBaaS 12 月 PK 榜

OpenMLDB 贡献者任务第六期 | 暖冬时节,活力继续

第四范式开发者社区

人工智能 机器学习 数据库 开源 特征

浅析静态应用安全测试

华为云开发者联盟

测试 开发 华为云 12 月 PK 榜

熹乐科技范维肖CC:基于开源 YoMo 框架构建“全球同服”的 Realtime Metaverse Application

声网

框架 #开源

chatGPT实战之「基于你的数据库,为你智能生成SQL」

非喵鱼

Java MySQL sql openai ChatGPT

HIFIVE音加加提供曲库、评分、修音功能的K歌SDK-iOS版本

曲多多(嗨翻屋)版权音乐

ios 泛娱乐 版权音乐 K歌 K歌SDK

广告倒排服务极致优化

百度Geek说

架构 数据结构 后端 12 月 PK 榜

强化学习调参技巧二:DDPG、TD3、SAC算法为例:

汀丶人工智能

强化学习 深度强化学习 12月日更 12月月更

Wallys/industrial M.2 card/DR9074E vs DR90746E/Minipcie /qsdk/qcn9074

wallysSK

QCN9074 QCN9024 QCN9072 qcn9064

瓴羊Quick BI数据填报组件,实现智能化管理和高效挖掘利用

夏日星河

演讲实录|姚延栋:终止“试点炼狱”,智能汽车时代数字化转型与实践

YMatrix 超融合数据库

车联网 海量数据 超融合数据库 智能网联 YMatrix

两步开启研发团队专属ChatOps|极狐GitLab ChatOps 的设计与实践

极狐GitLab

团队管理 DevOps ChatOps 极狐GitLab ChatGPT

【合作案例】科协基地预约小程序 | 闵行区科普资源地图

天天预约

事务隔离级别和脏读的快速入门_架构_Jonathan Allen_InfoQ精选文章