写点什么

行业案例| MongoDB 在腾讯零售优码中的应用

  • 2022 年 5 月 07 日
  • 本文字数:4467 字

    阅读完需:约 15 分钟

行业案例| MongoDB在腾讯零售优码中的应用

本文主要分享腾讯智慧零售团队优码业务在 MongoDB 中的应用,采用腾讯云 MongoDB 作为主存储服务给业务带来了较大收益,主要包括:高性能、快捷的 DDL 操作、低存储成本、超大存储容量等收益,极大的降低了业务存储成本,并提高了业务迭代开发效率。


一. 业务场景   

腾讯优码从连接消费者到连接渠道终端,实现以货的数字化为基础的企业数字化升级,包含营销能力升级和动销能力升级。腾讯优码由正品通、门店通和会员通三个子产品组成。

腾讯优码整体视图

1.1 正品通


腾讯优码正品通提供防伪鉴真能力,实现一物一码全流程正品追溯,全链路数据存储至区块链,确保真实可信;更可直达品牌私域,实现流量进一步转化;同时正品通提供微信域内的品牌保护能力,阻断品牌伪冒网站传播、帮助消费者识别假冒商品。

产品主要包含如下核心特性:

1.2 门店通


腾讯优码门店通是服务品牌方、经销商、业代员以及终端门店四大零售链路核心角色实现基于终端销售门店的销售管理手段升级与销售额提升

产品主要包含如下核心特性:

1.3 会员通


腾讯优码会员通是面向零售品牌商提供的 SaaS+定制化服务的产品,以扫码为切入点,连接线上线下场景。提供丰富的扫码/互动活动模型、活动评估体系助力品牌连接消费者。

产品主要包含如下核心特性:


二.码存储选型   

腾讯智慧零售优码业务存储零售商品二维码信息,该信息为智慧零售最核心的数据信息,提供“从连接消费者到连接渠道终端,实现以货的数字化为基础的企业数字化升级”相关服务。因此码数据存储问题是项目最核心的问题。

2.1 需求和方案


要解决码存储问题,首先需要分析码存储的特征。经过分析码存储问题的主要特征是:

  • 海量数据:腾讯优码做的商品二维码,随着越来越多的商品使用腾讯优码业务,二维码数据开始呈现指数级增长。

  • 关联存储:码与码之间存在 1:1 和 1:N:N 的关联关系,需要存储这种关系,并且提供相应的关联查询。

  • 多维度查询:针对不同的应用场景需要提供不同维度的条件查询。

在获取到码存储特征之后,经过多方调研和排查之后,初步选取了 2 种存储方案:

1. MySQL + ES:MySQL 分库分表存储码元数据,提供需要高性能的读写场景;然后根据需求将部分数据同步 ES 以应对各种复杂的查询场景。

2. MongoDB:MongoDB 是全球排名最高的分布式 NoSQL 数据库,其核心特性是 No Schema、高可用和分布式,非常适合分布式存储。

2.2 方案分析

2.2.1 MySQL + ES 方案分析

MySQL + ES 是一个比较常见的存储解决方案,并且在很多领域内被广泛应用,如会员或商品信息储存领域。此方案的优势是能够提供非常多的查询方式和不同的性能保障,可以应对各种各样复杂的业务查询需求。

MySQL + ES 的常见架构是写操作直接作用于 MySQL,然后通过 canal + Kafka 的方式将数据变更同步到 ES,然后再根据不同的查询场景从 MySQL 或者 ES 查询数据。下图是在腾讯优码业务场景下可能的架构图:

从架构图可以看出,本方案存在几个问题:

  • 数据同步和一致性问题:这个问题在数据量不大的情况下不会有影响。但是如果数据量百亿甚至千亿时就是一个非常严重的问题。

  • 数据容量问题:一般情况下 MySql 的单表数据最好维持在百万级一下,如果单表数据量过大之后读写都是个问题。那么如果要存储千亿数据就要几千上万张表,如此多的分表需要业务自己维护时开发运维都是几乎不可行的。

  • 成本问题:数据冗余存储,会增加额外的存储成本。同时 ES 为了保证数据可靠性和查询性能,需要更多的机器和内存。而且 ES 存在数据膨胀问题,对于同样的数据,需要相当 MySql 来说更大的磁盘。

  • DDL 运维问题:MySql 在分库分布之后,因为 DDL 语句需要操作大量的库表,因此非常耗时,同时也容易出错。根据我们以前的项目经验来说,当有几百张表,单表几十万数据时,一个简单的增加字段的 DDL 语句也需要 1 小时甚至更久才能完成。

  • 开发成本问题:此方案需要业务自己维护分库分表、数据同步和根据需求选取不同的查询引擎。不仅整个架构复杂,同时在做业务需求时需要慎重考虑,稍不注意使用错的存储引擎就可能导致性能问题。

  • 水平扩容问题:MySql 分库分表要扩容需要业务手动 rehash 搬迁数据,成本非常高,而且很难处理扩容过程中的数据读写问题。

2.2.2 MongoDB 方案分析

MongoDB 是非常出名的分布式存储引擎,具备 No Schema、高可用、分布式、数据压缩等多方面的优势。虽然 MongoDB 是 NoSQL 存储引擎,但是其 Wired Tiger 存储引擎和 innerdb 一样底层使用的是 B+树,因此 MongoDB 在提供分布式存储的前提下同时能够提供大部分 MySql 支持的查询方式。因此,在使用 MongoDB 时,我们不需要 MySql 冗余表或者 ES 来支持大部分的分布式查询。在腾讯优码的应用场景下,基于 MongoDB 的存储架构如下图所示:

从图中可以看出,MongoDB 可以避免冗余存储带来的数据同步和一致性问题、存储成本问题、资源/运维/开发成本。而且在进一步测试和分析 MongoDB 的功能和性能之后,我们发现 MongoDB 还具备如下优势:

  • 无 DDL 问题:因为 MongoDB 是 No Schema 的,因此可以避免 MySql 的 DDL 问题。

  • 数据自动均匀:MongoDB 有自动 rebalance 功能,可以在数据分布不均匀的时候,自动搬迁数据,保证各个分片间的负载均匀。

  • 更低的成本:MongoDB 自带数据压缩,在同等数据下,MongoDB 需求的磁盘更少。

  • 更高的性能:MongoDB 最大化的利用了内存,在大部分场景下拥有接近内存数据库的性能。经过测试 MongoDB 的单分片读性能约为 3 万 QPS。

  • 更多的读写方式:虽然 MongoDB 没有 ES 的倒排索引,其支持的查询方式略逊于 ES。但是,MongoDB 在拥有大部分 ES 的查询能力的同时,其性能远高与 ES;而且相对 MySql 来说 MongoDB 的字段类型支持内嵌对象和数组对象,因此能满足跟多的读写需求。


2.3 方案对比


通过前面的分析,我们初步判断 MongoDB 拥有更好的表现。因此为了进一步确定 MongoDB 的优势,我们深入对比了 MySQL + ES 与 MongoDB 在各方面的表现。

2.3.1 存储成本对比


MongoDB 在存储上的优势主要体现在两个方面:数据压缩和无冗余存储。

为了更加直观的看出磁盘使用情况,我们模拟了在腾讯优码业务场景下,MySQL + ES 和 MongoDB 下的实际存储情况。

一方面,在 MySQL+ES 的方案下,为了满足需要我们需要将冗余一份 ES 数据和 MySQL 的冗余表。其中码的核心数据存储在 MySQL 中,其磁盘总量仅占总的 38.1%。前面说过 MongoDB 的方案是不需要冗余存储的,因此使用 MongoDB 可以减少这 61.9%的总数据容量。

另一方面,经过测试同样的码数据,MongoDB snappy 压缩算法的压缩率约 3 倍,zlib 压缩算法的压缩率约 6 倍。因此,虽然业务为了保证系统的稳定性而选择 snappy 压缩算法,但 MongoDB 仍然只需要 MySQL 三分之一的磁盘消耗。

2.3.2 开发运维成本

  • 无数据同步链路:使用 MongoDB 不需要数据同步,因此就不需要维护 canal 服务和 kafka 队列,大大减少开发和运维难度。

  • 人力成本收益:在 MySQL+ES 架构下每次对 MySQL 集群做添加字段变更,都需运维 一定的人日投入,并且存在业务抖动风险,同时影响业务迭代发布进度,迭代发布耗时且风险大。

  • 开发维护成本:MongoDB 存储架构简单,一份存储,无数据一致性压力。

  • 动态扩容:MongoDB 支持随时动态扩容,基本不存在容量上限问题,而 MySQL 在扩容时需要业务手动 rehash 变迁数据,并自己保证数据一致性和完整性。


2.3.3 性能对比


经过压测,同样的 4C8G 的机器配置下,MySQL 和 MongoDB 在大数据量下写性能基本一致。MySQL 的读性单分片约 6000QPS 左右,ES 的性能只有 800QPS 左右。而 MongoDB 单分片地读性能在 3 万 QPS 左右,远高于 MySQL 和 ES 的性能。

2.3.4 总结

经过上面的分析和对比之后,可以明显看出 MongoDB 在各方面都有优势。为了更加直观的看出不同方案的差异,这里列出了从功能、性能、成本、可扩展性和可维护性等 5 个方面的对比数据:

综上所述,MongoDB 不仅能完全满足业务需求,同时在性能、成本、可维护性等各方面都优于其它两种方案,因此腾讯优码最终选用的是 MongoDB 作为业务核心数据码的存储方案。


三.MongoDB 分片集群优化过程

零售优码业务对成本要求较高、数据量较大,线上真实读写流量不是太高(读 3W QPS 要求),因此采用低规格 4C8G 规格(单节点规格)分片模式集群部署。

3.1 分片集群片建选择+预分片


零售优码数据查询都是通过码 id 查询,因此选择码 id 作为片建,这样可以最大化查询性能,索引查询都可以通过同一个分片获取数据。此外,为了避免分片间数据不均衡引起的 moveChunk 操作,因此选择 hashed 分片方式,同时提前进行预分片,MongoDB 默认支持 hashed 预分片,以优码详情表为例,预分片方式如下:

use db_code_xx  sh.enableSharding("db_code_xx")  //n为实际分片数  sh.shardCollection("db_code_xx.t_code_xx", {"id": "hashed"}, false,{numInitialChunks:8192*n})  
复制代码

3.2 低峰期滑动窗口设置


由于 MongoDB 实例节点规格低(4C8G),当分片间 chunks 数据不均衡的情况下,会触发自动 balance 均衡,由于实例规格低,balance 过程存在如下问题:

  • CPU 消耗过高,迁移过程甚至消耗 90%左右 CPU

  • 业务访问抖动,耗时增加

  • 慢日志增加

  • 异常告警增多

以上问题都是由于 balance 过程进行 moveChunk 数据搬迁过程引起,为了快速实现数据从一个分片迁移到另一个分片,MongoDB 内部会不停的把数据从一个分片挪动到另一个分片,这时候就会消耗大量 CPU,从而引起业务抖动。

MongoDB 内核也考虑到了 balance 过程对业务有一定影响,因此默认支持了 balance 窗口设置,这样就可以把 balance 过程和业务高峰期进行错峰,这样来最大化规避数据迁移引起的业务抖动。例如设置凌晨 0-6 点低峰期进行 balance 窗口设置,对应命令如下:

use config  db.settings.update({"_id":"balancer"},{"$set":{"activeWindow":{"start":"00:00","stop":"06:00"}}},true) 
复制代码

3.3 写多数派优化


由于优码二维码数据非常核心,为了避免极端情况下的数据丢失和数据回归等风险,因此客户端采用 writeConcern={w: “majority”}配置,确保数据写入到副本集大多数成员后才向客户端发送确认。

链式复制的概念:假设节点 A(primary)、B 节点(secondary)、C 节点(secondary),如果 B 节点从 A 节点同步数据,C 节点从 B 节点同步数据,这样 A->B->C 之间就形成了一个链式的同步结构,如下图所示:


      

MongoDB 多节点副本集可以支持链式复制,可以通过如下命令获取当前副本集是否支持链式复制:

cmgo-xx:SECONDARY> rs.conf().settings.chainingAllowed  true  cmgo-xx:SECONDARY>   
复制代码

此外,可以通过查看副本集中每个节点的同步源来判断当前副本集节点中是否存在有链式复制情况,如果同步源为 secondary 从节点,则说明副本集中存在链式复制,具体查看如下副本集参数:

cmgo-xx:SECONDARY> rs.status().syncSourceHost  xx.xx.xx.xx:7021  cmgo-xx:SECONDARY>   
复制代码

由于业务配置为写多数派,鉴于性能考虑可以关闭链式复制功能,MongoDB 可以通过如下命令操作进行关闭:

cfg = rs.config()  cfg.settings.chainingAllowed = falsers.reconfig(cfg)  
复制代码
  • 链式复制好处:可以大大减轻主节点同步 oplog 的压力。

  • 链式复制不足:当写策略为 majority 时,写请求的耗时变大。


基于写性能考虑,当业务采用“写大多数”策略时,直接关闭链式复制功能,确保写链路过长引起的写性能下降。


关于作者:

CSIG 腾讯优码团队、腾讯 MongoDB 团队


用户头像

还未添加个人签名 2019.07.27 加入

还未添加个人简介

评论

发布
暂无评论
行业案例| MongoDB在腾讯零售优码中的应用_mongodb_MongoDB中文社区_InfoQ写作社区