MongoDB
MongoDB资料整理
定义
MongoDB是介于关系数据库和非关系数据库之间的一种高性能、开源、无模式的文档型数据库,使用C++开发。相关介绍如下:
内部构造
BSON
在MongoDB中,文档是对数据的抽象,它被使用在Client端和Server端的交互中。所有的Client端(各种语言的Driver)都会使用这种抽象,它的表现形式就是我们常说的BSON(Binary JSON )。
使用BSON格式出于以下3种目的:
BSON是为效率而设计的,它只需要使用很少的空间。即使在最坏的情况下,BSON格式也比JSON格式再最好的情况下存储效率高。- 在某些情况下,
BSON会牺牲额外的空间让数据的传输更加方便。比如,字符串的传输的前缀会标识字符串的长度,而不是在字符串的末尾打上结束的标记。这样的传输形式有利于MongoDB修改传输的数据。 BSON格式的编码和解码都是非常快速的。它使用了C风格的数据表现形式,这样在各种语言中都可以高效地使用。
名字空间和盘区
每一个数据库都由多个名字空间组成,每一个名字空间存储了相应类型的数据。数据库中的每一个Collection都有各自对应的名字空间,索引文件同样也有名字空间。所有名字空间的元数据都存储在.ns文件中。
名字空间中的数据在磁盘中分为多个区间,这个叫做盘区。
内存映射存储引擎
MongoDB目前支持的存储引擎为内存映射引擎。当MongoDB启动的时候,会将所有的数据文件映射到内存中,然后操作系统会托管所有的磁盘操作。MMAP的只是将文件映射到进程空间,而不是直接全部map到物理内存,只有访问到这块数据时才会被操作系统以Page的方式换到物理内存。这部分的管理工作由操作系统完成,对于MongoDB的开发者而言,也是透明的.其实我们所能用的所有函数,包括系统内核里的实现函数,操作的统统都是虚拟内存,也就是每个进程所谓的4GB(32位系统)的虚拟地址空间.物理内存对于用户是不可见的,不可操作的。这也就是为什么MongoDB可以存储比内存更大的数据,但是却不建议热数据超过内存大小的原因。因为热数据大于内存的话,操作系统需要频繁的换入换出物理内存中的数据,会严重影响MongoDB的性能。
MongoDB特点
MongoDB 是一个面向集合的,模式自由的文档型数据库.
- 面向集合, 意思是数据被分组到若干集合,这些集合称作聚集(
collections)。 - 模式自由, 意思是数据库并不需要知道你将存入到聚集中的文档的任何结构信息.实际上,你可以在同一个聚集中存储不同结构的文档。
- 文档型, 意思是我们存储的数据是键-值对的集合,键是字符串,值可以是数据类型集合里的任意类型,包括数组和文档.
- 动态查询,MongoDB 支持丰富的查询表达式。查询指令使用
JSON形式的标记,可轻易查询文档中内嵌的对象及数组 - 完整的索引支持:包括文档内嵌对象及数组。
MongoDB的查询优化器会分析查询表达式,并生成一个高效的查询计划 - 查询监视, MongoDB 包含一系列监视工具用于分析数据库操作的性能
- 复制及自动故障转移,
MongoDB数据库支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制。复制的主要目标是提供冗余及自动故障转移 - 高效的传统存储方式,支持二进制数据及大型对象(如照片或图片)
- 自动分片以支持云级别的伸缩性,自动分片功能
Auto-Sharding支持水平的数据库集群,可动态添加额外的机器
MongoDB局限性与不足
- 锁粒度太粗,
MongoDB在2.2版本以前用的是全局锁,2.2版本后面,使用的是库锁,粒度有所下降 - 不支持
join操作和事务机制,这个确实是非MongoDB要做的领域 - 对内存要求比较大,至少要保证热数据(索引,数据及系统其它开销)都能装进内存
- 用户权限方面比较弱,这一点
MongoDB官方推荐的是将机器部署在安全的内网环境中,尽量不要用权限 MapReduce在单个实例上无法并行,只有采用Auto-Sharding才能并行。这是由JS引擎的限制造成的MapReduce的结果无法写入到一个被Sharding的Collection中,对这个问题的解决也不彻底Auto-Sharding还存在很多问题,所谓的水平扩展也不是那么理想
适用场景
- 网站数据:
MongoDB非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性 - 缓存:由于性能很高,
MongoDB也适合作为信息基础设施的缓存层。在系统重启之后,由MongoDB搭建的持久化缓存层可以避免下层的数据源过载 - 大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储
- 高伸缩性的场景:
MongoDB非常适合由数十或数百台服务器组成的数据库。MongoDB的路线图中已经包含对MapReduce引擎的内置支持 - 用于对象及
JSON数据的存储:MongoDB的BSON数据格式非常适合文档化格式的存储及查询
与关系型数据库比较
| RDBMS | MongoDB |
|---|---|
| Table(表) | Collection(集合) |
| Column(栏) | Key(键) |
| Value(值) | Value(值) |
| Records / Rows(记录/列) | Document / Object(文档/对象) |
优点
- 弱一致性(最终一致),更能保证用户的访问速度
在传统的关系型数据库中,一个COUNT类型的操作会锁定数据集,这样可以保证得到“当前”情况下的精确值。这在某些情况下,例 如通过ATM查看账户信息的时候很重要 - 文档结构的存储方式,能够更便捷的获取数据
- 内置
GridFS,支持大容量的存储GridFS是一个出色的分布式文件系统,可以支持海量的数据存储。
内置了GridFS了MongoDB,能够满足对大数据集的快速范围查询。 - 内置
Sharding
提供基于Range的Auto Sharding机制:一个collection可按照记录的范围,分成若干个段,切分到不同的Shard上。 - 第三方支持丰富。(这是与其他的
NoSQL相比,MongoDB也具有的优势)
现在网络上的很多NoSQL开源数据库完全属于社区型的,没有官方支持,给使用者带来了很大的风险。
而开源文档数据库MongoDB背后有商业公司10gen为其提供供商业培训和支持。
而且MongoDB社区非常活跃,很多开发框架都迅速提供了对MongDB的支持。 - 性能优越
在使用场合下,千万级别的文档对象,近10G的数据,对有索引的ID的查询不会比mysql慢,而对非索引字段的查询,则是全面胜出。mysql实际无法胜任大数据量下任意字段的查询,而mongodb的查询性能实在让我惊讶。写入性能同样很令人满意,同样写入百万级别的数 据,mongodb比我以前试用过的couchdb要快得多,基本10分钟以下可以解决。缺点
mongodb不支持事务操作
所以事务要求严格的系统(如果银行系统)肯定不能用它。(这点和优点①是对应的)mongodb占用空间过大
关于其原因,在官方的FAQ中,提到有如下几个方面:
1、空间的预分配:为避免形成过多的硬盘碎片,mongodb每次空间不足时都会申请生成一大块的硬盘空间,而且申请的量从64M、128M、256M那 样的指数递增,直到2G为单个文件的最大体积。随着数据量的增加,你可以在其数据目录里看到这些整块生成容量不断递增的文件。
2、字段名所占用的空间:为了保持每个记录内的结构信息用于查询mongodb需要把每个字段的key-value都以BSON的形式存储,如果value域相对于key域并不大,比如存放数值型的数据,则数据的overhead是最大的。一种减少空间占用的方法是把字段名尽量取短一些,这样占用 空间就小了,但这就要求在易读性与空间占用上作为权衡了。我曾建议作者把字段名作个index,每个字段名用一个字节表示,这样就不用担心字段名取多长 了。但作者的担忧也不无道理,这种索引方式需要每次查询得到结果后把索引值跟原值作一个替换,再发送到客户端,这个替换也是挺耗费时间的。现在的实现算是 拿空间来换取时间吧。
3、删除记录不释放空间:这很容易理解,为避免记录删除后的数据的大规模挪动,原记录空间不删除,只标记“已删除”即可,以后还可以重复利用。
4、可以定期运行db.repairDatabase()来整理记录,但这个过程会比较缓慢MongoDB没有如MySQL那样成熟的维护工具,这对于开发和IT运营都是个值得注意的地方
我们的游戏数据迁移
根据我们现在的游戏数据库的设计,有以下两种方式替代。
替换方案
MongoDB替换MySQL
只需要在MongoDB新建与MySQL里面的表相对应的collections即可。MongoDB替换MySQL和Redis
因为MongoDB属于关系数据库和NoSQL的中间品,所以同样可以用它来替换我们正在使用的Redis关系数据库。
Linux下PHP访问
由于修改数据库后,只需要相应的修改PHP后台这边的访问代码,前端那边可以不做变化,Lua那边如果需要访问数据库,同样也要修改相应的访问代码。这里介绍PHP下的访问
- 安装相关驱动
mongo-php-driver - 写代码测试
//连接到mongodb
$m = new MongoClient();
//选择一个数据库,不存在则创建
$db = $m -> heroLegend;
//创建一个集合collection
$collection = $db -> createCollection("gameUsers");
//插入一条文档记录document
$document = array("id"=>"1",
"architecture"=>"dfdfdf"
);
$collection -> insert($document);
//查看
$cursor = $collection->find();
//更新记录
$collection->update(array("id"=>"1"), array('$set'=>array("architecture"=>"MongoDB")));
//删除记录
$collection->remove(array("id"=>"1"),false);
除了以上实例外,在php中你还可以使用findOne(), save(), limit(), skip(), sort()等方法来操作Mongodb数据库。

浙公网安备 33010602011771号