我只是想知道,如果另一个查询编辑了您正在处理的那些行,大多数关系数据库是如何处理维护您的结果集的。例如,如果我执行了一个大约100k行的select,当我还在获取这些行时,另一个查询进来了,并对其中一行执行了update。我还没有读过update,我想知道数据库引擎是如何处理的。如果你只有一种类型的数据库的细节,那我还是想听听。
select
update
v9tzhpje1#
请查找多版本并发控制。不同的数据库有不同的方法来管理它。对于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。它在这个线程Database: What is Multiversion Concurrency Control (MVCC) and who supports it?的stackoverflow中解释
eh57zj3b2#
您在并发编程(Wikipedia concurrency control)中描述的一般目标是序列化(Wikipedia serializability):一种实现方式管理数据库,就好像事务以某种顺序不重叠地发生一样。这一点的重要性在于,只有这样,系统才能按照我们通常解释的代码所描述的方式运行。否则,操作的结果是所有进程并发运行的组合。然而,通过限制非正常、非隔离的所谓异常行为的类别,可以增加事务吞吐量。因此,这些实现技术也是适当的。(例如MVCC。)但要理解:这种非串行化行为 * 不 * 将一个事务与另一个事务隔离。(即所谓的“隔离”级别实际上是非隔离级别。)隔离的管理方法是,基于对共享资源的读写,将事务实现分解为多个片段,并将它们与来自其他事务的片段交错执行,其效果与非重叠执行的某个序列相同。粗略地说,可以“悲观地”“锁定”其他进程使其不能访问改变的资源并让它们等待,或者“乐观地”“版本控制”数据库并“回滚,”当更改不可协调(不可序列化)时,丢弃某些进程的工作。this answer中的一些技术是基于一个主要产品的实现者对可序列化性的理解。有关相关概念和技术,请参见维基百科文章或数据库教科书。例如Ramez Elmasri & Shamkant B. Navathe的数据库系统基础。(许多教科书、幻灯片和课程都是免费的。)(Two我的回答和对您问题的评论提到了MVCC。正如我所评论的,MVCC不仅只是一种实现技术,它甚至不支持事务序列化,即实际上隔离事务,好像每个事务都是一次完成的。它允许某些类型的错误(又名异常)。它必须与其他隔离技术混合。MVCC回答:评论和赞成票反映了一种混淆,一种流行的和有价值的技术,一种有用的和有限的 * 失败 *,以隔离每个问题与实际的核心问题和手段。)
wixjitnu3#
正如Jayadevan所指出的,* 最广泛使用的 * 数据库允许您在读取值的同时修改值,它们所使用的一般原则是multi-version concurrency control or MVCC。所有 * 广泛使用的现代RDBMS* 实现都支持读取正在更新的行,它们依赖于行版本控制的一些概念。具体细节在不同的实现中有所不同。(我将在这里解释PostgreSQL的更多内容,但您应该接受Jayadevan的答案,而不是我的答案)。PostgreSQL uses transaction ID ranges to track row visibility。因此,在一个表中会有一个元组的多个副本,但是任何给定的事务只能“看到”其中的一个副本。每个事务都有一个唯一的ID,新的事务具有新的ID。每个元组都有隐藏的xmin和xmax字段,这些字段跟踪哪些事务可以“看到”元组。插入是通过将元组's xmin,以便具有较低xid的事务知道在读取表时忽略该元组。删除是通过设置元组的xmax来实现的,以便具有较高xid的事务知道在读取表时忽略该元组。更新是通过有效地删除旧元组来实现的(设置xmax),然后插入一个新的元组(设置xmin),这样旧的事务处理仍然可以看到旧的元组,但新的事务处理可以看到新的元组。当没有正在运行的事务处理仍然可以看到已删除的元组时,vacuum将其标记为待覆盖的空闲空间。Oracle使用撤消和重做日志,所以在主表中只有元组的一个副本,想要找到旧版本的事务必须在日志中找到它。像PostgreSQL一样,它使用行版本控制,尽管我不太确定细节。现在几乎所有其他的数据库都使用类似的方法,那些过去依赖于锁定的数据库,比如旧的MS-SQL版本,已经转移到了MVCC。MyISAM表仍然依赖于表锁(但是它们也会吃掉你的数据,所以不要把它们用于你关心的任何事情)。一些嵌入式数据库,如SQLite,仍然只依赖于表锁--这往往需要较少的磁盘空间和I/O开销,代价是大大降低了并发性。一些数据库允许您绕过MVCC,如果您对表进行独占锁的话。(标记为社区wiki,因为我也对这个问题进行了接近投票)。您还应该阅读transaction isolation和locking上的PostgreSQL文档,以及您使用的其他数据库的类似文档。请参阅维基百科上关于isolation的文章。
xmin
xmax
vacuum
g6baxovj4#
快照隔离解决了您所描述的问题。如果您使用锁定,您可以像遍历数据库的迭代器一样看到相同的记录两次,因为在您进行扫描时,未锁定的记录会在您的脚下发生变化。Read committed isolation level with locking suffers from this problem。根据锁的粒度,WHERE predicate 可能会锁定匹配的页和元组,以便运行的读查询不会看到出现的幻像数据(幻像读)我实现了multiversion concurrency control in my Java project。事务被赋予了一个单调递增的时间戳,该时间戳从0开始,每次事务被中止时增加1。后面的事务具有更高的时间戳。当事务进入读取状态时,它只能看到时间戳小于或等于其自身的数据,并且是为该键提交的(或该元组的列)。(等于以便它可以看到自己的写入)当事务写入时,它会将该键的提交时间戳更新为该事务的时间戳。
4条答案
按热度按时间v9tzhpje1#
请查找多版本并发控制。不同的数据库有不同的方法来管理它。对于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。它在这个线程Database: What is Multiversion Concurrency Control (MVCC) and who supports it?的stackoverflow中解释
eh57zj3b2#
您在并发编程(Wikipedia concurrency control)中描述的一般目标是序列化(Wikipedia serializability):一种实现方式管理数据库,就好像事务以某种顺序不重叠地发生一样。
这一点的重要性在于,只有这样,系统才能按照我们通常解释的代码所描述的方式运行。否则,操作的结果是所有进程并发运行的组合。然而,通过限制非正常、非隔离的所谓异常行为的类别,可以增加事务吞吐量。因此,这些实现技术也是适当的。(例如MVCC。)但要理解:这种非串行化行为 * 不 * 将一个事务与另一个事务隔离。(即所谓的“隔离”级别实际上是非隔离级别。)
隔离的管理方法是,基于对共享资源的读写,将事务实现分解为多个片段,并将它们与来自其他事务的片段交错执行,其效果与非重叠执行的某个序列相同。粗略地说,可以“悲观地”“锁定”其他进程使其不能访问改变的资源并让它们等待,或者“乐观地”“版本控制”数据库并“回滚,”当更改不可协调(不可序列化)时,丢弃某些进程的工作。
this answer中的一些技术是基于一个主要产品的实现者对可序列化性的理解。有关相关概念和技术,请参见维基百科文章或数据库教科书。例如Ramez Elmasri & Shamkant B. Navathe的数据库系统基础。(许多教科书、幻灯片和课程都是免费的。)
(Two我的回答和对您问题的评论提到了MVCC。正如我所评论的,MVCC不仅只是一种实现技术,它甚至不支持事务序列化,即实际上隔离事务,好像每个事务都是一次完成的。它允许某些类型的错误(又名异常)。它必须与其他隔离技术混合。MVCC回答:评论和赞成票反映了一种混淆,一种流行的和有价值的技术,一种有用的和有限的 * 失败 *,以隔离每个问题与实际的核心问题和手段。)
wixjitnu3#
正如Jayadevan所指出的,* 最广泛使用的 * 数据库允许您在读取值的同时修改值,它们所使用的一般原则是multi-version concurrency control or MVCC。所有 * 广泛使用的现代RDBMS* 实现都支持读取正在更新的行,它们依赖于行版本控制的一些概念。
具体细节在不同的实现中有所不同。(我将在这里解释PostgreSQL的更多内容,但您应该接受Jayadevan的答案,而不是我的答案)。
PostgreSQL uses transaction ID ranges to track row visibility。因此,在一个表中会有一个元组的多个副本,但是任何给定的事务只能“看到”其中的一个副本。每个事务都有一个唯一的ID,新的事务具有新的ID。每个元组都有隐藏的
xmin
和xmax
字段,这些字段跟踪哪些事务可以“看到”元组。插入是通过将元组'sxmin
,以便具有较低xid的事务知道在读取表时忽略该元组。删除是通过设置元组的xmax
来实现的,以便具有较高xid的事务知道在读取表时忽略该元组。更新是通过有效地删除旧元组来实现的(设置xmax
),然后插入一个新的元组(设置xmin
),这样旧的事务处理仍然可以看到旧的元组,但新的事务处理可以看到新的元组。当没有正在运行的事务处理仍然可以看到已删除的元组时,vacuum
将其标记为待覆盖的空闲空间。Oracle使用撤消和重做日志,所以在主表中只有元组的一个副本,想要找到旧版本的事务必须在日志中找到它。像PostgreSQL一样,它使用行版本控制,尽管我不太确定细节。
现在几乎所有其他的数据库都使用类似的方法,那些过去依赖于锁定的数据库,比如旧的MS-SQL版本,已经转移到了MVCC。
MyISAM表仍然依赖于表锁(但是它们也会吃掉你的数据,所以不要把它们用于你关心的任何事情)。
一些嵌入式数据库,如SQLite,仍然只依赖于表锁--这往往需要较少的磁盘空间和I/O开销,代价是大大降低了并发性。一些数据库允许您绕过MVCC,如果您对表进行独占锁的话。
(标记为社区wiki,因为我也对这个问题进行了接近投票)。
您还应该阅读transaction isolation和locking上的PostgreSQL文档,以及您使用的其他数据库的类似文档。请参阅维基百科上关于isolation的文章。
g6baxovj4#
快照隔离解决了您所描述的问题。如果您使用锁定,您可以像遍历数据库的迭代器一样看到相同的记录两次,因为在您进行扫描时,未锁定的记录会在您的脚下发生变化。
Read committed isolation level with locking suffers from this problem。
根据锁的粒度,WHERE predicate 可能会锁定匹配的页和元组,以便运行的读查询不会看到出现的幻像数据(幻像读)
我实现了multiversion concurrency control in my Java project。事务被赋予了一个单调递增的时间戳,该时间戳从0开始,每次事务被中止时增加1。后面的事务具有更高的时间戳。当事务进入读取状态时,它只能看到时间戳小于或等于其自身的数据,并且是为该键提交的(或该元组的列)。(等于以便它可以看到自己的写入)
当事务写入时,它会将该键的提交时间戳更新为该事务的时间戳。