在设计事务型数据库时,选择合适的并发控制策略,如悲观并发控制(PCC)或乐观并发控制(OCC),往往是其中最棘手的决定。理论上,OCC可以提供高并发并减少锁定带来的开销,而PCC通过在修改数据之前获取锁来确保性能的可预测性。但实际情况往往与理想的理论模型相去甚远。
在 TiDB 的早期版本里,我们完全依赖于 OCC。我们认为 OCC 能够满足我们客户的需求。然而,随着我们与来自不同行业和应用场景的客户深入合作,实际情况并不总是像我们假设的那样。最终,这促使我们在 TiDB (从 3.0.8 版本开始) 从 OCC 转向了 PCC,为客户提供更稳定和可预测的并发体验,让客户的体验更加稳定和可预测。
接下来,我将与您分享我们从客户那里学到的经验以及我们为什么提供PCC支持。
什么是PCC与OCC的快速介绍
在我们开始之前,先稍微讲一下PCC和OCC,聊聊它们的好地方和不足之处。
乐观的并发控制(OCC):
- 工作原理: 事务可以自由运行,仅在提交时检查是否有冲突。冲突会触发回滚和重试。
- 优点: 在低冲突场景下具有高并发性;无需提前获取锁。
- 缺点: 在高并发情况下回滚可能不可预测;重试逻辑复杂;如果在事务后期才出现冲突,则可能导致之前的工作白费。
悲观的并发控制(PCC)
- 它是如何工作的: 事务在进行更改之前会获取锁,防止其他事务同时修改相同的数据。
- 优点: 性能更加可预测;减少回滚;处理并发的代码路径更简单。
- 缺点: 可能会出现锁竞争和额外开销;在读多写少的环境中,可能降低原始并发度。
客户教给我们的经验
在刚开始开发 TiDB 时,我们对客户做出了很多假设,但事实证明,这些都成了我们的教训,包括但不限于以下几点。
别以为客户知道他们自己的工作负载。
许多客户认为他们的“低争用”情况,但随着业务增长或使用模式变化,热点和争用变得非常普遍。一旦关于工作负载行为的假设被证明是错误的,OCC的回滚风暴和重试功能会迅速出现。
别以为客户知道怎么写重试逻辑。
使用 OCC,每次冲突都可能触发重试。多少次重试才算够?重试会不会导致“重试风暴”,进而放大延迟?客户可能既没有足够的知识,也没有耐心来实施复杂的回退重试策略。
别以为客户能轻易改动他们的代码库
从一种并发模型迁移到另一种,或者将复杂的重试逻辑集成到现有的代码库中,这是一项艰巨的任务。从旧数据库迁移出来的客户可能没有足够的工程资源来优雅地处理应用程序中的OCC冲突(OCC,即乐观并发控制)。
不要以为客户的交易通常都是短期小额的。
一些客户运行长时间且复杂的多步骤交易。对于这种情况,OCC回滚操作尤其痛苦,因为大量的工作在提交阶段会被取消。PCC的早期锁定策略可以避免不必要的计算。
不要假设客户了解哪些按键容易出问题。
识别热键并设计时考虑它们需要深入了解工作模式。若缺乏这些知识,OCC可能会频繁出现难以诊断和修复的意外错误。相比之下,PCC则使争用情况更早变得明显且可预测。
客户究竟想要啥?从我们的角度来看,客户最需要的是什么?从我们的角度看,我们的假设不成立,客户可能更需要:
- 可预测性能: 他们希望获得稳定的、一致的响应时间,而不是频繁重试导致的突然峰值。
- 可控制的工作量: 他们可以通过阻塞事务而不是让事务运行并在后期失败来清晰地表明资源竞争。
- 操作简单性: 他们需要更好的可观测性以及在资源竞争下的调整或理解行为的能力。
- 轻松集成到生态系统: 大多数依赖现有的工具、ORM 和与可预测的锁定模型平滑配合的模式。
所以我们决定向我们的客户提供PCC服务,现在几乎所有客户在所有情况下默认使用PCC,甚至在一些对性能要求极高的场景中。
结论。
最后,我想强调PCC并不完美,PCC和OCC各有所长。事实上,包括TiDB在内的许多数据库都让用户根据需要选择这两种方案。然而,如果你的数据库只提供OCC,你可能会因此失去那些对数据库有更高要求的客户,特别是那些希望将数据库用于核心场景的客户。正如我们在TiDB的旅程中发现的那样,理论上的好处在实际中并不总是成立。提供PCC提升了我们客户的体验,并为更成熟的工作负载、更简单的应用代码以及更少的运行时意外打开了大门。
如果有一课我们要学,那就是:评估并发控制策略时,要考虑到实际情况,而不仅仅是理论上的假设。
参考
共同学习,写下你的评论
评论加载中...
作者其他优质文章