写点什么

MongoDB 在评论中台的实践

发布于: 2021 年 03 月 01 日

本文主要讲述 vivo 评论中台在数据库设计上的技术探索和实践。

一、业务背景


随着公司业务发展和用户规模的增多,很多项目都在打造自己的评论功能,而评论的业务形态基本类似。当时各项目都是各自设计实现,存在较多重复的工作量;并且不同业务之间数据存在孤岛,很难产生联系。因此我们决定打造一款公司级的评论业务中台,为各业务方提供评论业务的快速接入能力。在经过对各大主流 APP 评论业务的竞品分析,我们发现大部分评论的业务形态都具备评论、回复、二次回复、点赞等功能。


具体如下图所示:



涉及到的核心业务概念有:

  • 【主题 topic】评论的主题,商城的商品、应用商店的 APP、社区的帖子

  • 【评论 comment】用户针对于主题发表的内容

  • 【回复 reply】用户针对于某条评论发表的内容,包括一级回复和二级回复


二、数据库存储的选择


团队在数据库选型设计时,对比了多种主流的数据库,最终在  MySQL  和  MongoDB  两种存储之进行抉择。



由于评论业务的特殊性,它需要如下能力:


  • 【字段扩展】业务方不同评论模型存储的字段有一定差异,需要支持动态的自动扩展。

  • 【海量数据】作为公司中台服务,数据量随着业务方的增多成倍增长,需要具备快速便捷的水平扩展和迁移能力。

  • 【高可用】作为中台产品,需要提供快速和稳定的读写能力,能够读写分离和自动恢复。


而评论业务不涉及用户资产,对事务的要求性不高。因此我们选用了 MongoDB 集群 作为最底层的数据存储方式。


三、深入了解 MongoDB

3.1 集群架构


由于单台机器存在磁盘/IO/CPU 等各方面的瓶颈,因此以 MongoDB 提供集群方式的部署架构,如图所示:



主要由以下三个部分组成:

  • mongos:路由服务器,负责管理应用端的具体链接。应用端请求到 mongos 服务后,mongos 把具体的读写请求转发到对应的 shard 节点上执行。一个集群可以有 1~N 个 mongos 节点。

  • config:配置服务器,用于分存储分片集合的元数据和配置信息,必须为 复制集(关于复制集概念戳我) 方式部署。mongos 通过 config 配置服务器合的元数据信息。

  • shard:用于存储集合的分片数据的 mongod 服务,同样必须以 复制集 方式部署。


3.2  片键


MongoDB 数据是存在 collection(对应 MySQL 表)中。集群模式下,collection 按照 片键(shard key)拆分成多个区间,每个区间组成一个 chunk,按照规则分布在不同的 shard 中。并形成元数据注册到 config 服务中管理。



分片键只能在分片集合创建时指定,指定后不能修改。分片键主要有两大类型:


  • hash 分片:通过 hash 算法进行散列,数据分布的更加平均和分散。支持单列和多列 hash。

  • 范围分片:按照指定片键的值分布,连续的 key 往往分布在连续的区间,更加适用范围查询场景。单数据散列性由分片键本身保证。


3.3 评论中台的实践

3.3.1 集群的扩展


作为中台服务,对于不同的接入业务方,通过表隔离来区分数据。以 comment 评论表举例,每个接入业务方都单独创建一张表,业务方 A 表为  comment_clientA ,业务方 B 表为 comment_clientB,均在接入时创建表和相应索引信息。但只是这样设计存在几个问题:


  • 单个集群,不能满足部分业务数据物理隔离的需要。

  • 集群调优(如 split 迁移时间)很难业务特性差异化设置。

  • 水平扩容带来的单个业务方数据过于分散问题。


因此我们扩展了 MongoDB 的集群架构:



  1. 扩展后的评论 MongoDB 集群 增加了 【逻辑集群】和【物理集群】的概念。一个业务方属于一个逻辑集群,一个物理集群包含多个逻辑集群。

  2. 增加了路由层设计,由应用负责扩展 Spring 的 MongoTemplate 和连接池管理,实现了业务到 MongoDB 集群之间的切换选择服务。

  3. 不同的 MongoDB 分片集群,实现了物理隔离和差异调优的可能。


3.3.2 片键的选择


MongoDB 集群中,一个集合的数据部署是分散在多个 shard 分片和 chunk 中的,而我们希望一个评论列表的查询最好只访问到一个 shard 分片,因此确定了 范围分片 的方式。


起初设置只使用单个 key 作为分片键,以 comment 评论表举例,主要字段有{"_id":唯一 id,"topicId":主题 id,"text":文本内容,"createDate":时间} ,考虑到一个主题 id 的评论尽可能连续分布,我们设置的分片键为   topicId。随着性能测试的介入,我们发现了有两个非常致命的问题:


  • jumbo chunk 问题

  • 唯一键问题


jumbo chunk:


官方文档中,MongoDB 中的 chunk 大小被限制在了 1M-1024M。分片键的值是 chunk 划分的唯一依据,在数据量持续写入超过 chunk size 设定值时,MongoDB 集群就会自动的进行分裂或迁移。而对于同一个片键的写入是属于一个 chunk,无法被分裂,就会造成  jumbo chunk 问题。


举例,若我们设置 1024M 为一个 chunk 的大小,单个 document 5KB 计算,那么单个 chunk 能够存储 21W 左右 document。考虑热点的主题评论(如微信评论),评论数可能达到 40W+,因此单个 chunk 很容易超过 1024M。超过最大 size 的 chunk 依然能够提供读写服务,只是不会再进行分裂和迁移,长久以往会造成集群之间数据的不平衡.


唯一键问题:


MongoDB 集群的唯一键设置增加了限制,必须是包含分片键的;如果_id 不是分片键,_id 索引只能保证单个 shard 上的唯一性。


  • You cannot specify a unique constraint on a hashed index

  • For a to-be-sharded collection, you cannot shard the collection if the collection has other unique indexes

  • For an already-sharded collection, you cannot create unique indexes on other fields


因此我们删除了数据和集合,调整    topicId 和 _id 为联合分片键 重新创建了集合。这样即打破了 chunk size 的限制,也解决了唯一性问题。


3.4 迁移和扩容

随着数据的写入,当单个 chunk 中数据大小超过指定大小时(或 chunk 中的文件数量超过指定值)。MongoDB 集群会在插入或更新时,自动触发 chunk 的拆分。


拆分会导致集合中的数据块分布不均匀,在这种情况下,MongoDB balancer 组件会触发集群之间的数据块迁移。balancer 组件是一个管理数据迁移的后台进程,如果各个 shard 分片之间的 chunk 数差异超过阈值,balancer 会进行自动的数据迁移。



balancer 是可以在线对数据迁移的,但是迁移的过程中对于集群的负载会有较大影响。一般建议可以通过如下设置,在业务低峰时进行(更多见官网


db.settings.update({ _id: "balancer" },{ $set: { activeWindow : { start : "<start-time>", stop : "<stop-time>" } } },{ upsert: true })
复制代码


MongoDB 的扩容也非常简单,只需要准备好新的 shard 复制集后,在 Mongos 节点中执行:



sh.addShard("<replica_set>/<hostname><:port>")
复制代码

扩容期间因为 chunk 的迁移,同样会导致集群可用性降低,因此只能在业务低峰进行

四、写在最后


MongoDB 集群在评论中台项目中已上线运行了一年多,过程中完成了约 10 个业务方接入,承载了 1 亿+评论回复数据的存储,表现较为稳定。BSON 非结构化的数据,也支撑了我们多个版本业务的快速升级。而热门数据内存化存储引擎,较大的提高了数据读取的效率。


但对于 MongoDB 来说,集群化部署是一个不可逆的过程,集群化后也带来了索引,分片策略等较多的限制。因此一般业务在使用 MongoDB 时,副本集方式就能支撑 TB 级别的存储和查询,并非一定需要使用集群化方式。


以上内容基于 MongoDB 4.0.9 版本特性,和最新版本的 MongoDB 细节上略有差异。


参考资料:https://docs.mongodb.com/manual/introduction/


作者:vivo 官网商城开发团队

发布于: 2021 年 03 月 01 日阅读数: 42
用户头像

官方公众号:vivo互联网技术,ID:vivoVMIC 2020.07.10 加入

分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。

评论 (1 条评论)

发布
用户头像
mongo集群引入 【逻辑集群】和【物理集群】的概念, 就可以对不同的业务进行差异化调优, 很棒的思路!
2021 年 03 月 19 日 14:10
回复
没有更多了
MongoDB 在评论中台的实践