优化高写入数据库工作负载以实现低延迟
高写入数据库工作负载带来了一系列与读密集型工作负载截然不同的挑战。例如:
- 扩展写入可能成本高昂,特别是按操作付费时,写入成本通常是读取的5倍
- 锁定会增加延迟并降低吞吐量
- I/O瓶颈可能导致写入放大并使崩溃恢复复杂化
- 数据库反压可能限制传入负载
虽然成本很重要,但在许多情况下,这不是我们在此要讨论的主题。相反,让我们重点关注团队常遇到的性能相关复杂性,并讨论应对这些问题的选项。
什么是"实时高写入工作负载"?
首先,让我们澄清"实时高写入"工作负载的含义。我们指的是:
- 摄取大量数据(例如超过5万OPS)
- 写入操作多于读取操作
- 受严格延迟SLA约束(例如个位数毫秒的P99延迟)
在现实世界中,它们出现在从在线游戏到实时证券交易所的各种场景中。一些具体示例:
- 物联网工作负载:通常涉及时间序列数据的小型但频繁的仅追加写入。这里的摄取速率主要由收集数据的端点数量决定。
- 日志记录和监控系统:也处理频繁的数据摄取,但没有固定的摄取速率。它们不一定只是追加操作,并且可能容易出现热点。
- 在线游戏平台:需要处理实时用户交互,包括游戏状态变化、玩家操作和消息传递。工作负载往往是尖峰式的,活动突然激增。
- 电子商务和零售工作负载:通常是更新密集型的,通常涉及批处理。
- 广告技术和实时竞价系统:需要瞬间决策。
- 实时证券交易所系统:必须支持高频交易操作、持续股票价格更新和复杂的订单匹配过程。
影响写入性能的关键架构和配置考虑因素
存储引擎架构
存储引擎架构的选择从根本上影响数据库的写入性能。存在两种主要方法:LSM树和B树。
已知能高效处理写入的数据库,如ScyllaDB、Apache Cassandra、HBase和Google BigTable,使用日志结构合并树。这种架构非常适合处理大量写入。由于写入立即追加到内存中,这允许非常快速的初始存储。
使用B树结构,每个写入操作都需要定位和修改树中的节点——这涉及顺序和随机I/O。随着数据集的增长,树可能需要额外的节点和重新平衡,导致更多的磁盘I/O,从而影响性能。B树通常更适合涉及连接和临时查询的工作负载。
负载大小
负载大小也会影响性能。对于小负载,吞吐量良好,但CPU处理是主要瓶颈。随着负载大小的增加,整体吞吐量降低,磁盘利用率也会增加。
压缩
对于高写入工作负载,磁盘利用率是需要密切关注的事项。压缩有助于控制这一点——因此请明智选择压缩策略。更快的压缩速度对于高写入工作负载很重要,但也要考虑可用的CPU和内存资源。
Compaction
对于基于LSM的数据库,选择的compaction策略也会影响写入性能。Compaction涉及将多个SSTable合并为更少、更有组织的文件,以优化读取性能、回收磁盘空间、减少数据碎片并保持整体系统效率。
选择compaction策略时,可以追求低读取放大,使读取尽可能高效。或者,可以通过避免compaction过于激进来追求低写入放大。或者,可以优先考虑低空间放大,并让compaction尽可能高效地清除数据。
对于高写入工作负载,我们警告用户不惜一切代价避免分层compaction。该策略专为读密集型用例设计。使用它可能导致令人遗憾的40倍写入放大。
批处理
在像ScyllaDB和Cassandra这样的数据库中,批处理实际上可能是一个陷阱——特别是对于高写入工作负载。如果习惯于关系数据库,批处理可能看起来是处理大量写入的好选择。但如果不小心操作,它实际上可能会减慢速度。
对于高写入情况,请仔细构建批处理,以避免大型跨节点批处理可能引入的延迟。
总结
我们提供了相当多的警告,但不用担心。很容易编译经验教训列表,因为许多团队在处理实时高写入工作负载方面非常成功。现在您知道了他们的许多秘密,而无需经历他们的错误。:-)