MongoDB的真正性能-实战百万用户一:一亿的道具

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介:

使用情景

开始之前,我们先设定这样一个情景:

1.一百万注册用户的页游或者手游,这是不温不火的一个状态,刚好是数据量不上不下的一个情况。也刚好是传统MySql数据库性能开始吃紧的时候。

2.数据库就用一台很普通的服务器,只有一台。读写分离、水平扩展、内存缓存都不谈。一百万注册用户如果贡献度和活跃度都不高,恐怕公司的日子还不是那么宽裕,能够在数据库上的投资也有限。

以此情景为例,设每个用户都拥有100个道具,用户随时会获得或失去道具。

我们就来看看这一亿的道具怎么搞。

道具一般要使用原型、实例的设计方法,这个不属于数据库的范畴。

道具类型001 是屠龙刀,屠龙刀价格1500,基础攻击150,这些,我们把它们称为道具原型,保存在原型数据文件中。

这个原型数据文件,无论是存在何种数据库或者本地文件中,对服务器来说都不是问题,也不干扰数据库设计,所以我们不去讨论他。

关系数据库设计方法

典型的关系数据库设计方法:

用户表:字段 xxx userid xxx   ,记录数量100万

xxx是其他字段,userid标示用户

用户道具表:字段 xxx userid itemtype xxx ,记录数量一亿

xxx是其他字段,userid 标示

一个亿的记录数是不是看起来有点头疼,mysql这个时候就要想各种办法了。

MongoDB设计方法

但我们用mongoDB来实现这个需求,直接就没有问题

首先第一个集合:users集合,用UserName 作为_id ,记录数100万

然后道具的组织,我们有两种选择

1.在users集合的值中建立Items对象,用Bson数组保存道具(Mongo官方称为Bson,和Json一模一样的存储方法)

方法一,没有额外的记录数

2.新建userItems集合,同样用UserName作为_id 每个UserItems集合的值中建立一个Item对象,使用一个Bson数组来保存道具

方法二,多了一个集合和100万记录数

 

我们的道具数据看起来像下面这样:

{_id:xxx,Items:[

{Itemtype:xxx,ItemPower:xxx},

...

...

...

 

]}

测试方法

测试方法如下:测试客户端随机检查一个用户的道具数量,小于100加一个道具,大于100 删除一个道具。

连续100万次,采用10个线程并发。

如果用关系数据库设计方法+mysql来实现,这是一个很压力很大的数据处理需求。

可是用文档数据库设计方法+MongoDB来实现,这个测试根本算不上有压力。

注意事项

即使我们用了一个如此胜之不武的设计方式,你依然有可能还是能把他写的很慢。

因为MongoDB在接口设计上并没有很好的引导和约束,如果你不注意,你还是能把他用的非常慢。

第一个问题:Key-Value数据库可以有好多的Key,没错,但对MongoDB来说,大错特错

MongoDB的索引代价很大,大到什么程度:

1.巨大的内存占用,100万条索引约占50M内存,如果这个设计中,你一个道具一条记录,5G内存将用于索引。

我们的屌丝情景不可能给你这样的服务器,

2.巨大的性能损失,作为一个数据库,所有的东西终将被写入硬盘,没有关系数据库那样的表结构,MongoDB的索引写入性能看起来很差,如果记录数据较小的时候,你可以观测到这样震撼的景象,加一个索引,性能变成了1/2,加两个索引,性能变成了1/3。

只有当第二个索引的查询不可避免,才值得增加额外索引。因为没索引的数据,查询性能是加几个零的慢,比加索引更惨。

我们既然选择了Key-Value数据库,应尽量避免需要多个索引的情况。

所有的索引只能存在于内存中,而读取记录时,也需要将Bson在内存中处理,内存还承担着更重要的作用:读取缓存。

本来就不充裕的内存,应该严格控制我们的记录条数,能够用Bson存储的,尽量用之。

那么我们之前在MongoDB的设计中怎么还考虑第二种设计方法呢?独立一个userItems 集合,不是又多出100万条记录了吗?

这基于另两个考虑:a.Bson的处理是要反复硬盘和内存交换的,如果每条记录更小,则IO压力更小。内存和硬盘对服务器来说都是稀缺资源,至于多大的数据拆分到另一个集合中更划算,这需要根据业务情况,服务器内存、硬盘情况来测试出一个合适大小,我们暂时使用1024这个数值,单用户的道具表肯定是会突破1024字节的,所以我们要考虑将他独立到一个集合中

b.可以不部署分片集群,将另一个集合挪到另一个服务器上去。只要服务器可以轻松承载100万用户,200万还会远么?在有钱部署分片集群以前,考虑第二组服务器更现实一些。

第二个问题:FindOne({_id:xxx})就快么?

毋庸置疑,FindOne({_id:xxx})就是最直接的用Key取Value。

也的确,用Key取Value 就是我们能用的唯一访问Value的方式,其他就不叫Key-Value数据库了。

但是,由于我们要控制Key的数量,单个Value就会比较大。

不要被FindOne({_id:xxx}).Items[3].ItemType这优雅的代码欺骗,这是非常慢的,他几乎谋杀你所有的流量。

无论后面是什么 FindOne({_id:xxx})总是返回给你完整的Value,我们的100条道具,少说也有6~8K.

这样的查询流量已经很大了,如果你采用MongoDB方案一设计,你的单个Value是包含一个用户的所有数据的,他会更大。

如果查询客户端和数据库服务器不在同一个机房,流量将成为一个很大的瓶颈。

我们应该使用的查询函数是FindOne({_id:xxx},filter),filter里面就是设置返回的过滤条件,这会在发送给你以前就过滤掉

比如FindOne({_id:xxx},{Items:{"$slice":[3,1]}}),这和上面那条优雅的代码是完成同样功能,但是他消耗很少的流量

第三个问题:精细的使用Update

这和问题二相对的,不要暴力的FindOne,也尽量不要暴力的Update一整个节点。虽然MangoDB的性能挺暴力的,IO性能极限约等于MongoDB性能,暴力的Update就会在占用流量的同时迎接IO的性能极限。

除了创建节点时的Insert或者Save之外,所有的Update都应该使用修改器精细修改.

比如Update({_id:xxx},{$set:{"Items.3.Item.Health":38}});//修改第三把武器的健康值

至于一次修改和批量修改,MongoDB默认100ms flush一次(2.x),只要两次修改比较贴近,被一起保存的可能性很高。

但是合并了肯定比不合并强,合并的修改肯定是一起保存,这个也要依赖于是用的开发方式,如果使用php做数据客户端,缓存起来多次操作合并了一起提交,实现起来就比较复杂。

 

注意以上三点,一百万注册用户并不算很多,4G内存,200G硬盘空间的MongoDB服务器即可轻松应对。性能瓶颈是硬盘IO,可以很容易的使用Raid和固态硬盘提升几倍的吞吐量。不使用大量的Js计算,CPU不会成为问题,不要让索引膨胀,内存不会成为问题。你根本用不着志强的一堆核心和海量的内存,更多的内存可以让缓存的效果更好一些,可是比读写分离还是差远了。如果是高并发时查询性能不足,就要采用读写分离的部署方式。当IO再次成为瓶颈时,就只能采用集群部署MongoDB启用分片功能,或者自行进行分集合与key散列的工作。

 原文发布时间为:2013-05-10

本文来自云栖社区合作伙伴“Linux中国”

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
30天前
|
存储 NoSQL API
微服务——MongoDB实战演练——需求分析
本文档《5-MongoDB实战演练》聚焦于某头条文章评论业务的需求分析与功能实现。基于MongoDB,需完成以下功能:1)提供基本的增删改查API;2)支持通过文章ID查询相关评论;3)实现评论点赞功能。结合实际业务场景,演示MongoDB在数据存储与操作中的应用,附带示意图帮助理解业务结构。
24 2
微服务——MongoDB实战演练——需求分析
|
30天前
|
NoSQL MongoDB 微服务
微服务——MongoDB实战演练——文章评论的基本增删改查
本节介绍了文章评论的基本增删改查功能实现。首先,在`cn.itcast.article.dao`包下创建数据访问接口`CommentRepository`,继承`MongoRepository`以支持MongoDB操作。接着,在`cn.itcast.article.service`包下创建业务逻辑类`CommentService`,通过注入`CommentRepository`实现保存、更新、删除及查询评论的功能。最后,新建Junit测试类`CommentServiceTest`,对保存和查询功能进行测试,并展示测试结果截图,验证功能的正确性。
35 2
|
30天前
|
NoSQL Java MongoDB
微服务——MongoDB实战演练——文章评论实体类的编写
本节主要介绍文章评论实体类的编写,创建了包`cn.itcast.article.po`用于存放实体类。具体实现中,`Comment`类通过`@Document`注解映射到MongoDB的`comment`集合,包含主键、内容、发布时间、用户ID、昵称等属性,并通过`@Indexed`和`@CompoundIndex`注解添加单字段及复合索引,以提升查询效率。同时提供了Mongo命令示例,便于理解和操作。
34 2
|
30天前
|
NoSQL 测试技术 MongoDB
微服务——MongoDB实战演练——MongoTemplate实现评论点赞
本节介绍如何使用MongoTemplate实现评论点赞功能。传统方法通过查询整个文档并更新所有字段,效率较低。为优化性能,采用MongoTemplate对特定字段直接操作。代码中展示了如何利用`Query`和`Update`对象构建更新逻辑,通过`update.inc("likenum")`实现点赞数递增。测试用例验证了功能的正确性,确保点赞数成功加1。
26 0
|
30天前
|
NoSQL 测试技术 MongoDB
微服务——MongoDB实战演练——根据上级ID查询文章评论的分页列表
本节介绍如何根据上级ID查询文章评论的分页列表,主要包括以下内容:(1)在CommentRepository中新增`findByParentid`方法,用于按父ID查询子评论分页列表;(2)在CommentService中新增`findCommentListPageByParentid`方法,封装分页逻辑;(3)提供JUnit测试用例,验证功能正确性;(4)使用Compass插入测试数据并执行测试,展示查询结果。通过这些步骤,实现对评论的高效分页查询。
32 0
|
30天前
|
NoSQL MongoDB 微服务
微服务——MongoDB实战演练——文章微服务模块搭建
本节介绍文章微服务模块的搭建过程,主要包括以下步骤:(1)创建项目工程 *article*,并在 *pom.xml* 中引入依赖;(2)配置 *application.yml* 文件;(3)创建启动类 *cn.itcast.article.ArticleApplication*;(4)启动项目,确保控制台无错误提示。通过以上步骤,完成文章微服务模块的基础构建与验证。
26 0
|
1月前
|
NoSQL Java 测试技术
MongoDB实战演练
本文介绍了基于Spring Boot和MongoDB实现文章评论功能的完整流程。主要包括需求分析、表结构设计、技术选型(如mongodb-driver与SpringDataMongoDB)、项目搭建及配置、实体类编写、基本增删改查功能实现、分页查询以及点赞功能的开发。通过Comment实体类、CommentRepository接口和CommentService服务层,实现了评论的存储、查询及更新操作,并利用MongoTemplate优化了点赞功能的性能。最后通过JUnit测试验证各功能的正确性。该方案适合需要高效处理非结构化数据的文章评论系统开发。
MongoDB实战演练
|
30天前
|
NoSQL MongoDB 数据库
微服务——MongoDB实战演练——表结构分析
本文档来源于数据库articledb,展示了一张图片资源。图片宽度为1207像素,高度607像素,采用内联显示方式。内容涉及图像处理与样式设定,适用于文档或网页设计中多媒体元素的布局参考。图片来源为cdn.nlark.com,支持webp格式并附带水印处理。
28 1
微服务——MongoDB实战演练——表结构分析
|
30天前
|
NoSQL Java 数据库连接
微服务——MongoDB实战演练——技术选型
本节主要介绍技术选型中的两个重要工具:**mongodb-driver** 和 **SpringDataMongoDB**。其中,mongodb-driver 是 MongoDB 官方提供的 Java 驱动包,用于连接和操作 MongoDB 数据库,功能类似 JDBC 驱动。通过官方示例可快速上手。而 SpringDataMongoDB 是 Spring 生态的一员,封装了 mongodb-driver,提供了更简洁的 API,方便开发者在 Spring 环境中操作 MongoDB。两者各有优势,可根据实际需求选择合适的技术方案。
32 2
|
23天前
|
NoSQL MongoDB 数据库
【直播回放】MongoDB全球开发者认证介绍线上直播 助力您掌握企业级实战能力
想通过MongoDB认证提升竞争力却无从下手?这场线上直播为你解惑!权威解读考试大纲、题型与评分标准,资深专家分享备考策略,涵盖学习计划、实战技巧及心理调整。更有最新认证激励政策、专属徽章与大礼包等你解锁!无论你是开发者、管理员还是学生,都能为职业发展铺路。立即预约3月26日直播回放,与MongoDB专家互动答疑,轻松迈向专业高峰!