常见问题解答:MongoDB原理

常见问题解答 MongoDB原理,翻译基于mongoDB 官方文档 http://docs.mongodb.org/manual/faq/fundamentals/

本文档主要介绍mongodb使用的基础上层问题以及使用的概略。 如果在本文中没有找到你关心的问题解答,mongoDB官方建议你去mail list提问 ,或者阅读这份更完整的FAQ

 

MongoDB是什么类型的数据库?

 

MongoDB是面向文档的数据库管理系统DBMS(显然mongodb不是oracle那样的RDBMS,而仅仅是DBMS)。 想想一下MySQL中没有任何关系型数据库的表,而由JSON类型的对象组成数据模型的样子会师如何的?

 

值得注意的是,MongoDB既不支持JOIN(连接)也不支持transaction(事务)。Significantly, MongoDB supports neither joins nor transactions.

 

但是请注意MongDB有着大量其他优良的特性,如二级索引、功能丰富的查询语言以及对每一个单个文档文件的原子写保证以及完全一致性的读取。

此外,例如master-salve replication 自动故障切换的主从复制技术和通过自动的基于范围的分区来实现的内建的横向扩展。

mongodb的document基于BSON格式,BSON是一种二进制对象格式,内容形式上类似于JSON。

 

Mongodb数据库中有table表的概念吗?

 

mongodb中存放数据的集合中没有table,但有很类似于table和column的概念; mongodb中将数据均存放在collections中,collections很类似于RDBMS关系型数据库中的table表。  一个collections存放有一个或多个document,document对应于RDBMS中的一个记录或者一行数据,每一个文档均有一个或者多个field字段,其对应于关系型数据库中的column。

collection与关系型数据库中的表有着几个重大的区别:

一个collection中的每一个document可能都有着不同的field或不同的field顺序,例如:

对于关系型数据库而言 emp表(empno int,empname varchar2(200)),则emp表的每一行数据都是如下的形式  :

 

empno empname 
10    "mike"
20    "tom"
30    "maclean"

如果上述数据有违反emp表结构设计的数据,例如希望多存一个字段salary都是非法的,RDBMS将不允许这类非法操作被执行,会直接报错。

 

而对于MongoDB数据库而言,并不在意固定的模式设计,collection中可以存放field顺序、结构完全不同的数据:

 

> db.dbdao_t1.insert({empno:10,enpname:"maclean",salary:90000});
WriteResult({ "nInserted" : 1 })
> db.dbdao_t1.insert({itemno:20,itemname:"box"});
WriteResult({ "nInserted" : 1 })
> 
> db.dbdao_t1.insert({exam_id:"1z0-043",score:100});
WriteResult({ "nInserted" : 1 })
> db.dbdao_t1.find();
{ "_id" : ObjectId("554f065b92269e7fc2fa83f0"), "empno" : 10, "enpname" : "maclean", "salary" : 90000 }
{ "_id" : ObjectId("554f067092269e7fc2fa83f1"), "itemno" : 20, "itemname" : "box" }
{ "_id" : ObjectId("554f068792269e7fc2fa83f2"), "exam_id" : "1z0-043", "score" : 100 }


以上插入了3行结构完全不同的数据到同一个collection中。MongoDB这样的设计让开发变得十分迅速,因为对于MongoDB的适应场景往往是那些需要快速实现系统的互联网或移动应用程序,其要求开发速度尽可能快,使用MongoDB由于其动态模式的特性,开发人员无法花初始时间在考虑数据结构上;而且后续修改现有的数据结构也异常简单,对于传统的RDBMS而言如果你要给表加字段或做修改字段,都可能需要一定的维护窗口时间;试想一下你在2015年开发一个热门概念的Web项目,对于项目本身而言尽可能快地实现功能和让用户进驻可能是你比别人早获得风险投资的重要一环,在这个前提下代码的健壮性、性能并发等因素均会靠边站。MongoDB的产生正是基于这个大投资环境。

 

MongoDB数据库中有模式Schema吗?

 

MongoDB中采用动态模式(Dynamic Schema)。用户可以随意创建collection,而不需要定义结构。collections中的每一个document不需要一定是同样的field顺序、个数、类型、名称。实际上就是不存在什么结构而言,你想插入到collection中的document可以是任何形式的field顺序、个数、类型、名称,不存在RDBMS中的alter table的需求和功能。

在mongoDB实践中,一个collection中的document文件其中大量存放的数据应当是同质的,显然不该将员工数据和商品数据放在同一个collection里。当然mongodb没有硬性要求这一点。MongoDB的灵活数据模式意味着模式的迁移和扩展在实践中都非常容易。这些都是为了更敏捷地基于MongoDB开发应用而存在。

 

使用何种语言与mongodb交互?

 

几乎对应所有的主流编程语言,Mongodb都有了对应的客户端驱动。具体可以参考这个语言和驱动列表。

 

MongoDB支持SQL语言吗?

 

不支持

但是MongoDB有一大堆自有的丰富的动态查询语言

 

MongDB的经典适用场景是那些?

 

由于mongodb的定位是为了通用场景设计的数据库,所以其适合于大多数的用户场景。例如内容管理系统content management systems,移动应用mobile applications,游戏gaming,电子商务
e-commerce,数据分析analytics,数据归档和日志记录等

不要把MongoDB使用在必须要用到sql,Join或者多对象事务的场景里

 

MongoDB支持ACID吗?

 

首先明确的是MongoDB 不支持多文档事务multi-document transactions.

但是,MongoDB提供对单个文件的原子操作。10gen认为这种文件级别document-level的原子操作已经足够满足解决那些在关系型数据库里要求事务ACID才能解决的问题了。

举个例子来说在MongoDB,用户可以将数据嵌入到一个document的嵌套队列或嵌套文档中,并通过一个简单的原子操作来更新整个文档。而关系型数据库中需要多个表的多个行数据才能表达如mongodb中一个document的数据,这就需要支持事务以便保证更新数据是原子操作。

MongoDB允许客户端读取被刚插入或刚更新而没有被提交commit的文档document,无论当时是write concern还是journaling配置。 结果是应用程序将观察到2种现象:

  1. 对于有多并发读写的系统,MongoDB将允许客户端读取到那些写操作还没有彻底返回的写操作的结果
  2. 若Mongd在写操作对应的日志提交前就异常终止,甚至当写返回成功了,在mongd重启后查询可能找不到该写操作

其他数据库当中将这种隔离语义 叫做 read uncommited 读未提交。 对于所有的插入和更新,MongDB以以下级别的隔离性修改每一个document文档:客户端永远不会看到单个文档被修改的中间状态, 也就是说客户端要么看到一个文档修改前的样子,要么看到一个文档修改后的样子,客户端不会看到一个文档被修改了一半的样子,例如 { _id:1,empno:20, empname:”maclean”} ,这个document被update为{_id:1, empno:30, empname:”macleanliu”},客户端要么看到前者 要么看到后者,而不会读取到{_id:1, empno:30, empname:”maclean”} 。

但是对于多文档的操作multi-document  operations,MongoDB并不提供任何多个文档的隔离性或者事务。

当一个独立的mongod实例返回了一个成功记录的写关注write concern,这意味着数据被完全提交到磁盘上,即便mongod进程意外终止了,下次重启时也能保证这些数据确实可以被找回。

对于一个复制集replica set而言,写操作仅仅在复制写操作并提交到位于主复制集成员的journal日志中后才是持久的。MongoDB定期提交commit数据到journal日志无论是否是写关注的journaled write concern, 参数commitIntervalMs 可以用来控制mongod多久提交一次。

 

MongoDB会需要使用大量内存吗?

 

mongodb并不需要大量的内存。 mongodb是可以运行在内存并不大的机器上的。

MongoDB自动使用服务器上的剩余空闲内存作为其缓存。系统资源监控可能显示MongoDB使用大量内存, 但其使用是动态的。 若其他应用进程需要大量的服务器内存,MongoDB会退让内存给其他进程。

 

从底层技术上来说,操作系统虚拟内存子系统管理MongoDB的内存。这意味着MongoDB会尽可能使用其所能使用的空闲内存,必要的时候也会使用swap。 当然在内存够多的情况下, MongoDB可以获得更佳的性能。

 

我如何为MMAPv1 配置缓存大小

mongoDB中mmapv1的内存配置是自动搞定的,用户无法调整。mongodb使用内存映射文件来尽可能使用所有空闲内存。就像操作系统利用文件系统内存那样。

对于MongoDB 3.0以后引入的WiredTiger引擎,用户可以配置一些内存参数,包括storage.wiredTiger.engineConfig.cacheSizeGB –wiredTigerCacheSizeGB。

 

MongoDB需要为应用级缓存配置独立的缓存层吗?

 

不需要,在MongoDB数据库中一个文档的表现类与其在应用程序内存中的表现差不多。这意味着数据库中存放的数据形式不仅仅在磁盘的持久层中是有效的,其对应的在应用程序缓存中也是有效的。这也就让在应用程序中搞一个独立的缓存层变得不必要了。

这与关系型数据库不同,关系型数据库中缓存数据的成本较高。关系型数据库首先要将数据转换为对象的表现形式,之后应用程序才能读取并可以将已转换的数据放到独立缓存中。若这些转换数据要求先做一些JOIN操作,则此过程增加了额外的成本;这让关系型数据库中的缓存层变得更重要。

 

MongoDB支持缓存吗?

是的, MongoDB 尽可能将最近使用过的数据缓存起来。 若用户为查询创建了索引且工作数据集合在内存里放得下,那么MongoDB 尽可能让所有查询基于内存走。

但MongoDB目前不支持查询缓存(缓存查询结果),MongoDB所有的查询均基于数据文件中的collections或index索引。

 

MongoDB中写磁盘是立即的,还是懒惰的?

默认情况下写操作会每100毫秒被记录到journal 日志。基于此,只要写操作已经被同步到journal种了, 即便你把MongoDB所在服务器的电源拔了,数据还是不会丢失。 这和oracle中只要commit提交写入到磁盘,那么事务就不会丢失类似。 可以通过设置参数commitIntervalMs来控制写操作被提交到日志的提交的频率。

journal提交几乎是立即实时的,而MongoDB写数据文件则和Oracle数据库的DBWR进程写数据文件一样是 异步的、懒惰的。 官方文档描述MongoDB可能会登上60秒才把数据实际写入数据文件。由于先写日志的机制,所以数据持久性不会因为惰性写而被影响,因为journal日志中有足够的数据来保证崩溃恢复(在这里mongodb官方文档使用了和oracle一样的关键词  crash recovery.)可以通过修改参数 syncPeriodSecs来修改写入数据文件的频率。

 

MongoDB的源代码使用何种语言编写?

MongoDB主要基于C++实现, 客户端库程序则由各个客户端语言实现。当然部分驱动使用C语言作为扩展以便获得最佳性能。

 

32bit位的MongoDB有什么限制吗?

在MongoDB 3.0以后对于32位平台包括windows和linux,mongodb的商业版本将不再支持这些32位平台 。 而仅仅支持64位的linux和windows。

32位版本的mongodb不支持wiredTiger存储引擎。

当运行32位的mongodb,则数据库的总大小,包括数据和索引的总和大小上线是2GB。  所以,不要在32位平台上部署mongoDB(实际maclean也不建议在windows上部署和学习MonoDB,linux总是最佳平台)。

64位版本的Mongodb,在实际上没有存储大小的限制。 对于产品环境的使用,当然建议也几乎只能使用64位操作系统+64位MongoDB版本。

 

注意:在32位mongodb中默认禁用journaling,因为启用了journaling后会进一步限制32位数据库的大小。

 

Comment

*

沪ICP备14014813号-2

沪公网安备 31010802001379号