本文由Neha Gupta和Kanica Mandhania共同撰写,发表于2023年12月22日。
Zomato是一家总部位于印度的餐饮聚合平台,运营覆盖超过1000个城市,列出了超过35万家餐厅。自2008年成立以来,Zomato经历了巨大的增长,已经成为印度食品科技行业的市场领军者。随着在线食品订购需求的不断增加,Zomato意识到创新在满足可扩展性需求中的重要性。
由于餐饮业务的特殊性质,顾客流量主要集中在用餐高峰期,这导致高峰和非高峰时段的工作负载差异显著。此外,在诸如深秋节和新年前夕等特殊场合,Zomato会经历大规模的流量激增,明显高于通常的日流量。因此,Zomato必须选择一种数据库,确保在任何规模下都能保持稳定的低延迟,并具有在低活动期无需过度配置即可处理流量峰值的能力。
在本文中,我们将阐述促使Zomato从关系数据库和TiDB迁移到的主要因素。这种全托管的无服务器键值NoSQL数据库使我们能够有效管理流量尖峰,并降低总拥有成本(TCO)。
Zomato的账单平台负责管理订单后的处理,主要专注于维护账本和处理在线订餐、外出就餐、Blinkit、Hyperpure和喂养印度等多个业务垂直的付款。此平台有效地处理支付分发给餐厅合作伙伴和骑手,规模庞大。
在撰写本文时,Zomato的账单平台每天处理约1000万个事件,每周约产生100万笔支付。由于生成发票和处理付款是Zomato的关键任务,因此账单系统的可用性和韧性对我们业务的成功尤为重要。
Zomato账单平台架构采用异步消息传递方式,利用Kafka将独立微服务以松耦合的方式集成在一起,以便于扩展和灵活发展。该平台既是Kafka事件的生产者,也是消费者,处理账单订单,并为各种业务用例生成处理后的账本。以下是传统架构的示意图。
Zomato账单平台曾使用TiDB,一种开源分布式SQL数据库,支持在线事务处理(OLTP)和在线分析处理(OLAP)工作负载。TiDB结合了NoSQL数据库的可扩展性和传统关系数据库的ACID合规性,其分布式架构实现了水平扩展、容错和高可用性。TiDB系统由多个互相通信的组件组成,形成一个完整的系统。
以下是维护TiDB系统的关键组件:
组件 | 说明 |
---|---|
TiDB服务器 | 作为无状态SQL层,通过MySQL协议提供外部访问,可以水平扩展。 |
Placement Driver | 管理集群中的元数据和数据调度命令。 |
TiKV服务器 | 负责分布式数据存储,功能为事务性键值存储引擎。 |
TiFlash服务器 | 一种专门的列式存储服务器,针对快速分析处理进行了优化。 |
Zomato最初引入TiDB来管理我们的OLTP工作负载。选择TiDB是因为Zomato工程团队在关系模型和MySQL方面的专业知识,以及其横向扩展的能力可以满足我们的扩展需求。
然而,随着Zomato的运营规模的剧增,特别是在新增Blinkit和HyperPure业务后,我们的账单平台也需要支持多租户。我们在扩展和维护TiDB数据库时面临了一些挑战:
DynamoDB是一种无服务器、NoSQL、全托管的数据库服务,在任何规模下均能提供单数毫秒的响应时间,使您能够开发和运行现代应用,同时只需为您使用的功能付费。
除了我们已经在Zomato的多个关键业务服务中使用DynamoDB外,以下功能突显了DynamoDB的一些关键特性,可以提高可扩展性、简化开发人员的生产力、减少重复任务所花费的时间,并降低我们的TCO:
为了应对上述问题,Zomato团队确定需要重新设计数据存储层。在对多种数据库进行了全面评估后,工程团队决定使用DynamoDB。以下部分突出了指导我们方法的关键要点。
在原TiDB设计中,我们遵循了传统的关系数据库管理系统(RDBMS)方法。每个表都专门用于存储特定实体的数据,并具有连接到其他表的外键的唯一键。为了从相关表中检索数据并呈现统一的结果,执行了涉及多个表的JOIN操作。例如,我们针对每个业务垂直(如食品配送、外出就餐和HyperPure)有单独的表集合,其中包含了payout_details
、billing_ledger
、payout_order_mapping
和billing_breakup
等实体,如下图所示。
删除)
考虑到我们在多个微服务中使用DynamoDB的经验,我们决定将这种关系模式简化为一个统一的DynamoDB表,采用设计模式,涵盖所有业务,并存储各种实体的数据。这一策略有效地将相关数据汇聚到一个表中(见下图),通过消除从不同磁盘位置获取数据的需要来提升性能,并且有效减少阅读操作的成本,因为一次读取操作现在可以检索所有所需数据,而不是对不同实体进行多次查询。
删除)
在DynamoDB中,数据以,这些分区是物理存储单元。每个表可以有多个分区,通常情况下,分区键决定了数据的存储位置。对于具有复合主键的表,排序键可以用作分区边界。如果集合大小超过10GB,DynamoDB会根据排序键划分分区。设计模式和确定分区键对高效数据访问至关重要。数据访问不均衡可能导致,其中一个分区承受更高的工作负载,从而导致限流和I/O能力的低效利用。
我们通过引入复合属性的分区键来解决这一问题,从而实现数据在多个分区之间的分发。单个表的分区键由商户ID、支付周期和业务垂直的组合构成,各元素以分隔符分隔。对于我们的工作负载,这种方法改善了分区键的基数,并确保读取和写入操作尽可能均匀地分配在各表上,从而避免性能下降。
此外,我们利用了反向索引方法来查询数据。此设计模式通常与DynamoDB一起使用。它允许查询多对多关系的另一侧,而这在标准的键值存储方法中通常不可行。
与主表的分区键相似,确保全局二级索引(GSI)的分区键能够保障读取和写入操作在分区之间均匀分布是非常重要的,以避免限流。我们通过引入基于业务逻辑的一个数字,即_划分编号_,与索引键结合,从而实现了。结果,更新后的GSI格式现在表示为<index- key>_<division-number>
。
我们希望从TiDB迁移到DynamoDB的过程能够实现零停机且不会干扰正常操作。我们采用了逐步迁移的方法,并制定了双写、复制、数据同步和故障转移机制等策略,以确保在迁移过程中能够持续访问我们的数据。
以下示意图展示了每个阶段的详细信息。
删除)
该方法使Zomato在整个迁移过程中保持了对关键数据的持续访问,确保了流畅的操作,并将对生产力或客户体验的任何负面影响最小化。
在这一部分,我们讨论了Zomato通过此解决方案所获得的性能和可扩展性提升。
从TiDB切换到DynamoDB的决策主要是为了提升应用性能和减少操作复杂度。下图显示的性能提升表明,微服务响应时间平均下降了90%。另一个值得注意的观察是,无论流量水平如何,微服务的响应时间始终保持在75毫秒左右。
删除)
原始架构中数据库层的性能成为一个显著的瓶颈,导致微服务的吞吐量仅为每分钟2000个请求(见下图)。因此,解决数据库层的局限性以增加微服务的吞吐量和整体性能,满足Zomato的可扩展性需求,是非常重要的。
删除)
完成迁移至DynamoDB后,数据库性能显著提升,成功消除了数据库层的瓶颈。因此,在当前规模下,微服务的吞吐量提升至每分钟8000个请求,使Zomato能够处理四倍于以前设计的流量。增强的吞吐量导致延迟减少,实现了近实时账单处理,从而改善了SLA。
删除)
在采用DynamoDB的新解决方案中,我们只需为所使用的资源付费,这与之前在TiDB中因突然流量高峰而过度配置数据库的做法不同。因此,我们的账单系统每月支出显著减少了50%。
在本文中,我们探讨了Zomato如何成功迁移至DynamoDB而没有任何停机时间或对客户体验造成负面影响。这一转型的实现方式无缝,使Zomato能够高效满足不断扩增的用户基数,实现四倍的交易处理能力,延迟减少近90%,并把数据库成本优化了50%。此外,这一解决方案还改善了我们的整体报告时间,从而促进了日常决策的优化。它还改善了商户账户报表的响应时间,提升了各利益相关者的用户体验和SLA。
要学习更多关于如何在使用DynamoDB时最大化性能和最小化吞吐量成本的信息,请参考。
删除)NehaGupta** 是Zomato的工程经理,拥有7年的经验,负责设计和开发能够有效处理海量数据的健壮和可扩展的解决方案。
删除)KanicaMandhania** 是Zomato的高级软件工程师,拥有7年的工作经验。她热衷于利用技术解决客户挑战,并热爱构建让生活更便捷的产品。
删除)VishalGupta** 是AWS印度的高级解决方案架构师,位于德里。Vishal与大型企业和
Leave a Reply