天天看点

购物车从 Cassandra 迁移到 Mongo,以增强购物体验-译文

作者:闪念基因

介绍

购物车是电子商务业务中的关键组成部分。顺畅的结账流程可增强用户对平台的信心。鉴于购物车在保持用户购买意向方面的重要性,我们需要确保购物车系统高度稳定、性能卓越、容错能力强且能适应高流量。

Myntra 的用户群持续快速增长。平台上的用户越多,创建的购物车就越多,每个购物车的操作也越多。购物车数据保存在 Cassandra 数据存储中。Cassandra 是购物车持久化的有力选择,因为它在写入操作和容错能力方面具有巨大的可扩展性。然而,为什么它不是我们完美的选择以及我们向 MongoDB 迁移的过程正是我们将在这篇博文中讨论的内容。

从 Cassandra 迁移到 MongoDB 的决定

在流量高峰期,购物车服务开始出现约 0.05% 的错误率,这是由于超时和延迟增加约 1 秒所致。Cassandra DB 的延迟超过 250 毫秒。这导致大量用户无法“添加到购物车”,并导致我们的收入下降。以下是 Cassandra DB 的一些主要观察结果。

Cassandra 存在的问题

我们使用 Cassandra 作为主要数据库来保存用户购物车。虽然最初考虑到高可用性和可扩展性,这样做是合理的,但最近我们开始注意到不少性能问题影响了购物车应用程序的整体稳定性。

  • 墓碑导致高读取延迟:在购物车中,我们有近 35% 的操作是更新(覆盖)、删除和 TTL 过期,这导致 Cassandra 中的墓碑数量非常高。购物车上的每个删除/更新操作都会产生 16 个墓碑。当墓碑大小增加太多时,会导致读取查询速度变慢,因为数据库需要扫描大量墓碑才能获得实时数据。在我们的案例中,这导致延迟峰值超过 250 毫秒,从而增加了我们的 SLA 和服务性能。为了解决这个问题,我们需要定期修复和压缩数据库,尤其是在 HRD 事件之前。
  • 使用批处理语句:在 Cassandra 中创建的多个查找表导致每次创建、合并或删除购物车时至少需要 4-5 个查询来读取和更新表。这些批处理语句用于保持一致性。这增加了流向数据库的网络流量,增加了整体网络带宽利用率,并增加了延迟。
  • 对非列的支持:缺乏对表中计数器列和非计数器列的支持。我们需要一个单独的表来跟踪产品数量,这增加了管理多个更新的复杂性。

Cassandra 改进

几乎没有做出任何改进来纠正这些问题。

  • GC 宽限期缩短:为了减少墓碑累积,GC 宽限期从默认的 10 天缩短至仅 6 小时,从而加快了墓碑删除速度并提高了性能。我们使用仲裁作为一致性,这确保了我们不会出现由于墓碑在压缩之前未传播到副本而导致已删除数据重新出现的问题。
  • GC 长时间暂停:观察到频繁的系统停顿和读取延迟峰值,与持续 2 到 3 秒的 GC 长时间暂停相吻合。启用 GC 日志记录并分析 GC 性能有助于识别此问题。识别和解决长时间 GC 暂停后,系统稳定性得到增强,读取延迟峰值减少。
  • 堆外缓存:为了解决性能问题,重新配置了 Cassandra 中的缓存设置。key_cache_size_in_mb 和 row_cache_size_in_mb 分别从 2GB 减少到 512MB 和从 4GB 减少到 512MB,而 file_cache_size_in_mb 从 8GB 增加到 18GB。使用块缓存等堆外缓存选项来并行缓存从磁盘读取的 SS 表部分。重新配置缓存设置和利用堆外缓存提高了缓存使用效率并加快了读取操作。
  • 调整布隆过滤器大小:布隆过滤器通过避免在 SS 表中进行不必要的检查来帮助加快读取操作,经过调整以优化其在内存中的大小。优化内存中的布隆过滤器大小通过避免在 SS 表中进行不必要的检查来加速读取操作。
  • 压缩策略:根据工作负载选择合适的压缩策略至关重要。默认的 Size Tiered Compaction 策略适用于写入密集型工作负载,而 Leveled Compaction 可能更适合读取密集型工作负载。根据工作负载选择合适的压缩策略需要优化数据存储和查询响应时间。

总体而言,这些措施使 Cassandra 系统更加高效和稳定,减少了停顿、提高了缓存利用率,并针对特定工作负载优化了压缩策略。实施这些更改后,我们能够支持之前 1.5 倍的负载。但是,这些措施不足以解决我们的问题,因为我们的数据库本质上是瞬态的,需要针对读取和删除进行高度优化的解决方案。

选择 Mongo

在为 Cart 选择数据存储时,需要考虑一些主要因素。强一致性、对二级索引的支持等有助于我们评估 Mongo 是否是正确的选择。

  • Cart 是一个读取量很大的系统,读取量与写入量之比为 3:1。可以通过向 MongoDB 集群添加更多从属节点来处理读取量较大的负载。
  • MongoDB 支持文档中任何字段的二级索引,这使其更适合某些购物车用例。
  • Cassandra 节点需要定期进行修复和压缩操作,而 MongoDB 集群可以避免这些操作。
  • Mongo 是一种低维护服务器,与支持相同流量所需的 Cassandra 节点相比,其节点数量较少。我们将占用空间减少了 40%。
  • 与 Cassandra 相比,Mongo 可以轻松地将业务数据映射到具有可选属性的模式。

在推进 MongoDB 的过程中,我们还考虑了一些其他因素,

  • 整个购物车数据都存储为一个文档。虽然这可以优化一次性加载整个购物车的读取,但这可能会增加磁盘数据吞吐量,因为每次读取或写入都会产生大量 IO。但是,对于加载整个购物车的工作负载,此模型可以通过内部数据库缓存和操作系统页面缓存提供更好的性能。
  • 结账操作需要高度一致的数据,因此整个购物车的结账操作都需要由主节点支持

数据建模和模式设计

旧 Cassandra 架构

旧 Cassandra 模式的一个主要问题是使用多个查找表来获取数据。要访问购物车,系统必须执行两次读取调用:一次是查找表以获取购物车 ID,另一次是查找表以获取相关数据。这导致每次购物车读取操作至少需要两次读取调用。执行任何插入或删除操作时,我们必须至少执行 2 次写入调用。

Mongo 模式

在新的 Mongo 架构中,我们进行了一些更改以纠正上述问题。我们决定不再使用查找表来获取购物车 ID,而是使用购物车 ID 的明确用户标识符部分,这样就无需使用多个查找表。

  • Logged-In-Cart:此文档存储购物车 ID 和会话 ID 以及与购物车相关的数据。决定将会话 ID 明确存储在登录购物车文档中是为了减少合并操作的数量。
  • Non-Logged-In-Cart:此文档包含未登录用户的购物车。此购物车的有效期与已登录的购物车不同。我们不会在此购物车中存储与用户相关的数据。购物车合并过程完成后,此购物车将被删除。

购物车合并流程

当未登录的用户创建购物车时。然后客户登录到他/她的 Myntra 帐户,然后客户希望在登录后显示来自客户未登录购物车和登录购物车的商品。合并登录购物车和未登录购物车的过程称为购物车合并。

Cassandra 与 Mongo DB 处理

Cassandra 流

购物车从 Cassandra 迁移到 Mongo,以增强购物体验-译文

Mongo 流

购物车从 Cassandra 迁移到 Mongo,以增强购物体验-译文

MongoDB购物车过期TTL解决方案

系统为登录和未登录的用户购物车维护一个集合,每个集合具有不同的 TTL(生存时间)。到期日期设置为 0,实际到期时间通过应用程序使用 TTL 的日期时间格式进行管理。TTL 监视器定期扫描通过 TTL 创建的 Mongo 索引并识别需要删除的到期文档。检查点每 50 秒发生一次,在接下来的 10 秒内,监视器会要求 MongoDB 线程使尽可能多的文档到期。

如果未在 10 秒内处理,未过期的文档会堆积在堆栈中,然后 MongoDB 线程会被要求批量过期它们。过期后,会触发重新索引过程。为了最大限度地减少高索引期间的停机时间,文档的过期时间安排在午夜,此时整体流量较低。这种方法减少了创建的索引数量,并确保所有过期时间都发生在非高峰流量时段。

MongoDB 规模基准测试

基准测试的目标是确定正确的硬件和软件配置来处理购物车流量,同时牢记购物车应用程序与 Cassandra 相比的一致性要求。

  • 第一步是确定要进行基准测试的 NFR
  • 满足未来需求的并发请求数
  • 每个 API 的预期吞吐量
  • 考虑所有正在进行的功能以实现规模
  • 预期延迟水平
  • 确定与 Mongo 一起用于基准测试练习的数据模型
  • 确定硬件配置 -
  • 核心数
  • 考虑工作负载的热数据(例如 25–30%)的 RAM
  • 估计存储容量
  • 需要启用缓存的硬盘
  • 确定 MongoDB 配置。
  • Mongo 版本
  • 主站、从站设置(开始时为 1 个主站、2 个从站)
  • 检查点 — 每 1 分钟
  • 日志同步 — 每 100 毫秒
  • 缓存配置
  • 一致性级别
  • 根据当前的流量模式确定工作负载。
  • 重度读取,读取:写入比率为 3:1
  • 准备示例数据集
  • 确定客户端配置
  • 连接池大小
  • 执行不同的工作负载运行
  • 考虑所有读取
  • 考虑混合负载
  • 考虑不同的一致性级别
  • 捕获应用程序和数据库指标
  • 延迟、吞吐量
  • CPU 利用率、RAM 使用率
  • 磁盘每秒读取和写入的 IOPS 数
  • 磁盘 IO 利用率
  • 磁盘 IO 吞吐量
  • P99 和 p99.9 延迟

我们能够对~170 万 RPM 和延迟~50ms(p99.9)进行基准测试

迁移策略

以下是移民战略设定的目标,

  • 零停机时间
  • 对交通零影响
  • 启用 AB 来路由流量

启用 Cassandra 和 Mongo 之间的双向双同步

在执行一次性迁移之前,我们在 Cassandra 和 MongoDB 之间建立了数据同步管道。对 Cassandra 的任何写入都将同步到 MongoDB,反之亦然。这需要异步处理,这样才不会影响流入的用户请求的写入延迟。处理所有竞争条件、故障和重试,以便两个数据存储之间的数据始终保持同步。

作为双同步过程的一部分,在一个数据库中执行的所有操作都将同步到另一个数据库。建立了从主数据库到辅助数据库的基于 Kafka 的管道。当用户执行任何操作时,都会创建一个日记条目,以捕获他/她所执行的操作。在 Kafka 管道中创建并发布事件。数据库中的这个日记条目确保我们不会在没有使用辅助数据库中的事件的情况下切换用户分配的数据库。一旦消费者使用了该事件,数据库条目就会被删除。如果我们找不到与用户相关的任何条目,那么用户就有资格进行迁移。

一次性数据迁移

该计划涉及一次性将完整的购物车数据集从 Cassandra 迁移到 MongoDB。这将使用批处理应用程序完成,该应用程序读取查找表并通过查询 Cassandra Cart 表获取最新数据来移动与每个条目相关的购物车。为防止重复迁移,已通过双同步迁移的记录将不会进行批量迁移。

使用 A/B 进行增量流量路由

发布功能门控和用户驱动 A/B 的一次性迁移配置。启用 A/B 以便流量流入 MongoDB 集群(例如 1% 的用户)。一旦没有报告问题,就向 100% 的用户推出。

确保容错能力

双同步日志

  • 为了确保在双同步或一次性迁移期间不会丢失数据,我们决定维护一个日志。这个日志的理念是,每当在用户的主数据库(可以是 Cassandra 或 Mongodb)上执行写入操作时,我们都会在日志中维护该事件,直到该数据同步到另一个数据库。
  • 让我们考虑这样一种情况,如果用户对他们的购物车进行了一些更改,并且在此期间我们为该用户启用 AB,那么可能会丢失一些数据,因为双同步是一个异步过程,因此需要一些时间来复制整个数据。
  • 因此,每当用户尝试访问他们的购物车时,我们首先调用日志并检查该用户是否已经存在事件,我们获得主数据库(即上次写入发生的位置),该数据库也在该模式中维护。
  • 一旦双同步作业完成,该事件就会从日志中删除。
  • 此外,我们在日志中只维护任何用户的单个事件,这意味着如果用户连续执行 2 次写入,即使第一次写入未复制到辅助数据库,我们也将只保留最新写入的事件,这确保始终将最新数据同步到其他数据库。
  • 对于所有未能在 Kafka 中发布的记录(无论由于何种原因),同步作业将获取与更改相关的数据库条目,然后根据这些条目更新第二个数据库。
  • 在一次性迁移期间,我们有一项工作就是不断对随机用户的数据库数据进行采样,以查找任何差异。这非常重要,因为我们对两个数据存储中的架构设计进行了重大更改。
购物车从 Cassandra 迁移到 Mongo,以增强购物体验-译文

指标、警报和监控

以下是一些有助于更快解决问题的指标。

一次性数据迁移指标

  • 一次性迁移(速率,总体)
  • 一次性迁移中的错误(比率,总体)
  • 数据验证错误(率,总体)

双同步指标

  • 更新事件发布(速率,总体)
  • 更新事件消耗(速率,总体)
  • 出版错误、消费(率、总体)

部署策略

MongoDB 是“暗发布”的,以避免对传入流量造成任何风险。按顺序推出对于无缝迁移到 Mongo 发挥了重要作用。

  • 首先,我们启用双同步
  • 然后我们启用同步日志(数据库决策表,它将确保您的数据库在数据完全迁移之前不会发生变化)
  • 对现有购物车进行一次性迁移(所有未通过双同步移动的购物车)
  • 协调一次性迁移中的所有错误
  • 打开功能门控
  • 为一小部分用户开启 A/B 功能,以将流量路由到 Mongo
  • 监控仪表板是否存在任何异常
  • 修复两个数据存储之间的任何同步相关问题
  • 逐步增加 A/B 用户
  • 覆盖流向 Mongo 的 100% 用户流量
  • 清理 Cassandra 中的所有现有数据
  • 一旦稳定,计划退役 Cassandra 节点

结论

现在已经过去了几个月,自迁移以来我们没有遇到任何问题,并且我们已成功解决了我们在 Cassandra 中观察到的所有性能瓶颈。数千万辆购物车已迁移,延迟从 250 毫秒(高峰期间)改善到 < 25 毫秒。通过更好的架构设计和 Mongo DB 的读取性能,读取性能已大幅提高。这还将我们的 硬件占用空间减少了 40%,并且消除了 Cassandra 所需的大量维护和运营成本。但我们从不想排除这样一个事实,即从购物车演变之初就采用更好架构的 Cassandra 可以实现比我们更好的性能。

总的来说,通过扩展 Mongo 节点,我们现在有能力处理高达当前 3 倍的流量,之后我们才需要重新考虑未来的下一个最佳选择。

作者:Parul Agnihotri-Myntra Engineering

Co-Contributors: Shubham Shailesh Doijad, Pramod MG,Ajit Kumar Avuthu, Vindhya Shanmugam, Subhash Kumar Mamillapalli

出处:https://medium.com/myntra-engineering/cart-migration-from-cassandra-to-mongo-for-an-enhanced-shopping-performance-104c5d3b0da7

继续阅读