https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000821594
疑问一:提交请求处理那里,提到如下内容。这里的"本地提交时间戳
"到底是等于最大读时间戳
,还是等于 最大读时间戳
与GTS取值
这二者中的MAX?
当事务提交时,我们会走对应的两阶段提交,我们对于参与者中的每一个分区取本地 最大读时间戳
作为本地提交时间戳。这个保证是为了做单值的读写冲突(anti dependency),根据保证,可以得到我们的提交时间戳一定大于之前所有的读取,因此我们在串行执行中可以在这些之前的读取后面。如图所示,事务 12 进入提交阶段,并设置状态为 PREPARE,设置本地事务版本号为本地 最大读时间戳
120 与取 GTS 为 150 的最大值 150 作为 本地事务版本号
疑问二: 写请求处理那里,提到如下内容。SI隔离级别下为防止丢失更新,会用读时间戳与本行上的ts进行比较,如果前者小于后者,则事务会回滚。这里"写操作的读时间戳"指的是什么?
快照读隔离级别为了防止读写冲突(anti dependency)和写读请求(read dependency)成环,即丢失更新(lost update),尽管写入或唤醒后加锁成功,会用读时间戳,跟本行上维护的 最大提交事务时间戳
作比较,如果读时间戳小于行上的最大提交事务时间,则会回滚掉此事务。比如说若上图写操作的读时间戳是 100,事务 12 以时间戳 160 提交,那么就会触发写操作对应事务的回滚,对应的报错(TRANSACTION_SET_VIOLATION
)
1 个赞
逸畅
2024 年7 月 16 日 17:08
#6
你好,感谢你对 OceanBase 的关注~ 我是事务内核的相关研发同学,以下解答你的问题:
疑问一:
此处在由内部文档转换为官方文档的时候缺失了一些信息,导致可能出现歧义。原文如下:
回归正题,当事务提交时,我们会走对应的两阶段提交,我们对于参与者中的每一个分区取本地最大读时间戳作为本地提交时间戳,对于第一个参与者还通过全局时间戳获取服务获取全局时间戳,并取最大值作为本地提交时间戳。这个保证是为了做单值的读写冲突(anti dependency),由于我们之前的保证,可以得到我们的提交时间戳一定大于之前所有的读取,因此我们在串行执行中可以在这些之前的读取后面,那么前面的事务没读到本事务就是当然的了。如图所示,事务12进入提交阶段,并设置状态为 PREPARE,设置本地事务版本号为本地最大读时间戳 120 与(假设为第一个分区)取全局时间戳为 150 的最大值 150 作为本地事务版本号。
所以可得,此处的“本地提交时间戳=MAX(本地最大读时间戳, GTS)”是针对“第一个参与者”而言的,而对于其他参与者,均取“本地提交时间戳=本地最大读时间戳”。此外,由于 GTS 是用于保证“外部一致性”的,与“并发控制”本身无关,即不使用 GTS 也能保证并发控制的正确性,因此本文在尽可能弱化这一概念。
疑问二:
写操作的读时间戳在不同的隔离级别下表现不一致。在 RC 即以下的隔离级别中,写操作的读时间戳即语句开始时获取的时间戳,因此,对于 RC 隔离级别,遇到 TRANSACTION_SET_VIOLATION 报错时不需要回滚事务,仅需要重做语句,重新获取读时间戳即可;而对于 SI 隔离级别,写操作的读时间戳即事务开始时获取的时间戳,因此在遇到 TRANSACTION_SET_VIOLATION 报错时,只能通过回滚事务来避免“丢失更新”。
如有未解答清晰的地方可以继续在本帖中评论,再次感谢你对 OceanBase 的关注!
5 个赞
感谢研发大佬的回复。
在疑问二中,你提到RC隔离级别下遇到TRANSACTION_SET_VIOLATION 报错时不需要回滚事务,仅需要重做语句,重新获取读时间戳即可。我的疑问是,RC隔离级别下,理论上是可以接受丢失更新的,OB在这里也需要通过重做语句来避免丢失更新吗?重做动作是OB自动handle吗?
逸畅
2024 年7 月 16 日 21:34
#9
对于第一个问题,我们内部发生了激烈的争论,最后达成的共识如下:
“丢失更新” 是从用户视角观察到的丢失,例如业务侧的事务 T1 先 select A = 1,再在 select 的结果上进行 A = 1 + 1 更新,T2 也做相同的动作,此时是有可能丢失更新的,即最后 A 可能结果还为 2。但是对于 update A = A + 1,在 OceanBase 的实现中,两个事务执行结束后 A 只有可能为 3。此处是 OceanBase 的兼容 Oracle 的处理,即对于单条 SQL 内避免丢失更新,但显然,业务侧是可以将 update A = A + 1 拆解为 a = select A, A = a + 1 的,此时是会丢失更新的,只是 OceanBase 避免了这个问题。从其他数据库中对于丢失更新的描述也可以看出,他们都是对于多条语句间丢失更新情况的描述:
https://docs.oracle.com/database/121/CNCPT/consist.htm#CNCPT020
https://www.cockroachlabs.com/docs/stable/read-committed
对于第二个问题,是的,RC 隔离级别下如果在提交时发现在该语句执行过程中相关行上有更新的提交,会自动回滚语句并获取最新的读时间戳,以看到最新的视图,并在此基础上重做写操作。该行为对性能实际上是有影响的,如不处理在并发场景下应该会有更优的性能。
3 个赞