「数据库架构」三分钟搞懂事务隔离级别和脏读

简介: 「数据库架构」三分钟搞懂事务隔离级别和脏读

重要要点


  • 仅凭ACID或非ACID来思考,还需要知道数据库支持的隔离级别。
  • 标榜为“最终一致”的某些数据库可能返回与任何时间点不一致的结果。
  • 一些数据库提供的隔离级别比您要求的更高。
  • 脏读会导致您看到同一记录的两个版本,或者完全错过一条记录。
  • 在单个事务中多次重新运行查询时,可能会出现幻像行。

最近,当开发人员David Glasser了解MongoDB默认执行脏读的糟糕方式时,MongoDB再次成为Reddit的佼佼者。在本文中,我们将解释什么是隔离级别和脏读以及如何在流行的数据库中实现它们。

在ANSI SQL中,有四个标准隔离级别:可序列化,可重复读取,已提交读取和未提交读取。


许多数据库的默认设置为“读取已提交”,它仅保证在进行该事务时您不会看到过渡中的数据。它通过在读取期间短暂地获取锁来实现此目的,同时保持写入锁直到事务被提交。

如果您需要在一个事务中多次重复相同的读取操作,并且想要合理地确定它总是返回相同的值,则需要在整个持续时间内保持读取锁定。使用“可重复读取”隔离级别时,将自动为您完成此操作。

我们说“可重复读”是“合理肯定的”,因为可能存在“幻读”。使用where子句(例如“ WHERE Status = 1”)执行查询时,可能会发生幻像读取。这些行将被锁定,但是没有什么阻止添加符合条件的新行。术语“幻像”适用于第二次执行查询时出现的行。

为了绝对确保同一事务中的两次读取返回相同的数据,可以使用Serializable隔离级别。这使用“范围锁”,如果新行与打开的事务中的WHERE子句匹配,则可以防止添加这些行。

通常,隔离级别越高,由于锁争用而导致的性能越差。因此,为了提高读取性能,某些数据库还支持“读取未提交”。此隔离级别忽略锁(实际上在SQL Server中称为NOLOCK)。结果,它会执行脏读。

脏读问题

在讨论脏读之前,您必须了解表实际上并不存在于数据库中。表只是一个逻辑构造。实际上,您的数据存储在一个或多个索引中。在大多数关系数据库中,主索引被称为“聚集索引”或“堆”。(对于NoSQL数据库,术语有所不同。)因此,在执行插入操作时,它需要在每个索引中插入一行。执行更新时,数据库引擎仅需要触摸引用正在更改的列的索引。但是,它通常必须对每个索引执行两次操作,即从旧位置删除和向新位置插入。

在下图中,您可以看到一个简单的表和一个执行计划,其中更新了两个对象IX_Customer_State和PK_Customer。由于全名未更改,因此跳过了IX_Customer_FullName索引。



注意:在SQL Server中,PK前缀是指主键,它通常也是用于聚集索引的键。IX用于非聚集索引。其他数据库有其自己的约定。

通过这种方式,让我们看一下脏读可能导致数据不一致的多种方式。

未提交的读取最容易理解。通过忽略写锁定,使用“读未提交”的SELECT语句可以在事务完全提交之前看到新插入或更新的行。如果该转换然后被回滚,那么从逻辑上讲,SELECT操作将返回从不存在的数据。

在更新操作期间移动数据时,会发生两次读取。假设您正在按州读取所有客户记录。如果上述更新语句是在您加州记录的时间与您阅读德克萨斯州记录的时间之间执行的,则您可以看到客户1253两次;一次使用旧值,一次使用新值。


漏读的发生方式相同。如果我们将客户1253移到德克萨斯州到阿拉斯加,再按州选择数据,则可能会完全错过该记录。这就是David Glasser的MongoDB数据库所发生的事情。通过在更新操作期间从索引读取,查询会丢失记录。


根据数据库的设计方式和特定的执行计划,脏读也会干扰排序。例如,如果执行引擎收集指向所有感兴趣的行的一组指针,然后更新一行,然后执行引擎实际上使用所述指针从原始位置复制数据,则可能发生这种情况。

快照隔离或行级别版本控制

为了提供良好的性能同时避免脏读问题,许多数据库都支持快照隔离语义。在快照隔离下运行时,当前事务无法查看在当前事务之前启动的任何其他事务的结果。

这是通过制作要修改的行的临时副本来完成的,而不是仅仅依靠锁。这通常称为“行级版本控制”。

当请求读取提交隔离时,大多数支持快照隔离语义的数据库都会自动使用它。

SQL Server中的隔离级别

SQL Server支持所有四个ANSI SQL隔离级别以及一个显式的快照级别。取决于使用READ_COMMITTED_SNAPSHOT选项配置数据库的方式,“已提交读”也可以使用快照语义。

在启用此选项之前和之后,请彻底测试数据库。虽然它可以提高读取性能,但可能会减慢写入速度。如果您的tempdb处于慢速驱动器上,则尤其如此,因为这是行的旧版本存储的地方。

臭名昭著的NOLOCK指令(可应用于SELECT语句)与在设置为“读取未提交”的事务中运行具有相同的效果。由于SQL Server 2000和更早版本尚未提供行级版本控制,因此该版本已大量使用。尽管不再需要或不建议使用,但该习惯仍然存在。

有关更多信息,请参见SET TRANSACTION ISOLATION LEVEL(Transact-SQL)。

PostgreSQL中的隔离级别

虽然PostgreSQL正式支持所有四个ANSI隔离级别,但实际上它只有三个。每当查询请求“读取未提交”时,PostgreSQL都会以静默方式将其升级为“读取已提交”。因此PostgreSQL不允许脏读。

当选择级别Read Uncommitted时,您实际上会获得Read Committed,并且在Repeatable Read的PostgreSQL实现中不可能进行幻像读取,因此实际的隔离级别可能比您选择的严格。这是SQL标准所允许的:四个隔离级别仅定义了哪些现象一定不能发生,它们没有定义哪些现象必须发生。

PostgreSQL没有明确提供快照隔离。而是在使用“读取已提交”时自动发生。这是因为PostgreSQL从一开始就设计为具有多版本并发控制。

在9.1版之前,PostgreSQL不提供可序列化的事务,并且会静默地将它们降级为“可重复读”。当前没有支持的PostgreSQL版本仍然具有此限制。

有关更多信息,请参见13.2。事务隔离。

MySQL中的隔离级别

InnoDB默认为“可重复读取”,但提供所有四个ANSI SQL隔离级别。读取已提交使用快照隔离语义。

有关InnoDB的更多信息,请参见15.3.2.1事务隔离级别。

使用MyISAM存储引擎时,根本不支持事务。相反,它在表级别使用一个读写器锁。(尽管在某些情况下,插入操作可以绕过锁。)

Oracle中的隔离级别

Oracle仅支持3个事务级别:读已提交,可序列化和只读。在Oracle中,“默认值为读已提交”,它使用快照语义。

像PostgreSQL一样,Oracle不提供“读未提交”。绝对不允许脏读。

列表中还缺少“可重复读取”。如果您在Oracle中需要这种行为,则需要将隔离级别设置为Serializable。

Oracle唯一的隔离级别是只读。它没有很好的文档记录,手册只说:

只读事务仅查看那些在事务开始时提交的更改,并且不允许INSERT,UPDATE和DELETE语句。

有关其他两个隔离级别的更多信息,请参阅13数据并发性和一致性。

DB 2中的隔离级别

DB 2具有4个隔离级别,分别称为重复读取,读取稳定性,游标稳定性和未提交读取。但是,它们并不直接映射到ANSI术语。

可重复读是ANSI SQL称为可序列化的。也就是说,幻像读取是不可能的。

读取稳定性映射到ANSI SQL的可重复读取。

默认情况下,“游标稳定性”用于“读取已提交”。从9.7版开始,快照语义已生效。以前,它将使用类似于SQL Server的锁。

未提交读允许进行脏读,就像SQL Server的未提交读一样。该手册仅建议将其用于只读表,或者“在查看其他应用程序未提交的数据没有问题时”。

有关更多信息,请参见隔离级别。

MongoDB中的隔离级别

如前所述,MongoDB不支持事务。从手册中

由于MongoDB仅单文档操作是原子操作,因此两阶段提交只能提供类似于事务的语义。在两阶段提交或回滚期间,应用程序有可能在中间点返回中间数据。

实际上,这意味着MongoDB使用脏读语义,其中包括记录可能翻倍或丢失的可能性。

CouchDB中的隔离级别

CouchDB也不支持交易。但是与MongoDB不同,它确实使用多版本并发控制来防止脏读。

读取请求在请求开始时始终会看到您数据库的最新快照。

这使CouchDB等效于具有Snapshot语义的Read Committed隔离级别。

有关更多信息,请参见最终一致性。

Couchbase服务器中的隔离级别

尽管经常与CouchDB混淆,但Couchbase Server是一个非常不同的产品。对于索引,它没有隔离的概念。

在执行更新时,它仅更新主索引,如果您愿意,也可以更新“真实表”。所有二级索引均会延迟更新。

该文档尚不清楚,但在建立索引时似乎使用快照。如果是这样,脏读应该不是问题。但是由于延迟索引更新,您仍然无法获得真正的“读取已提交”隔离级别。

与许多NoSQL数据库一样,它不直接支持事务。但是,您确实可以使用显式锁。这些只能保留30秒,然后自动丢弃。

有关更多信息,请参阅锁定项目,您需要了解的有关Couchbase体系结构的所有信息以及Couchbase View Engine内部。

Cassandra的隔离级别

在Cassandra 1.0中,甚至没有隔离写入单个行。字段是一一更新的,因此您最终可能会读取包含新旧值的记录。

从1.1版开始,Cassandra提供“行级隔离”。这使其达到与其他数据库称为“读取未提交”的相同隔离级别。更高级别的隔离是不可能的。

有关更多信息,请参见关于事务和并发控制。

了解数据库的隔离级别

从上面的示例中可以看到,仅将数据库视为ACID或非ACID是不够的。您确实需要知道它支持什么隔离级别以及在什么情况下。


相关文章
|
7月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
6月前
|
中间件 关系型数据库 Go
Go语言数据库编程:数据迁移与事务控制
本文介绍了《Go语言实战指南》中关于数据库编程的核心内容,涵盖使用 GORM 进行数据迁移与事务控制。主要内容包括:AutoMigrate 方法自动创建或更新表结构;事务控制的自动与手动实现方式;事务隔离级别的设置;以及在 Gin 框架中统一管理事务的实践建议。适合开发阶段的数据库结构管理和事务性操作需求。
|
6月前
|
存储 关系型数据库 数据库
高性能云盘:一文解析RDS数据库存储架构升级
性能、成本、弹性,是客户实际使用数据库过程中关注的三个重要方面。RDS业界率先推出的高性能云盘(原通用云盘),是PaaS层和IaaS层的深度融合的技术最佳实践,通过使用不同的存储介质,为客户提供同时满足低成本、低延迟、高持久性的体验。
|
7月前
|
存储 Cloud Native 关系型数据库
PolarDB开源:云原生数据库的架构革命
本文围绕开源核心价值、社区运营实践和技术演进路线展开。首先解读存算分离架构的三大突破,包括基于RDMA的分布式存储、计算节点扩展及存储池扩容机制,并强调与MySQL的高兼容性。其次分享阿里巴巴开源治理模式,涵盖技术决策、版本发布和贡献者成长体系,同时展示企业应用案例。最后展望技术路线图,如3.0版本的多写多读架构、智能调优引擎等特性,以及开发者生态建设举措,推荐使用PolarDB-Operator实现高效部署。
411 4
|
8月前
|
负载均衡 算法 关系型数据库
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL集群架构负载均衡故障排除与解决方案
本文深入探讨 MySQL 集群架构负载均衡的常见故障及排除方法。涵盖请求分配不均、节点无法响应、负载均衡器故障等现象,介绍多种负载均衡算法及故障排除步骤,包括检查负载均衡器状态、调整算法、诊断修复节点故障等。还阐述了预防措施与确保系统稳定性的方法,如定期监控维护、备份恢复策略、团队协作与知识管理等。为确保 MySQL 数据库系统高可用性提供全面指导。
|
10月前
|
人工智能 JavaScript 安全
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
490 13
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
|
9月前
|
存储 SQL 并行计算
【赵渝强老师】达梦数据库MPP集群的架构
达梦数据库提供大规模并行处理(MPP)架构,以低成本实现高性能并行计算,满足海量数据存储和复杂查询需求。DM MPP采用完全对等无共享体系,消除主节点瓶颈,通过多节点并行执行提升性能。其执行流程包括主EP生成计划、分发任务、各EP并行处理及结果汇总返回。为确保高可用性,建议结合数据守护部署。
304 0
|
10月前
|
SQL 弹性计算 安全
【上云基础系列04】基于标准架构的数据库升级
本文回顾了业务上云从基础到进阶的理念,涵盖基础版和全栈版架构。在“入门级:上云标准弹性架构基础版”的基础上,本文针对数据库升级,重点介绍了高可用数据库架构的升级方案,确保数据安全和业务连续性。最后,附有详细的“上云标准弹性架构”演进说明,帮助用户选择合适的架构方案。
|
9月前
|
SQL 数据库 索引
【YashanDB数据库】大事务回滚导致其他操作无法执行,报错YAS-02016 no free undo blocks
大事务回滚导致其他操作无法执行,报错YAS-02016 no free undo blocks
|
存储 SQL Apache
Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库
Apache Doris 是一个基于 MPP 架构的高性能实时分析数据库,以其极高的速度和易用性著称。它支持高并发点查询和复杂分析场景,适用于报表分析、即席查询、数据仓库和数据湖查询加速等。最新发布的 2.0.2 版本在性能、稳定性和多租户支持方面有显著提升。社区活跃,已广泛应用于电商、广告、用户行为分析等领域。
Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库