使用情景

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

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散列的工作。

MongoDB的真正性能-实战百万用户一-一亿的道具的更多相关文章

  1. MongoDB的真正性能-实战百万用户

    阅读目录 一.第一个问题:Key-Value数据库可以有好多的Key,没错,但对MongoDB来说,大错特错 二.第二个问题:FindOne({_id:xxx})就快么? 三.第三个问题:精细的使用U ...

  2. 百万用户时尚分享网站feed系统扩展实践

    Fashiolista是一个在线的时尚交流网站,用户可以在上面建立自己的档案,和他人分享自己的以及在浏览网页时看到的时尚物品.目前,Fashiolista的用户来自于全球100多个国家,用户达百万级, ...

  3. [转载]MongoDB的真正性能

    最近开始研究MySQL和MongoDB,发现这方面资料不多.尤其是真正的说到点子上的文章,太少了. 有一些对比测试的文章基本上都是瞎测,测试方法都测到了马腿上,得出的结论基本上都是NoSQL毫无价值 ...

  4. Jmeter实时监控+SpringBoot接口性能实战

    性能测试 Jmeter实时监控+SpringBoot接口性能实战 自动化 SpringBoot Java Jmeter实时监控+SpringBoot接口性能实战 一.实验目的及实验环境 1.1.实验目 ...

  5. 我用爬虫一天时间“偷了”知乎一百万用户,只为证明PHP是世界上最好的语言

    我用爬虫一天时间“偷了”知乎一百万用户,只为证明PHP是世界上最好的语言 2015-08-06 猿圈 我用爬虫一天时间“偷了”知乎一百万用户 只为证明PHP是世界上最好的语言 看了不少朋友圈里推荐的P ...

  6. jmeter接口测试实战-创建用户

    jmeter接口测试实战-创建用户 相信大多数看到标题的同学都会有疑问, 创建用户不是很简单吗, 调用一下创建用户接口, 传入指定入参, 用户即可创建成功, 今天我们的实战来讲讲创建场景.通过接口创建 ...

  7. mongodb怎么创建数据库和配置用户

    mongodb怎么创建数据库和配置用户,远程连接是不够的,还要上去操作和放数据的. 系统:centos 5.x 环境:mongodb 1.安装mongodb 这步就不说了,大家自己去看Centos安装 ...

  8. 项目方说性能达到百万TPS,如何测试它的可信度?

    项目方说性能达到百万TPS,如何测试它的可信度? 应用系统性能提升的关键在于运维端的接入管理模型(AAA,认证 Authentication.授权 Authorization.计费 Accountin ...

  9. mongodb集群性能优化

    mongodb集群性能优化 在前面两篇文章,我们介绍了如何去搭建mongodb集群,这篇文章我们将介绍如何去优化mongodb的各项配置,以达到最优的效果. 警告 不做任何的优化,集群搭建完成之后,使 ...

随机推荐

  1. Eclipse中web项目部署至Tomcat【转】

    Eclipse的web工程至Tomcat默认的部署目录是在工程空间下,本文旨在将部署目录改为Tomcat安装目录,并解决依赖包输出问题. 1.在Eclipse中添加Tomcat服务器.  2.将web ...

  2. centos环境自动化批量安装软件脚本

    自动化安装jdk软件部署脚本 准备工作: 1.在执行脚本的服务器上生成免密码公钥: 安装expect命令 yum install -y expect ssh-keygen 三次回车 2.将jdk-7u ...

  3. Nginx 和 PHP的安装配置

    1.安装PHP(注意系统默认安装了php,要安装高版本之前最好卸载旧版本,因为这个原因纠结了四个小时) ./configure \ --prefix=/usr/local/php \ --exec-p ...

  4. mvc导出EXCEL

    /// <summary> /// 导出EXCEL /// </summary> /// <returns></returns> public Acti ...

  5. Cocos学习-----Cocos2Dx安装

    昨天晚上从官网下载了cocos2dx,打开发现win下工程已经是2010了最少,很是郁闷.尝试着在08下新建工程然后添加文件,发现还是很麻烦,于是下载了vs2010,打开编译报错:error C206 ...

  6. c++接口实现与分离(转载)

    原文地址:http://www.360doc.com/content/13/0218/10/8363527_266294787.shtml 良好的设计应该只暴露接口给用户,所有的实现细节对用户来说应该 ...

  7. jsp js action之间的传值

    1.struts2 action如何向JSP的JS函数传值 action中定义变量public class TestAction extends ActionSupport implements Se ...

  8. SQL Server合并版本

    1) 更新表(另一张表) a)        写法轻松,更新效率高: update table1 set field1=table2.field1,field2=table2.field2 from ...

  9. linux服务器加硬盘扩容

    from: http://bbs.chinaunix.net/thread-3613556-1-1.html 试验环境: vmware下,centos6,64位版本,原来系统默认分区,/dev/sda ...

  10. Markdown syntax guide and writing on MWeb

    Philosophy Markdown is intended to be as easy-to-read and easy-to-write as is feasible.Readability, ...