CRDB-事务层知识点

事务层负责维护事务的原子性,确保事务中的所有操作都被提交或中止。此外,事务层在事务之间维护可序列化的隔离—这意味着事务与其他事务的影响完全隔离。尽管多个事务可能同时进行,但每个事务的体验就像每次只运行一个事务—可序列化的隔离级别。
事务层处理SQL层生成的KV操作。事务由多个KV操作组成,其中一些操作可能是单个SQL语句的结果。除了更新表项外,还必须更新索引项。在所有情况下保持完美的一致性涉及多种复杂的算法,并不是所有的算法都可以在本章中介绍。

MVCC原则

与大多数事务性数据库系统一样,CRDB实现了多版本并发控制(MVCC)模式。MVCC允许读者获得一致的信息视图,即使该信息正在被修改。如果没有MVCC,数据项的一致读取需要阻塞(通常使用“读锁”)该项的同时写,反之亦然。使用MVCC,即使在信息被并发事务修改时,读者也可以获得一致的信息视图。
MVCC的基本原理如下图所示。在时间t1时,会话s1从行r2中读取并访问该行的v1版本(1)。在时间t2时,另一个数据库会话s2更新该行(2),创建该行的v2版本(3)。在t3时,会话s1再次读取该行,但是——因为s2还没有提交它的更改——继续从版本v1(4)中读取。在s2提交(5)后,会话s1发出另一个select,现在从该行的新v2版本(6)中读取。
在这里插入图片描述

CRDB实现限制了事务从以前版本读取的能力。例如,如果读事务在写事务开始之后开始,那么它可能无法读取该行的原始版本,因为它可能与已经读取的其他数据不一致,或者在事务中稍后将读取的其他数据不一致。这可能导致读事务“阻塞”,直到写事务提交或中止。
稍后我们将看到存储引擎如何实现MVCC,但目前,重要的概念是系统维护任何行的多个版本,事务可以根据它们的时间戳和任何并发事务的时间戳确定要读取的行的哪个版本。

事务工作流

分布式事务必须分多个阶段进行。简单地说,分布式系统中的每个节点都必须为事务奠定基础,只有当所有节点都报告可以执行事务时,事务才会最终完成。
下图说明了一个高度简化的事务准备流程。在这种情况下,一个包含两个语句的事务被发送到CockroachDB网关节点(1)。第一个语句涉及到范围2的更改,因此请求被发送到该范围(2)的leaseholder,这将创建一个新的临时版本的行,并将更改传播到副本节点(3和4)。第二个语句影响范围4,因此事务协调器将该请求发送到适当的leaseholder(5)。当所有的更改都被正确地传播时,事务就完成了,并且客户端被通知成功(8)。
在这里插入图片描述

写意向

在事务处理的初始阶段,当还不知道事务是否会成功时,leaseholder将暂定的修改写入被称为写意图的修改值。写意图是记录的特殊构造的MVCC兼容版本,被标记为临时版本。它们既可以作为暂定的事务结果,也可以作为阻止任何并发更新同一记录的尝试的锁。
在事务要修改的第一个键范围内,cockachdb写一个特殊的事务记录。这记录了事务的最终状态。在图2-11所示的示例中,该事务记录将存储在范围2中,因为这是事务中要修改的第一个范围。
此交易记录将记录交易状态为以下之一:

  • PENDING
    指示写意图的事务仍在进行中。
  • STAGING
    已经执行了所有的事务写操作,但是还不能保证事务可以提交。
  • COMMITTED
    事务已成功完成。
  • ABORTED
    指示事务已中止,其值应被丢弃。

并行提交

在分布式数据库中,网络往返次数通常是延迟的主要因素。通常,提交分布式事务至少需要两次往返(实际上,这方面的一个经典算法称为两阶段提交)。cockachdb使用一种名为并行提交的创新协议来隐藏这些往返行程中的一个,以避免客户机感知到的延迟。
并行提交背后的关键是,一旦事务不可能中止,网关就可以将成功返回给客户端,即使事务还没有完全提交。其余的工作可以在返回后进行,只要其结果是确定的。这是通过与事务的最后一轮写操作并行地将事务转换到STAGING状态来实现的。所有这些写操作的键都记录在事务记录中。当且仅当所有这些写操作都成功时,必须提交STAGING事务。
通常,网关在这些写操作完成后就会了解它们的状态,并在后台开始事务的最终解决之前将控制权返回给客户端。如果网关失败,下一个遇到STAGING事务记录的节点负责查询每个写入的状态,并决定事务是否必须提交或中止(但由于事务记录和每个写入意图都是持久写入的,因此无论事务是由其原始网关还是由另一个节点解决,结果都保证是相同的)。
注意,在此解析过程完成之前,事务持有的任何锁都不会被释放。因此,从等待锁的另一个事务的角度来看,事务的持续时间仍然至少是两次往返(就像在两阶段提交中一样)。但是,从发出事务的会话的角度来看,运行时间大大减少了。

事务清理

正如上一节所讨论的,COMMIT操作在事务记录中“拨动开关”以将事务标记为已提交,从而最大限度地减少事务提交时可能发生的任何延迟。事务到达COMMIT阶段后,它将通过将它们修改为表示新记录值的普通MVCC记录来异步解析写意图。
然而,与任何异步操作一样,在执行此清理时可能会有延迟。此外,由于已提交的写意图看起来与挂起的写意图相同,因此在读取键时遇到写意图记录的事务需要确定写意图是否已提交。
如果另一个事务遇到了事务协调器尚未清理的写意图,那么它可以通过检查事务记录来执行写意图清理。写意图包含一个指向事务记录的指针,它可以显示事务是否已提交。

事务流程

下图说明了一个成功的双语句事务的流程。客户端发出UPDATE语句(1)。这将创建一个事务协调器,该事务协调器维护一个处于PENDING状态的事务记录。在相关范围(2)内向租赁者发出写意图命令。租赁者将意图标记写入其数据副本中。它将成功返回给事务协调器,而无需等待副本的意图被确认。
事务中的后续修改将以相同的方式处理。
客户端发出一个COMMIT(3)。事务协调器将事务状态标记为STAGING。当所有的写意图都被确认后,会通知初始化客户机成功,然后将事务状态设置为COMMITTED(4)。
成功提交后,事务协调器解析受影响范围内的写意图,这些写入意图成为正常的MVCC记录(5)。此时,事务已经释放了所有锁,同一记录上的其他事务可以继续进行。
在这里插入图片描述

上图是高度简化的,但仍然有点难理解。从图表中我们可以得到两个主要结论:
•大多数操作分为两个阶段;我们可以在第一个响应之后继续下一步,只需要在提交结束时解决所有问题。
•客户端的延迟不包括所有的清理操作。UPDATE操作在所有写意图传播之前返回,COMMIT操作在所有写意图解析之前返回。希望这能从应用程序响应时间中消除分布式数据库管理的大量开销。

读/写冲突

到目前为止,我们已经讨论了成功事务的处理。如果所有事务都成功,那就太好了,但是除了最琐碎的场景外,在所有的场景中,并发事务都会产生必须解决的冲突。
最明显的情况是两个事务试图更新同一记录。同一个键不能有两个写意图活动,因此其中一个事务将等待另一个事务完成,或者其中一个事务将被终止。如果事务具有相同的优先级,那么第二个事务(尚未创建写意图的事务)将等待。但是,如果第二个事务具有更高的优先级,则原始事务将被中止并必须重试。
事务优先级可以通过SET Transaction语句来调整。
TxnWaitQueue对象跟踪正在等待的事务以及它们正在等待的事务。这个结构是在与事务关联的Raft leader范围内维护的。当事务提交或中止时,TxnWaitQueue将被更新,并通知所有等待的事务。
如果两个事务都在等待另一个事务创建的写意图,就会发生死锁。在这种情况下,其中一个事务将被随机中止。
事务冲突也可能发生在读取器和写入器之间。如果读取器遇到一个未提交的写意图,它的时间戳比读的一致读时间戳更低(例如更早),那么一致读就不能完成。如果在读事务开始和它试图读取相关键之间发生了修改,就会发生这种情况。在这种情况下,读操作需要等待,直到写操作提交或中止。
这些“阻塞读取”可以在以下情况下避免:
如果读取具有高优先级,那么cockachdb可能会将低优先级的写入的时间戳“推”到更高的值,从而允许读取完成。如果推送操作使事务中以前的工作失效,则“推送”事务可能需要重新启动。
使用AS OF SYSTEM TIME的过期读取不会阻塞(只要事务不超过指定的过期时间)。我们将在本章后面讨论AS OF SYSTEM TIME。
在多区域配置中——global表使用一种修改过的事务协议,其中读不会被写阻塞。
许多事务冲突是自动管理的,虽然这些事务冲突会影响性能,但不会影响功能或代码设计。但是,在多种情况下,应用程序可能需要处理中止的事务。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据源的港湾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值