【问题标题】:How do databases maintain results if another query edits those results如果另一个查询编辑这些结果,数据库如何维护结果
【发布时间】:2014-09-01 14:15:39
【问题描述】:

我只是想知道如果另一个查询编辑了您正在处理的那些行,大多数关系数据库如何处理维护您的结果集。例如,如果我执行类似 100k 行的 select 并且当我仍在获取那些另一个查询并在尚未读取的行之一上执行 update 时,update 不会在获取这些行时可以看到,我想知道数据库引擎是如何处理的。如果您只有一种类型的数据库的详细信息,那我还是想听听。

【问题讨论】:

  • 当心:MVCC 不仅只是一种 实现技术,它甚至不支持事务序列化,即实际上隔离事务,就好像每个事务都一次性完成一样。它允许某些类型的错误。它必须与其他技术混合使用才能完全隔离。
  • 在我对关于序列化实现的 stackoverflow 答案的回答中添加了一个链接(在编辑处)。

标签: mysql database postgresql relational-database mariadb


【解决方案1】:

请查找多版本并发控制。不同的数据库有不同的管理方法。对于 MySQL、InnoDB,你可以试试http://dev.mysql.com/doc/refman/5.0/en/innodb-multi-versioning.html。 PostgreSQL - https://wiki.postgresql.org/wiki/MVCC。这里有一个很棒的演示 - http://momjian.us/main/writings/pgsql/mvcc.pdf。在这个线程的stackoverflow中有解释Database: What is Multiversion Concurrency Control (MVCC) and who supports it?

【讨论】:

    【解决方案2】:

    您在并发编程 (Wikipedia concurrency control) 中描述的一般目标是序列化 (Wikipedia serializability):实现管理数据库就像事务以某种顺序发生而没有重叠一样。

    这样做的重要性在于,只有这样系统才能按照我们通常解释的代码所描述的方式运行。否则,操作的结果是所有同时执行的进程的组合。然而,通过具有有限类别的非正常非隔离所谓的异常行为,可以增加交易吞吐量。所以那些实现技术也是中肯的。 (例如 MVCC。)但请理解:这种非序列化行为将一个事务与另一个事务隔离。 (即所谓的“隔离”级别实际上是非隔离级别。)

    隔离是通过基于读取和写入共享资源将事务实现分解成片段并与来自其他事务的片段交错执行的方式来管理的,其效果与某些非重叠执行序列相同。粗略地说,人们可以“悲观地”“锁定”其他进程与更改的资源并让它们等待或“乐观地”“版本化”数据库并在更改不可调和(不可序列化)时“回滚”(丢弃)某些进程的工作.

    this answer 中提供了一些基于实现者对主要产品的可串行化理解的技术。有关相关概念和技术,请参阅 Wikipedia 文章或数据库教科书。例如 Ramez Elmasri 和 Shamkant B. Navathe 的数据库系统基础。 (许多教科书、幻灯片和课程都是在线免费的。)

    (对您的问题的两个答案和一个评论提到了 MVCC。正如我所评论的,MVCC 不仅只是一种实现技术,它甚至不支持事务序列化,即实际上隔离事务,就好像每个事务都一次完成一样。它允许某些类型的错误(也称为异常)。它必须与其他隔离技术混合使用。MVCC 答案、cmets 和 upvoting 反映了一种流行且有价值的技术之间的混淆,以解决有用且有限的失败根据您的问题与实际的核心问题和方法进行隔离。)

    【讨论】:

      【解决方案3】:

      正如 Jayadevan 所指出的,最广泛使用的数据库允许您在读取值时修改值的一般原则是 multi-version concurrency control or MVCC。所有广泛使用的现代 RDBMS 实现都支持读取正在更新的行,都依赖于某些行版本控制概念。

      不同实现的细节有所不同。 (我会在这里多解释一下 PostgreSQL,但你应该接受 Jayadevan 的回答,而不是我的回答)。

      PostgreSQL uses transaction ID ranges to track row visibility。所以一个表中会有多个元组副本,但任何给定的事务只能“看到”其中一个。每个事务都有一个唯一的 ID,较新的事务具有较新的 ID。每个元组都隐藏了xminxmax 字段,用于跟踪哪些事务可以“看到”元组。插入是通过设置元组的xmin 来实现的,这样具有较低xid 的事务就知道在读取表时忽略元组。删除是通过设置元组的xmax 来实现的,这样具有更高xid 的事务就知道在读取表时忽略元组。更新是通过有效地删除旧元组(设置xmax)然后插入一个新元组(设置xmin)来实现的,这样旧事务仍然可以看到旧元组,但新事务会看到新元组。当没有正在运行的事务仍然可以看到已删除的元组时,vacuum 将其标记为要覆盖的可用空间。

      Oracle 使用 undo 和 redo 日志,所以主表中只有一个元组的副本,想要找到旧版本的事务必须去日志中找到它。像 PostgreSQL 一样,它使用行版本控制,虽然我不太确定细节。

      如今,几乎所有其他数据库都使用类似的方法。过去依赖锁定的那些,例如旧的 MS-SQL 版本,已经迁移到 MVCC。

      MySQL 将 MVCC 与 InnoDB 表一起使用,这些表现在是默认的。 MyISAM 表仍然依赖表锁定(但它们也会吃掉你的数据,所以不要将它们用于你关心的任何事情)。

      一些嵌入式数据库,如 SQLite,仍然只依赖表锁定 - 这往往需要更少的磁盘空间浪费和 I/O 开销,但代价是大大降低了并发性。如果您对表进行排他锁,某些数据库允许您绕过 MVCC。

      (标记为社区 wiki,因为我也对这个问题投了赞成票)。

      您还应该阅读transaction isolationlocking 上的PostgreSQL 文档,以及您使用的其他数据库的类似文档。请参阅isolation 上的维基百科文章。

      【讨论】:

      • MVCC 绝对是不是“一般原则”,请参阅我对这个问题的评论。
      • @philipxy 当然,还有更多内容——一旦你开始涵盖事务回滚日志、隔离级别、锁定、谓词锁定和枢轴(在 PostgreSQL 中按照 SERIALIZABLE)等,你很快就会进入“我”米写一本书”的领域。 对于大多数数据库系统 MVCC 现在是使读/写并发成为可能的重要组成部分。也就是说,我肯定会添加指向 Pg 的关于事务隔离的文档的链接,其中提供了更多信息。
      • 将 MVCC 称为一般原则是完全错误的,并且根据用户的问题说它实现了隔离是错误的。 MVCC 绝不是形成对该问题的理解的核心。围绕它制定答案从根本上歪曲了这些问题。您的评论进一步说明了这一点:回滚日志和持久性与隔离正交; “隔离” [原文如此] 级别都是关于(再次)-隔离;而levels & MVCC 使得更高的吞吐量成为可能,而不是并发。同意@legion 应该读一本书。同意这是一种相关(有限)的技术。
      • 如果您在后续回答中讨论您认为需要涵盖的主题,将会很有帮助;我认为评论聊天有点多。或者,如果您愿意,可以随意修改上面的 CW 以使您满意。
      • 同意。它正在进行中。如果有机会,我会在“粗略地说”中添加细节。
      猜你喜欢
      • 2015-03-16
      • 2011-12-02
      • 2011-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-30
      • 1970-01-01
      相关资源
      最近更新 更多