在高吞吐量的支付系统中,借记和贷记的连续流动意味着客户账户会不断被更新。想象一个流行的在线市场:它的中央结算账户,或者主要卖家的账户,可能会同时收到数千次更新。虽然整个系统架构可能是为了大规模操作而设计的,这些特定的“热点”账户却可能成为严重的瓶颈点。
这种现象,即大部分更新集中在少量数据上,会导致严重的数据库锁冲突,称为 热行争用(hot row contention)。
结果就是,交易处理速度变慢,延迟增加,这对业务运作造成了直接影响。
谁成了这个性能瓶颈的受害者?
虽然支付系统提供了鲜明的例证,热点争用是数据库世界中的一个普遍挑战。任何频繁更新特定记录的应用程序——无论是热门产品的库存、关键流程的状态,还是配置——都有可能受到这一性能杀手的影响。
在这篇文章中,我们将分析为什么会出现热点行争用,以支付系统为例来说明,并探讨几种经过实战考验的架构和数据库设计模式,以减少其影响。
问题:热点竞争本质上,热行冲突源于数据库如何管理并发的数据修改。在一个典型的关系型数据库(如 MySQL、PostgreSQL 或 SQL Server)中,当一个事务需要更新一行数据时,它会获取一个 独占锁。此锁阻止其他事务同时修改该行,来确保数据的一致性和完整性,即 ACID 模型中的 “I” 和 “C”。
当许多并发事务争抢同一个确切的行——我们所说的“热行”时,它们不得不排队等待当前持有锁的事务完成释放锁。
把它想象成一条单行道在高峰时段突然需要处理一个特定出口的车流。
在我们的支付系统示例中,这条频繁访问的行可能包含处理数千笔销售的账户余额,或者用于结算众多小额交易的内部结算账户。
这种瓶颈的影响是严重且深远的:
- 增加的交易延迟: 操作因排队而延迟。
- 更高的失败和重试率: 事务可能由于等待锁而超时失败,迫使应用程序重试,从而增加额外负载。
- 整体系统吞吐量下降: 整个系统的事务处理能力受到最热行更新速度的限制。
- 较差的客户体验: 慢速和错误让客户感到不满。
关键的是,即使是为实现水平扩展而设计的现代数据库(如 Spanner、CockroachDB、Yugabyte 等),也无法避免这类问题。
虽然它们在多个节点之间分配负载方面表现出色,但如果工作负载本身偏向于更新一些特定行,这些行依然会成为争用热点。这些系统可以在某种程度上缓解这些问题,但如果访问模式依然高度集中,这些问题并不会因此消失。
应对热点行争用的策略 1. 只写账本模型:优先写入追加式账本模型改变了数据修改的方式。而不是直接更新记录(比如直接修改账户行中的余额),每次交易都被记录为一个全新的、不可更改的条目。这就像在传统的手工账本中对于每个财务事件添加一个新的行项目。
这种方法有效地绕过了热行争用问题,因为插入新的不同行通常远不如许多事务尝试修改同一行那样容易导致数据库锁冲突。每个事务会独立地追加其数据。一个显著的优势是自动创建一个完整且不可更改的审计轨迹,这对于金融系统来说非常重要,因为每项历史更改都会被记录下来。
当前的状态,比如账户余额,并不是就地更新的,而是根据这些交易记录计算出来的。这个计算可以在需要余额时“按需”计算(确保实时准确,但对于交易频繁的账户可能较慢),或者“定期”由后台进程预先计算并存储余额以便快速读取(导致余额最终一致)。
主要优点:
- 显著减少写入冲突,从而提高事务吞吐量。
- 提供完整且内置的审计跟踪,非常适合用于金融合规和历史分析。
- 将写入操作简化为基本插入。
主要不足:
- 增加了读取当前状态(即余额计算)的复杂度。
- 如果定期固化余额,可能会带来最终一致性,读取时可能无法立即看到最新的交易。
- 虽然如此,这可能会慢慢增加存储使用量,但这通常是可以接受的权衡。
使用场景: 当你在特定记录上遇到了严重的写入竞争,需要严格的审计能力,并且能够处理按需计算余额带来的延迟,或者预计算余额带来的最终一致性问题。这种方法特别适合那些历史数据完整性和高写入吞吐量比即时余额读取更重要的系统。
2. 热门账户拆分:分而治之内部分片通过将单一的、经常更新的逻辑账户有效分割成数据库中的几个较小的独立“子账户”来解决热点行冲突。例如,高交易量的商家的单一账户余额可能被拆分到十个不同的子账户中。
这种方法通过将原本写入一个热行的写操作分散到多个子账户行来实现。更新商户余额的交易将被路由至其中一个子账户(例如,基于交易ID的哈希或轮转)。这显著降低了多个交易在同一物理行上竞争锁的机会。
然而,为了获取逻辑账户的真实总余额,应用程序现在必须查询所有构成子账户的余额并汇总它们的余额。这给应用程序的读取逻辑增加了额外的复杂性。
主要优点:
- 通过分散写操作,直接减少了特定可识别的热门账户的锁冲突。
- 只需少量修改底层数据库模式即可实现,主要影响应用程序的逻辑处理。
主要不足:
- 引入了管理子账户和聚合它们余额以供读取的复杂性。
- 确定合适的子账户数量可能具有挑战性,如果某个子账户本身变得非常活跃,重新拆分已拆分的账户也会很复杂。
- 如果账户的“热门”程度不可预测或频繁变化,这种方法可能不太适合。
何时使用: 您有一些少量特定的账户,这些账户经常引发争议。实现并管理分片逻辑(包括写入和特别是平衡汇总读取的分片逻辑)的额外的工作量被看作是可以接受的权衡,用来解决这些特定账户的瓶颈问题。
3. 内存缓冲和批处理操作:应对峰值此技术涉及拦截传入的交易请求,并将它们暂时存储在快速内存缓存或专用的缓存系统(如 Redis)中,而不是直接将每个交易写入主数据库中。这些缓存的交易随后会批量刷新到持久化数据库中。
通过批量操作,系统减少了直接写入数据库的单个写操作数量,从而减少了竞争。它还可以在缓冲区中提前汇总或合并事务(例如,将多个小额扣款合并到同一个账户,再写入一个更新)。这使得每次数据库写入更高效。
这里的关键考虑是数据耐久性。如果存放缓冲区的系统在事务被刷新到数据库之前崩溃,这些事务将可能丢失。因此,缓冲区本身需要具备健壮的崩溃恢复机制,或采用持久缓存。
主要优点:
- 显著提高整体写入吞吐量,通过降低数据库写入频率和减少其开销。
- 平滑数据库上的负载峰值,使其更加稳定。
- 允许在内存缓存中预先处理或聚合事务。
主要不足:
- 存在一个风险,如果缓冲系统崩溃且没有自己的持久化/恢复机制,缓冲中的事务可能会丢失数据。
- 每个事务需要更长时间才能完全持久化,事务在缓冲区等待提交到主数据库时。
- 增加了复杂度,需要管理缓冲、批量处理和恢复期间的数据一致性。
适用情况: 整个系统的整体吞吐量优先于每次交易的即时写入延迟和持久性。应用程序可以容忍在崩溃情况下少量数据丢失,或者这一风险已通过高度可用/持久的缓冲解决方案来缓解。通常用于处理高速度的事件流,此时在数据库持久化前进行一些初步处理或聚合是有益的。
4. 事件驱动架构(CQRS):彻底分离关注点命令查询职责隔离(CQRS),常常与事件溯源结合使用,涉及完全分离写操作(命令)和读取(查询)的数据模型和路径。写操作通常以不可变事件的形式被捕获(例如“支付发起”,“账户充值”),这些事件存储在优化的事件存储中。这些事件会触发单独的读取模型(视图)的更新,这些模型专门设计并反规范化,以便高效查询,例如展示账户余额。
这种分离使得读取路径和写入路径可以独立扩展,达到很高的程度。这种架构模式通过优化写入路径来解决争用问题,使追加事件变得高效,这本质上减少了争用。读取路径查询的是独立的数据模型,这些数据模型不与写操作竞争。
这样的折衷就是系统复杂程度显著增加,还有引入了最终的一致性。
由于读模型是异步更新的,基于写模型的事件,所以在查询中反映变更前会有一段时间的延迟。
主要优点:
- 通过独立优化读写路径,实现了极高的可扩展性和性能。
- 提供了高度灵活性,能够适应各种查询需求下的数据处理、存储和展示。
- 自然契合事件驱动模式,并有助于构建稳健且可审计的系统。
明显缺点:
- 显著增加整体系统设计和操作性复杂性(多个数据存储、事件的处理、消息队列,)。
- 需要谨慎管理写模型与读模型之间的最终的一致性。
- 实现起来可能比较困难,并且通常涉及更多的部分。
使用场景: 您正在设计一个复杂的大规模系统(通常是绿地项目),要求可扩展性、弹性和灵活性非常高,并且团队已经准备好应对固有的复杂性。当系统扩展能力达到极限,需要进行重大重构来取得长期收益时,也会考虑采用这种架构。
在调整架构前 — 了解乐观性锁定(OCC)在优化架构之前,考虑并发性管理。
乐观锁提供了一种传统悲观锁定的替代方法。它允许在读取时不必锁定行,而是假设冲突很少见,从而让事务继续运行。仅在提交时通过检查版本号或时间戳来验证数据是否已被修改。如果没有被修改,事务将提交。如果有其他事务修改了数据,当前事务将会失败,并且应用程序必须重试。虽然OCC的原则具有广泛的适用性,但其实现的简易性或内置支持的其他功能(如自动行版本控制)可能因数据库而异。
这种方法可以减少中等竞争行的锁等待时间,尤其是在实际冲突更新不频繁的情况下。然而,对于这样的极度“热门”的行,这些行持续遭受高频冲突更新时,可能会导致过多的重试,乐观并发控制因此变得不太有效。可能会比一开始就使用锁更严重地影响性能。当事务短且系统能够高效处理重试时,这种方法非常有用,但在处理最热门的行时,这种方法可能效果不佳。
基于可扩展系统的智能解决方案热点行冲突是高流量系统中的一个常见难题,但正如我们已经看到的,有效的策略可以克服它。从只追加日志到CQRS,每种模式都提供了一种不同的方法来处理这些数据库瓶颈。
关键在于没有一刀切的解决方案。您的选择将取决于诸如写入竞争的激烈程度、系统一致性需求、可接受的复杂性以及审计需求等因素的平衡。通常,最稳健的解决方案结合了多种技术。例如,许多大型金融科技公司成功地将只写入的账本与最终一致的视角结合,以便同时满足审计需求并实现快速读取。
最终,通过精心选择和应用这些模式主动应对热行竞争,对于构建可扩展且可靠的系统,能够随着业务需求的增长而至关重要。
共同學(xué)習(xí),寫下你的評(píng)論
評(píng)論加載中...
作者其他優(yōu)質(zhì)文章