数据库多版本并发控制

多版本并发控制MVCC

MVCC是一种通过保存数据在某个时间点的快照来实现并发控制的方法。它可以提高数据库的并发性能,避免读写冲突,降低死锁的概率,保证事务的隔离性和一致性²。

MVCC的基本思想是,每个事务都有一个唯一的时间戳,表示事务的开始时间或提交时间。每个数据对象也有两个时间戳,一个表示最后一次修改该对象的事务的开始时间或提交时间,另一个表示最后一次删除该对象的事务的开始时间或提交时间。这样,每个数据对象就有多个版本,每个版本对应一个事务对该对象的修改或删除操作。

当一个事务要访问一个数据对象时,它会根据自己的时间戳和数据对象的时间戳来判断是否可以访问该对象。不同的数据库系统可能有不同的判断规则,但一般来说,有以下几种情况:

  • 如果事务的时间戳早于数据对象的修改时间戳,说明该对象在事务开始后被修改过,因此事务不能访问该对象,而应该访问该对象的旧版本。
  • 如果事务的时间戳晚于数据对象的删除时间戳,说明该对象在事务开始前被删除了,因此事务不能访问该对象。
  • 如果事务的时间戳介于数据对象的修改时间戳和删除时间戳之间,说明该对象在事务开始前被修改过,并且在事务开始后被删除了,因此事务也不能访问该对象。
  • 如果以上情况都不满足,说明该对象在事务开始前没有被修改或删除过,并且在事务开始后也没有被修改或删除过,因此事务可以访问该对象。

通过这样的方式,MVCC可以实现以下几个优势:

  • MVCC可以实现非阻塞的读操作,即读操作不需要加锁也可以保证读取到一致性的数据快照。这样可以提高数据库的并发性能,避免读写之间互相阻塞。
  • MVCC可以实现乐观并发控制,即假设冲突是少见的,因此不需要在写操作前加锁,而是在提交时检查是否有冲突。这样可以降低死锁的概率,减少锁竞争和等待开销。
  • MVCC可以实现可重复读(REPEATABLE READ)和读已提交(READ COMMITTED)两种隔离级别。可重复读是指,在同一个事务中,对同一个数据多次读取得到的结果是一致的。读已提交是指,在同一个事务中,只能读取到其他已经提交了的事务所做的修改。MVCC可以通过给每个事务分配不同的数据快照来实现这两种隔离级别。

MVCC是一种有效地解决数据库并发控制问题的方法。它利用了数据版本和时间戳来判断事务是否可以访问某个数据对象。它可以提高数据库的并发性能,避免读写冲突,降低死锁的概率,保证事务的隔离性和一致性。

Snapshot

快照snapshot是一种备份方式,它可以在不影响数据库正常运行的情况下,对数据库中的数据在某个时间点的状态进行保存。快照snapshot可以用于数据恢复,报表生成,测试环境搭建等场景。

快照snapshot的原理是利用数据版本和时间戳来记录数据的变化。不同的数据库系统可能采用不同的快照技术,但一般可以分为以下几种类型:

  • 基于拷贝写入(Copy-on-Write)的快照技术:这种技术是在创建快照时,将源数据库中的数据完整地复制到一个新的位置,然后在源数据库中进行修改时,将原始数据页保存到快照中。这样,快照中的数据就保持了创建时的状态。这种技术的优点是创建快照很快,不需要冻结源数据库,缺点是修改源数据库时会增加I/O开销,而且每个快照都需要占用相当于源数据库大小的空间。
  • 基于写重定向(Redirect-on-Write)的快照技术:这种技术是在创建快照时,只记录源数据库中数据的位置信息,然后在源数据库中进行修改时,将新数据写入到一个新的位置,并更新位置信息。这样,快照中的数据就通过位置信息指向了源数据库中未修改的数据⁵。这种技术的优点是修改源数据库时不需要复制数据页,节省了I/O开销和空间开销,缺点是创建快照时需要冻结源数据库,而且随着修改次数增加,位置信息会变得复杂和混乱。
  • 基于首次写入变更(Copy-on-First-Write)的快照技术:这种技术是在创建快照时,也只记录源数据库中数据的位置信息,但是在源数据库中进行修改时,只有第一次修改才会将原始数据页保存到快照中,之后的修改都不会影响快照中的数据。这样,快照中的数据就通过位置信息和复制数据页共同表示了创建时的状态。这种技术的优点是创建快照很快,不需要冻结源数据库,并且可以减少复制数据页的数量和空间开销,缺点是还是会有一定的I/O开销,并且需要维护多个快照之间的关联性。

快照snapshot是一种实现数据库备份和恢复的有效方法。它利用了数据版本和时间戳来保存数据在某个时间点的状态。不同类型的快照技术有各自的优缺点和适用场景。

Snapshot Isolation

快照隔离是一种事务的隔离级别,它保证了事务的读操作可以看到数据库在某个时间点的一致性快照,而不受其他事务的修改影响。快照隔离可以提高数据库的并发性能,避免读写冲突,但也存在一些缺点和问题。

快照隔离的基本思想是利用数据版本和时间戳来记录数据的变化。不同的数据库系统可能采用不同的快照技术,但一般可以分为以下几个步骤:

  • 当一个事务开始时,它会获取一个开始时间戳(start timestamp),表示该事务可以看到的数据快照的时间点。这个时间戳可以是事务第一次读操作之前的任意时间,也可以是一个很旧的时间,以便读取历史数据。
  • 当一个事务要访问一个数据对象时,它会根据自己的开始时间戳和数据对象的版本信息来判断是否可以访问该对象。一般来说,只有在数据对象的创建或修改时间戳小于或等于事务的开始时间戳,并且数据对象的删除时间戳大于或不存在时,才可以访问该对象。这样就保证了事务读取到的数据是在自己开始之前已经提交了的,并且在自己开始之后没有被修改或删除了。
  • 当一个事务要修改或删除一个数据对象时,它会创建一个新版本的数据对象,并附上自己的开始时间戳作为修改或删除时间戳。这样就保证了不会覆盖其他事务已经提交了的数据版本。
  • 当一个事务准备提交时,它会获取一个提交时间戳(commit timestamp),表示该事务完成修改或删除操作的时间点。这个时间戳需要比现存的所有开始时间戳和提交时间戳都大,以保证全局有序。
  • 当一个事务提交时,它会进行冲突检查,即检查是否有其他事务在自己的开始时间戳和提交时间戳之间提交了与自己写集合有交集的数据。如果没有冲突,就把自己的提交时间戳更新到数据对象的版本信息中,并释放锁定资源;如果有冲突,就放弃提交并回滚操作。这样就保证了不会出现丢失更新(lost update)异常。

通过这样的方式,快照隔离可以实现以下几个优点:

  • 快照隔离可以实现非阻塞的读操作,即读操作不需要加锁也可以保证读取到一致性的数据快照。这样可以提高数据库的并发性能,避免读写之间互相阻塞。
  • 快照隔离可以实现乐观并发控制,即假设冲突是少见的,因此不需要在写操作前加锁,而是在提交时检查是否有冲突。这样可以降低死锁的概率,减少锁竞争和等待开销。
  • 快照隔离可以实现可重复读(repeatable read)和读已提交(read committed)两种隔离级别。可重复读是指,在同一个事务中,对同一个数据多次读取得到的结果是一致的。读已提交是指,在同一个事务中,只能读取到其他已经提交了的事务所做的修改。

然而,快照隔离也存在一些缺点和问题,主要有以下几个方面:

  • 快照隔离需要额外的存储空间和管理开销,因为它需要保存数据的多个版本和时间戳信息。这样会增加数据库的复杂度和负担,也会影响数据的清理和回收。
  • 快照隔离不能完全避免事务的冲突和回滚,因为它只在提交时检查冲突。如果有多个事务同时修改同一块数据,那么只有第一个提交的事务可以成功,其他的事务都会失败并重试。这样会降低数据库的吞吐量和可用性,也会增加事务的响应时间和延迟。
  • 快照隔离不能防止写偏斜(write skew)异常,即两个事务同时读取了同一块数据,但是根据读取的结果做出了不一致的修改。这样会导致数据库的逻辑错误和不一致性。例如,两个银行账户之间转账的场景,如果两个事务同时读取了两个账户的余额,但是只修改了其中一个账户的余额,那么就会出现总金额不变的情况。

快照隔离是一种实现数据库并发控制的有效方法。它利用了数据版本和时间戳来保存数据在某个时间点的状态,并在提交时检查冲突。它可以提高数据库的并发性能,避免读写冲突,但也存在一些缺点和问题。

Concurrency Control Protocol

三个方法

  1. Timestamp Ordering
  2. Optimisitc Concurrency
  3. Two-Phase Locking

数据库系统中的并发控制协议是一种用来保证多个事务同时访问和修改数据库时不会产生不一致和冲突的方法。不同的并发控制协议有不同的原理和优缺点,三种常见的方法:

  • 时间戳排序(Timestamp Ordering):这种方法是基于每个事务和数据对象都有一个唯一的时间戳来表示它们的创建或修改时间的。时间戳可以是逻辑上的计数器或物理上的时钟值。当一个事务要访问一个数据对象时,它会根据自己的时间戳和数据对象的时间戳来判断是否可以访问该对象。一般来说,有以下几种规则²:
    • 如果事务的时间戳早于数据对象的修改时间戳,说明该对象在事务开始后被修改过,因此事务不能访问该对象,而应该回滚或等待。
    • 如果事务的时间戳晚于数据对象的修改时间戳,说明该对象在事务开始前被修改过,因此事务可以访问该对象,并更新数据对象的读时间戳为事务的时间戳。
    • 如果事务要修改一个数据对象,它必须检查自己的时间戳是否晚于数据对象的读时间戳和修改时间戳,如果是,说明没有其他事务读取或修改了该对象,因此事务可以修改该对象,并更新数据对象的修改时间戳为事务的时间戳;如果否,说明有其他事务读取或修改了该对象,因此事务不能修改该对象,而应该回滚或等待。
  • 乐观并发控制(Optimistic Concurrency Control):这种方法是基于假设冲突很少发生,因此不需要在访问数据时加锁或检查冲突,而是在提交时才进行冲突检测和解决的。这种方法可以减少锁开销和等待时间,提高并发性能。一般来说,有以下几个步骤³:
    • 读阶段(Read Phase):事务读取数据库中的数据,并保存在本地缓存中,同时记录自己读取的数据版本号。
    • 验证阶段(Validation Phase):事务准备提交时,检查自己读取的数据版本号是否和数据库中当前的数据版本号一致,如果一致,说明没有其他事务修改了自己读取的数据,因此可以继续提交;如果不一致,说明有其他事务修改了自己读取的数据,因此需要回滚或重试。
    • 写阶段(Write Phase):事务通过验证后,将自己修改后的数据写入数据库,并更新数据版本号。
  • 两阶段锁定(Two-Phase Locking):这种方法是基于在访问数据时加锁来防止其他事务同时访问或修改同一数据对象的。这种方法可以保证串行化(Serializability),即多个并发执行的事务可以得到和某个串行执行顺序相同的结果。一般来说,有以下几个特点⁴:
    • 一个事务在访问一个数据对象之前必须先获得相应的锁,锁分为共享锁(Shared Lock)和排他锁(Exclusive Lock),共享锁用于读操作,排他锁用于写操作。一个数据对象可以被多个事务加共享锁,但只能被一个事务加排他锁,并且不能同时存在共享锁和排他锁。
    • 一个事务在释放锁之前不能再申请新的锁,这样可以避免死锁(Deadlock)的发生。一个事务申请和释放锁的过程分为两个阶段,即加锁阶段(Growing Phase)和解锁阶段(Shrinking Phase),因此称为两阶段锁定。
    • 一个事务只有在释放了所有的锁之后才能提交或回滚,这样可以保证可恢复性(Recoverability),即一个事务不会读取或修改另一个未提交的事务的数据。

Version Storage

版本存储是MVCC的一个重要组成部分,它决定了数据库如何保存和管理数据对象的多个版本。不同的数据库系统可能采用不同的版本存储技术,但一般可以分为以下三种类型:

  • 基于追加写入(Append-Only)的版本存储技术:这种技术是在每次更新数据对象时,将新版本追加到同一张表中,并在旧版本中添加一个指针指向新版本。这样,每个数据对象就形成了一个版本链表,可以通过指针遍历找到合适的版本。这种技术的优点是创建新版本很快,不需要移动或复制数据,缺点是占用了更多的空间,并且需要维护指针的正确性。
  • 基于时间旅行(Time-Travel)的版本存储技术:这种技术是在每次更新数据对象时,将旧版本复制到另一张表中,并更新指针。这样,每个数据对象也形成了一个版本链表,但是新版本在原表中,旧版本在另一张表中。这种技术的优点是可以方便地查询历史数据,缺点是需要复制数据,并且需要同步两张表的结构和索引。
  • 基于增量(Delta)的版本存储技术:这种技术是在每次更新数据对象时,只保存变化的字段信息到一个单独的空间中,并更新指针。这样,每个数据对象也形成了一个版本链表,但是只有最新版本在原表中,其他版本都通过增量信息恢复。这种技术的优点是可以节省空间,并且可以支持部分更新,缺点是需要计算增量信息,并且需要恢复数据。

Version Chain Ordering,VCO

两种方法

  1. Oldest-to-Newest(O2N)
  • 将新版本附加到链的末尾。
  • 查找时必须遍历链。
  1. Newest-Oldest (N2O)
  • 必须为每个新版本更新索引指针。
  • 查找时不必遍历链。

Garbage Collection

随着时间的推移,DBMS 需要从数据库中删除可回收的物理版本。

  • DBMS 中没有活动的 txn 可以“看到”该版本
  • 该版本是由中止的 txn 创建的。

两种方法

  1. Tuple-level
  2. Txn-level

垃圾回收是一种用于清理和回收数据库中不再可见或有效的数据对象版本的技。不同的垃圾回收方法有不同的策略和开销,两种常见的方法:

  • 元组级别(Tuple-level)的垃圾回收:这种方法是针对每个元组的版本链表为单位进行清理和回收的。它需要遍历数据库中的所有元组,检查每个元组版本的可见性,即是否有任何活跃的事务可以读取或修改该版本。如果一个元组版本对所有事务都不可见,那么就可以将其标记为垃圾,并从版本链表中删除。这样可以释放空间,减少版本链表的长度,提高查询效率。
  • 事务级别(Transaction-level)的垃圾回收:这种方法是针对每个事务为单位进行清理和回收的。它需要每个事务在执行过程中记录自己创建或修改的元组版本的集合,称为写集合(Write Set)。当一个事务提交或回滚时,它会检查自己的写集合中的元组版本是否对其他事务可见。如果一个元组版本只对自己可见,那么就可以将其标记为垃圾,并从版本链表中删除。这样可以避免扫描数据库中的所有元组,减少垃圾回收的开销。

Index Management

数据库管理系统中的索引管理是指使用索引技术来提高数据库查询性能的过程。索引是一种数据结构,可以快速定位和访问数据库表中的数据,而不需要搜索每一行。索引可以根据一个或多个表列创建,提供随机查找和有序记录访问的基础。索引也可以用来维护数据库的约束,如唯一性、排他性、主键和外键。

主键索引指向版本链头。

DBMS必须更新pkey索引的频率取决于更新元组时系统是否创建新版本

如果txn更新元组的pkey属性(s),则这将被视为删除,后跟插入。

Secondary Indexes

Secondary Indexes是一种提供数据文件的次要访问方式的索引,可以根据非排序字段进行查询。Secondary Indexes有两种方法:Logical Indexes和Physical Pointers。

Logical Indexes是一种使用逻辑记录地址的索引,逻辑记录地址是指记录的主键或唯一标识符。Logical Indexes的优点是当记录的物理地址发生变化时,不需要修改索引。Logical Indexes的缺点是需要额外的查找操作,从逻辑地址到物理地址的转换。

Physical Pointers是一种使用物理记录地址的索引,物理记录地址是指记录在磁盘上的位置。Physical Pointers的优点是可以直接定位到记录,无需额外的查找操作。Physical Pointers的缺点是当记录的物理地址发生变化时,需要修改索引。

Secondary Indexes的选择取决于数据文件的组织方式和更新频率。一般来说,如果数据文件是顺序存储且更新较少,可以使用Physical Pointers;如果数据文件是随机存储且更新较多,可以使用Logical Indexes。

MVCC Delete

只有当逻辑删除的元组的所有版本都不可见时,DBMS才会从数据库中物理删除元组。如果元组被删除,则在最新版本之后不能有该元组的新版本,无写-写冲突。

我们需要一种方法来表示元组在某个时间点已经逻辑删除。

MVCC Deletes的基本原理是,当一个事务删除一条记录时,不是直接从数据库中移除这条记录,而是在这条记录上写入一个特殊的版本,称为墓碑(tombstone)。墓碑标记了这条记录被删除的时间戳,以及删除它的事务ID。这样,其他事务可以根据自己的隔离级别和时间戳,决定是否读取这条被删除的记录。

MVCC Deletes的两种方法Deleted Flag和Tombstone Tuple是指在使用多版本并发控制(Multi-Version Concurrency Control,MVCC)技术的数据库系统中进行删除操作时,如何标记被删除的记录的不同方式。

  • Deleted Flag是一种使用一个标志位来表示记录是否被删除的方法。这个标志位可以是记录头部的一位,也可以是记录中的一个单独的列。当一个事务删除一条记录时,不是直接从数据库中移除这条记录,而是将这个标志位设置为1,表示这条记录已经被逻辑地删除。这样,其他事务可以根据自己的隔离级别和时间戳,决定是否读取这条被删除的记录。Deleted Flag的优点是简单易实现,不需要额外的空间和索引。Deleted Flag的缺点是会增加查询和更新的开销,因为需要过滤掉被删除的记录,并且可能导致数据文件和索引文件膨胀。

  • Tombstone Tuple是一种使用一个空的物理版本来表示记录是否被删除的方法。当一个事务删除一条记录时,不是直接从数据库中移除这条记录,而是在这条记录上写入一个特殊的版本,称为墓碑(tombstone)。墓碑标记了这条记录被删除的时间戳,以及删除它的事务ID。这样,其他事务可以根据自己的隔离级别和时间戳,决定是否读取这条被删除的记录。Tombstone Tuple的优点是可以避免修改原始数据文件和索引文件,减少锁的开销和冲突。Tombstone Tuple的缺点是需要额外的空间和索引来存储墓碑,并且需要定期进行垃圾回收来清理过期的墓碑。

当一个墓碑超过了数据库系统设定的垃圾回收阈值(garbage collection threshold),那么这个墓碑就可以被物理地从数据库中清除了。不同的数据库系统有不同的垃圾回收机制。例如,在PostgreSQL中,有一个后台进程叫做VACUUM,它会定期扫描数据库中的表和索引,回收过期的墓碑和其他无效数据。

当需要删除一个范围内的多条记录时,写入每一条记录的墓碑会非常低效。为了解决这个问题,一些数据库系统引入了范围墓碑(range tombstone)的概念。范围墓碑可以用一次写入操作来标记一个键值区间内的所有记录为已删除状态,无论这个区间内有多少条记录。范围墓碑也需要经过垃圾回收过程才能被最终移除

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值