两阶段提交应该会遇到阻塞问题。CockroachDB 是不是这样,如果不是,如何避免?
2 回答
总结:两阶段提交是阻塞的,所以重要的是保持两阶段提交的东西尽可能“小”,这样被阻塞的所有动作的集合是最小的。CockroachDB 使用带有意图的 MVCC 来做到这一点,两阶段提交仅在一个意图上。因为 CockroachDB 提供了可序列化的事务,它会重新排序事务时间戳,以便仅在绝对必要的情况下最大限度地减少阻塞。
更长的答案
两阶段提交在第一阶段之后是阻塞的,而所有参与者都等待协调者关于第二阶段是提交还是中止的回复。在此期间,已经发送“是”投票的参与者不能单方面撤销他们的投票,也不能将其视为已提交(因为协调员可能会以中止返回)。因此,他们被迫阻止所有需要具体了解此事务状态的后续操作。上面这句话的关键在于“需要”:我们有责任设计我们的系统以将这个集合减少到最低限度。CockroachDB 使用写意图和[MVCC]来最小化这些依赖关系。
考虑一个分布式(多键)事务键值存储的简单实现:我希望以事务方式提交一些写事务t1
。t1
跨越许多机器的许多键,但特别值得关注的是它写入k1 = v2
. k1
在机器上m1
(假设k1=v1
是以前的值)。
由于t1
跨越许多机器上的许多键,它们都涉及到两阶段提交事务。一旦该两阶段事务开始,我们必须注意我们有写入的意图k1=v2
,并且事务的状态是未知的(事务可能中止,因为其他写入之一无法继续)。
现在,如果出现其他t2
想要读取 的值的k1
事务,我们根本无法给该事务一个权威的答案,直到我们知道 2 阶段提交的最终结果。t2
被阻止。
但是,我们(和 CockroachDB)可以做得更好。我们可以为每个键保留多个版本的值,并有一个并发控制机制来保持所有这些版本的顺序。也就是说,我们可以分配我们的交易时间戳,并让我们的写入看起来(松散)如下:
`k1 = v1 committed at time=1`
`k1 = v2 at time=110 INTENT (pending transaction t1)`
现在,当t2
它出现时,它有一个选项:它可以选择在 上进行读取time<=109
,这不会在 上被阻止t1
。当然,有些事务不能做到这一点(如果说它们也是分布式的,并且有一个不同的组件只需要更高的时间戳)。这些交易将被阻止。但在实践中,这释放了数据库来分配时间戳,以便可以进行许多类型的事务。
正如另一个答案所说,Cockroach Labs 有一篇关于 CockroachDB在此处使用 MVCC 的帖子,其中还解释了一些进一步的细节。
CockroachDB 有一篇很长的博客文章,介绍了它如何使用两阶段提交而不在这里锁定:https ://www.cockroachlabs.com/blog/how-cockroachdb-distributes-atomic-transactions/
与锁定预防最相关的部分是它对“写意图”的使用(阶段:写意图是博客文章中的标题)。