<并发控制概述> 章节原理疑问

感谢官网< 并发控制概述> 的书写,很有启发性。 读后一个几个小的疑问, 想要确认一下:

“如图所示,事务 12 进入提交阶段,并设置状态为 PREPARE,设置本地事务版本号为本地 最大读时间戳 120 与取 GTS 为 150 的最大值 150 作为 本地事务版本号 。”

我的问题是:
a) 因为prepare并不影响可见性, 为什么prepare 需要产生一个版本号? 如果是在commit 结束(第二阶段), 我认为是需要一个版本号的。
b). 访问gts 代表着延迟, 为什么需要去访问gts。 根据 [逸畅] 大佬的在 官方文档中《并发控制概述》部分疑问 - 社区问答- OceanBase社区-分布式数据库 的回复, [quote=“逸畅, post:6, topic:35609860”]
由于 GTS 是用于保证“外部一致性”的,与“并发控制”本身无关,即不使用 GTS 也能保证并发控制的正确性,因此本文在尽可能弱化这一概念
[/quote], “外部一致性维护的用意又是什么呢, 有什么真实的应用场景吗?

原文: “当读取到 PREPARE 状态的事务时,… 但是若 本地时间戳 小于读时间戳,那么无法确认这个时间戳最后的 全局提交时间戳 和读时间戳的关系,因此解决方案是,优雅地等在这行的事务上(内部称为 lock for read ,两阶段提交在 OceanBase 数据库的假设中应该会是很快完成的过程。如下图所示,读取请求 r3 以 140 作为读版本号进行读取,会先推高 最大读事务时间戳 ,保证之后事务以大于 140 的 本地提交版本/全局提交版本 提交,然后等待两阶段提交状态且 本地提交时间戳 为 130 的事务最后决定 全局提交时间戳 和读时间戳 140 的关系。”

我的问题是:这里的 “本地时间戳” 是 事务prepare 完成时的时间戳吗? 如果是的话,现实中表达的含义是: 我发出请求的那一个刻,该事务正在提交。 但终究是没有提交完成的。 我们为什么要等而不是直接当成 事务未提交完成 来对待呢?

  1. 读时间戳是根据 “最大提交时间戳” 来算出来的, 那么在系统刚刚启动的时候, 最大提交时间戳 是怎么设置的? WAL 恢复出来的吗?

  2. 当一个2pc 事务提交以后,计算出的 最大提交时间戳 是如何让所有节点知道的呢? 通过广播 还是 谁需要需要读的节点向中心节点去读呢? 感觉前者整体性能会更好一些。

  3. 对于单节点的事务(非2pc),它的提交时间戳 是不是只用更新本地即可, 从而避免远程广播的代价。 这样 该节点的读请求 依然可以感知这个事务, 别的节点也不需要这个信息(?). 只是将来收到别人的广播时, 取一个Max 值即可。

  4. 是否存在【预取版本号】 来优化网络开销的设计。 很多人说有这样的逻辑, chatgpt 也说有, 但我很难理解其正确性。 根据我当前对文档的理解,只有在事务提交的时候,广播一次, 我们几乎没有场景需要远程获得, 所以【预取版本号】似乎是没必要的。 我还是希望官方的同学能出面澄清一下。

感谢!

你看一下 这个文档 这里面有解释
https://open.oceanbase.com/blog/1100128

感谢,你提供的文档中确实很好的解释了 <外部一致性> 的问题, 这个问题确实和 OBServer 的 MVCC 机制本身无关了, 个人更倾向于 文档中直接不写, 或者描述为, “如果需要外部一致性, 我们此时需要将版本号注册到 GTS 服务,并取得一个较值作为本地版本号。 否则 可以省略掉GTS 服务的沟通环节”。

所以这篇文档回答了我 【问题1】 中的 【部分问题】, 其他问题看起来还没有回答?

1. 关于 PREPARE 阶段产生版本号的原因

  • a) 为什么 PREPARE 阶段需要产生版本号?
    • 在两阶段提交协议中,PREPARE 阶段的主要目的是为了确保所有参与者都准备好提交事务。在这个阶段产生的版本号(本地事务版本号)是用于后续的提交阶段,确保事务的提交版本号不会小于 PREPARE 阶段的版本号。这样做有助于保证事务的原子性和一致性。虽然 PREPARE 阶段本身不影响可见性,但它为后续的提交阶段提供了必要的信息。
  • b) 为什么需要访问 GTS?
    • GTS (Global Timestamp Service) 是用于保证外部一致性的机制。在 PREPARE 阶段获取 GTS 的时间戳,是为了确保事务的提交版本号不仅不会小于 PREPARE 阶段的版本号,而且也不会小于全局的时间戳。这有助于保证事务的外部一致性,即事务的提交版本号在整个系统中是一致的。虽然 GTS 不直接影响并发控制的正确性,但它对于保证整个系统的外部一致性是非常重要的。

2. 关于等待 PREPARE 状态事务的原因

  • “本地时间戳” 是指什么?
    • 文档中的“本地时间戳”指的是事务在 PREPARE 阶段设置的版本号。当读取请求遇到 PREPARE 状态的事务时,如果该事务的本地时间戳小于读请求的读版本号,那么无法立即确定该事务最终是否会提交。因此,读请求需要等待事务的最终状态(提交或回滚)以确定是否能看到该事务的影响。
  • 为什么选择等待而不是直接忽略?
    • 如果直接忽略 PREPARE 状态的事务,可能会导致读取结果不一致。等待是为了确保读取的结果是正确的,即确保读取到的数据要么是事务提交后的最新状态,要么是事务回滚后的状态。这样可以保证读取的一致性和正确性。

3. 系统启动时最大提交时间戳的初始化

  • 最大提交时间戳是如何初始化的?
    • 系统启动时,最大提交时间戳通常是初始化为一个较低的值,比如 0。随着事务的执行,最大提交时间戳会被不断更新。如果系统崩溃后重启,最大提交时间戳可以通过恢复日志(WAL)来重建,确保系统状态的一致性。

4. 最大提交时间戳的传播方式

  • 最大提交时间戳是如何传播的?
    • 当一个两阶段提交事务完成时,最大提交时间戳通常通过广播的方式通知其他节点。这种方式可以确保所有节点都能及时更新自己的最大提交时间戳,从而保证读取的一致性和正确性。广播方式确实有助于提高系统的整体性能。

5. 单节点事务的提交时间戳

  • 单节点事务的提交时间戳如何处理?
    • 对于单节点事务,提交时间戳只需要在本地更新即可。这是因为单节点事务不会涉及到跨节点的协调,所以不需要远程广播提交时间戳。当其他节点需要了解这个提交时间戳时,它们可以通过查询或者在接收到相关消息时更新自己的最大提交时间戳。

6. 预取版本号的设计

  • 是否存在预取版本号?
    • 根据目前的文档,OceanBase 数据库并没有明确提及预取版本号的设计。通常情况下,事务在提交时才会广播提交时间戳。预取版本号的设计可能会引入额外的网络开销,并且在大多数情况下并不是必需的。
1 个赞

为什么 PREPARE 阶段需要产生版本号?

  • 在两阶段提交协议中,PREPARE 阶段的主要目的是为了确保所有参与者都准备好提交事务。在这个阶段产生的版本号(本地事务版本号)是用于后续的提交阶段,确保事务的提交版本号不会小于 PREPARE 阶段的版本号。这样做有助于保证事务的原子性和一致性。虽然 PREPARE 阶段本身不影响可见性,但它为后续的提交阶段提供了必要的信息。

–》 既然是必要,如果我们【不要】这个信息会出现什么异常呢?

  • 为什么选择等待而不是直接忽略?
    • 如果直接忽略 PREPARE 状态的事务,可能会导致读取结果不一致。等待是为了确保读取的结果是正确的,即确保读取到的数据要么是事务提交后的最新状态,要么是事务回滚后的状态。这样可以保证读取的一致性和正确性。

–》 有没有【具体例子】来说明什么【不这样做就会有问题】。

我当前的理解是上述的2个环节都是可以简化掉的。

其他问题都没有疑问了, 感谢回复。

你看看 这一章的并发控制概述
https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001053062

我所有的问题都是从这篇文章开始的~ 不过我确实是在 一开始发帖的时候,只写了 “感谢官网< 并发控制概述> ”,而没有加上文章的链接。

1、 为什么 PREPARE 阶段需要产生版本号?
但是这个里面确实有解释呀 提交请求处理 读请求处理 写请求处理 其实都和你说的 PREPARE 阶段有关系
在你说的【既然是必要,如果我们【不要】这个信息会出现什么异常呢?】 其实如果没有PREPARE 阶段这几个处理请求 都会有问题 这个文档中其实也有解释
2、 为什么选择等待而不是直接忽略?
这个目前看的只有文档解释 没有用例 这个暂时需要你加强理解

这里能请对应的内核看一下吗? 我感觉这里是一个潜在的性能优化,并且可以让代码更加简洁。如果不应该改变,也会有更加明确的理由。

好的 这边反馈一下 后续有进展了 及时和你沟通

这个阶段确实是必须的。 假设不考虑 prepare 阶段不更新版本号:

node1:
data-a : local_commit_ts: 100

node2:
data-x: local_commit_ts: 104
data-y: local_commit_ts: 104

2pc coordinator 会将global txn commit ts 设置为104,并记录事务状态 更新到 全局事务表。

如果在 local commit 104 之后, 全局事务表更新之前, 需要判断 data-x 的可见性, 此时读版本号为 104, 按照我的说法,读版本号符合要求, 因为全局事务状态没有提交,所以不可见。

随后, 全局事务表更新了。

随后,读到了data-y, 发现 读版本号和全局事务状态都通过,所以data-y可见了。

此时读不一致产生了,因为data-x 和 data-y 属于同一个事务,结果 一行可见,一行不可见了。