Mongodb体系结构与客户端基本操作及注意细节

数据库 其他数据库 MongoDB
说到Mongodb的体系结构,免不了与关系型数据库做个对比。本文以MySQL为例来进行一些比较。

[[128855]]

说到Mongodb的体系结构,免不了与关系型数据库做个对比。这里以MySQL为例,我们进行一些比较:

从逻辑结构上对比:

MySQL层次概念             MongoDB层次概念

数据库(database)      数据库(database)

表(table)                     集合(collection)

记录(row)                    文档(document)

在MongoDB中没有行、列、关系的概念,集合中的文档相当于一条记录,这体现了模式自由的特点。

从数据存储结构上对比:

MySQL的每个数据库存放在一个与数据库同名的文件夹中,MySQL如果使用MyISAM存储引擎,数据库文件类型就包括.frm、.MYD(存放数据,D为Data)、.MYI(存放索引,I为Index)。

MongoDB的默认数据目录时/data/db,它负责存储所有MongoDB的数据文件。在MongoDB内部,每个数据库都包含一个.ns文件和一些数据文件,而且这些数据文件会随着数据量的增加而变得越来越多,例如系统中有一个叫mydb的数据库,那么构成mydb这个数据库的文件就会有mydb.ns,mydb.0,mydb.1等等组成。

mydb.ns记录了数据库Json对象的命名空间(ns时namespace的缩写),也就是数据库集合里面的命名空间。mydb.0和mydb.1是存放数据库mydb的对象的空间,且大小按照2的n次方大小递增。如mydb.`0的大小是16M,当数据库mydb存满16M之后,就会形成生成mydb.1继续存储,mydb.1的大小为32M,以此类推,随着数据的增加,还会有mydb.2、mydb.3等文件出现,大小64M、128M。默认情况下,现在版本的Mongodb在数据库刚刚建立时就会预先分配好XXX.0和XXX.1共48M空间,之后再随着插入对象的增多而生成后续xxx.2等。

MongoDB客户段基本操作:

首先当然是要确认MongoDB的mongod服务是打开的。详细见我之前的博客。

打开MongoDB客户端的方法时运行MongoDB的bin目录下的mongo。

  1. [neil@neilhost Downloads]$ pstree -p | grep mongod 
  2. |-mongod(3556)-+-{mongod}(3557
  3. | |-{mongod}(3558
  4. | |-{mongod}(3559
  5. | |-{mongod}(3563
  6. | |-{mongod}(3564
  7. | |-{mongod}(3565
  8. | |-{mongod}(3566
  9. | |-{mongod}(3567
  10. | `-{mongod}(3568
  11. [neil@neilhost Downloads]$ cd /usr/local/mongodb/bin/mongo 
  12. bash: cd: /usr/local/mongodb/bin/mongo: 不是目录 
  13. [neil@neilhost Downloads]$ sudo /usr/local/mongodb/bin/mongo 
  14. MongoDB shell version: 2.6.8 
  15. connecting to: test 
  16. Welcome to the MongoDB shell. 
  17. For interactive help, type "help"
  18. For more comprehensive documentation, see 
  19. http://docs.mongodb.org/ 
  20. Questions? Try the support group 
  21. http://groups.google.com/group/mongodb-user 

接下来介绍几个基本操作,以及一个细节。

show dbs的意思是显示mongodb中所有的数据库。刚安装好mongodb时,默认有两个数据库admin和local,不去管他们。

db指的是当前工作环境所在的数据库。当你每次进入mongo时。默认进入的数据库时test,它是一个隐式存储的数据库,如果需要进入特定的数据库,或是想建立一个新的数据库,只需要“use 数据库名称”就可以了。在mongodb中不需要create database这种操作,想用就用,mongodb会自动帮我们建立了数据库,就像一个服务周到的“黑执事”。这里,我use dt2建立了一个新的数据库dt2,客户端立即现实工作环境转入到dt2。但是,如果你show dbs,发现数据库并没有真正建立。是需要新建表并且插入一些数据才可以吗?不是,只需要你在当前数据库dt2输入任何一个细小的操作命令,如显示当前数据库的集合有哪些,这时候dt2就会被真正建立了。

  1. connecting to: test 
  2. > show dbs 
  3. admin (empty) 
  4. dt1 0.078GB 
  5. local 0.078GB 
  6. > db 
  7. test 
  8. > use dt2 
  9. switched to db dt2 
  10. > show dbs 
  11. admin (empty) 
  12. dt1 0.078GB 
  13. local 0.078GB 
  14. > show collections 
  15. > show dbs 
  16. admin (empty) 
  17. dt1 0.078GB 
  18. dt2 (empty) 
  19. local 0.078GB 

在上面的命令中,show collections是显示当前数据库下的集合有哪些。因为现在还没有集合,所以什么都不显示。

接下来我们尝试建立集合和插入数据。

  1. > db.student.find() 
  2. > show collections 

mongodb建立集合(表),依然是一个不需要声明着建立表的过程。即不需要create collection或create table之类操作。

直接用即可。

这里我们依旧首先验证一个小细节。什么样子的表操作才会导致表的生成。通过上面的命令。我们在还没有student集合的时候对其进行查询(find()),但是,之后显示集合的命令可以看到并没有为dt2建立student集合。

于是我直接在student表中插入一个Json对象(存储在Mongodb的单元是Bson对象,在逻辑概念上是一个文档)

  1. > db.student.insert({name:"Viper",age:20}) 
  2. WriteResult({ "nInserted" : 1 }) 
  3. > show collections 
  4. student 
  5. system.indexes 

这时候,当show collections的时候可以看见,student集合已经有了。这说明在建立集合时,必须想新的集合中插入有效数据,才能真正建立集合。

总结上面的两个细节就是,在MongoDB中建立数据库时,只要use数据库,并且在数据库下执行任何看似不会有任何影响的命令,如查询集合,都会使得数据库建立起来;但是,如果在数据库下,对于新建集合的文档查询是不会导致集合建立的,必须有文档数据插入集合,才能使得集合真正建立起来。这样一对细节很多人未必知道!

另外当空数据库建立集合时,会生成一个索引表,system.indexes。该数据库下的所有集合的ObjectId的索引值全都存放在这里面。

#p#

那么下面我们来分别说说增删改查。

前面的例子中其实已经加入了一条文档,这里我们在增加一个文档到student集合。

  1. > db.student.insert({name:"TA",age:18,phone:["1311234567","021-87658765"],GPA:{Math:88,English:99}}) 
  2. WriteResult({ "nInserted" : 1 }) 
  3. > db.student.find() 
  4. "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper""age" : 20 } 
  5. "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA""age" : 18"phone" : [ "1311234567""021-87658765" ], "GPA" : { "Math" : 88"English" : 99 } } 

这里可以看到,我向mongodb的student集合中插入的两个文档,它们的“表结构”是不一样的。这就是NoSQL数据库与关系型数据库的最重要区别之一(其他区别之前也提过,ACID特性(事务的支持),并发)。

其中第二条文档中我们看到key-value的value可以是一些数、字符串等基本类型外,还可以是数组(其实可以理解为栈,后面的博客文章会介绍value是数组情况下的push和pop,所以理解为一种类似于栈的列表更妥贴),如上面的phone键。更强大的地方是,文档,即插入的Json对象的keybalue的value可以是另一个Json对象,如上面的GPA键。

其实,这些数组、json对象更像是python语法中的列表和字典,哈哈哈哈。。。

这里,我将key-value的value数据类型做个整理:

  • null: 表示空值或者不存在的值
  • 布尔类型: true和false,如{male:true}
  • 32位整数: Mongodb的控制台使用JS引擎进行输入,而JS仅仅支持64位浮点数,所以32位整数会被自动转义
  • 64位整数: 同上,会被自动转义成64位浮点数
  • 64位浮点数: Mongodb的控制台数字的默认类型。如{salary:23871.12}
  • 字符串 :UTF-8字符串都可以表示为字符串类型的数据
  • 符号: 在MongoDB中不支持这种类型,将自动转义成字符串
  • ObjectId: MongoDB独有,对象id时文档中***的12位16进制的id。(时间戳 | 机器 | PID | 计数器)
  • 日期: 注意:使用的时候要加上new。如{birthday:new Date()}
  • 正则表达式: 文档键值可以包含正则表达式,其正则表达式采用JS语法来表示。如:{key:/ho/i}
  • 代码: 文档中可以包含JS文档。如{key:function(){/*........*/}}(可以是没有函数名的匿名函数)
  • 数组:文档中的key的value可以表示为数组,数组内还可以嵌套数组。
  • 內嵌文档: 文档可以包含别的文档,也可以作为value嵌入到父文档中。如{x:{name:"happyBKs",age:2}}

插入之后的Json对象我们可以看到,新插入的每个文档都被赋予了一个_id,这是可以理解为记录的主,是mongodb自动生存成的key,它的value是一个特定的ObjectId对象,是一个96位二进制数,由机器码、机器进程号、时间、当前命名空间的编号四个部分自动生成,***。当然,如果你愿意,也可以在插入Json对象时自己指定_id的value,只要未发生主键冲突,都可以正常插入。

增加一个记录除了用insert方法之外还有一种方法,那就是save。它的功能是当你指定Json的_id,并且_id在集合中已经存在,那么它会更新相应的文档;否则,则插入一个新的文档。请看下面这个例子,_id的为1的Json对象,***次save是被添加,而第二次则是被更改。

  1. > db.student.find() 
  2. "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper""age" : 20 } 
  3. "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA""age" : 18"phone" : [ "1311234567""021-87658765" ], "GPA" : { "Math" : 88"English" : 99 } } 
  4. > db.student.save({_id:1,name:"happyBKs",age:0}) 
  5. WriteResult({ "nMatched" : 0"nUpserted" : 1"nModified" : 0"_id" : 1 }) 
  6. > db.student.find() 
  7. "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper""age" : 20 } 
  8. "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA""age" : 18"phone" : [ "1311234567""021-87658765" ], "GPA" : { "Math" : 88"English" : 99 } } 
  9. "_id" : 1"name" : "happyBKs""age" : 0 } 
  10. > db.student.save({_id:1,name:"hahaBKs",age:2}) 
  11. WriteResult({ "nMatched" : 1"nUpserted" : 0"nModified" : 1 }) 
  12. > db.student.find() 
  13. "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper""age" : 20 } 
  14. "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA""age" : 18"phone" : [ "1311234567""021-87658765" ], "GPA" : { "Math" : 88"English" : 99 } } 
  15. "_id" : 1"name" : "hahaBKs""age" : 2 } 
  16. >

更改的操作用的是update方法。

用法为: db.collection.update({...},{...})

参数有两个,***个指定要改谁,第二个指定改成什么。

但是事情没有这么简单,请看下面的例子。原本的打算是将名字为hahaBKs的记录增加一个性别字段,但是发现除了_id的所有字段都被覆盖掉了,只剩下了gender。这显然不是我们的预想的结果。

  1. > db.student.find() 
  2. "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper""age" : 20 } 
  3. "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA""age" : 18"phone" : [ "1311234567""021-87658765" ], "GPA" : { "Math" : 88"English" : 99 } } 
  4. "_id" : 1"name" : "hahaBKs""age" : 2 } 
  5. > db.student.update({name:"hahaBKs"},{gender:"male"}) 
  6. WriteResult({ "nMatched" : 1"nUpserted" : 0"nModified" : 1 }) 
  7. > db.student.find() 
  8. "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper""age" : 20 } 
  9. "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA""age" : 18"phone" : [ "1311234567""021-87658765" ], "GPA" : { "Math" : 88"English" : 99 } } 
  10. "_id" : 1"gender" : "male" } 

那么问题出在哪里呢?这里我们需要用到的一个操作符$set,具体的用法以后的文章李会详细来说。

下面我直接给出一个解决办法的示例,如下:

  1. > db.student.insert({name:"TB",age:11,gender:"male",room:"301"}) 
  2. WriteResult({ "nInserted" : 1 }) 
  3. > db.student.find() 
  4. "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper""age" : 20 } 
  5. "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA""age" : 18"phone" : [ "1311234567""021-87658765" ], "GPA" : { "Math" : 88"English" : 99 } } 
  6. "_id" : 1"gender" : "male" } 
  7. "_id" : ObjectId("54fc521d3fc8173ba3302e6e"), "name" : "TB""age" : 11"gender" : "male""room" : "301" } 
  8. > db.student.update({name:"TB"},{$set:{age:22,classid:"1515"}}) 
  9. WriteResult({ "nMatched" : 1"nUpserted" : 0"nModified" : 1 }) 
  10. > db.student.find() 
  11. "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper""age" : 20 } 
  12. "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA""age" : 18"phone" : [ "1311234567""021-87658765" ], "GPA" : { "Math" : 88"English" : 99 } } 
  13. "_id" : 1"gender" : "male" } 
  14. "_id" : ObjectId("54fc521d3fc8173ba3302e6e"), "name" : "TB""age" : 22"gender" : "male""room" : "301""classid" : "1515" } 

上面的例子是,增加了一个name叫TB的对象,然后对其进行更改,更改的内容包括,将原有age键的值11改成22,并增加一个新的键classid赋值“1515”。这里需要用到的操作符就是$set,用于设置键的值。

删除的方法是remove。

用法是 db.collection.remove({...})

参数里写明想需要删除的对象的条件。

注意,当参数空着,或者空的Json对象。即db.collection.remove()和db.collection.remove({}),将把该集合中的所有文档全部删除!!!

  1. > db.class.insert({classname:"English",teacher:"Mr A"}) 
  2. WriteResult({ "nInserted" : 1 }) 
  3. > db.class.insert({classname:"Math",teacher:"Mr B"}) 
  4. WriteResult({ "nInserted" : 1 }) 
  5. > db.class.find() 
  6. "_id" : ObjectId("54fc54773fc8173ba3302e6f"), "classname" : "English""teacher" : "Mr A" } 
  7. "_id" : ObjectId("54fc54833fc8173ba3302e70"), "classname" : "Math""teacher" : "Mr B" } 
  8. > db.class.remove({classname:"English"}) 
  9. WriteResult({ "nRemoved" : 1 }) 
  10. > db.class.find() 
  11. "_id" : ObjectId("54fc54833fc8173ba3302e70"), "classname" : "Math""teacher" : "Mr B" } 
  12. > db.class.remove({}) 
  13. WriteResult({ "nRemoved" : 1 }) 
  14. > db.class.find() 

上面的例子中新建了一个class集合,并插入两个文档。其中种种,自己看吧。

用法两种

db.collection.find({....}),其中参数是查询对象的条件。如果find()或find({})就是全查,这和remove很类似。

db.collection.findOne({...})和find相似,但是它只会返回***个查询到的符合条件的对象。

  1. > db.class.insert({classname:"English",teacher:"Mr AAA"}) 
  2. WriteResult({ "nInserted" : 1 }) 
  3. > db.class.insert({classname:"English",teacher:"Mr ZZZ"}) 
  4. WriteResult({ "nInserted" : 1 }) 
  5. > db.class.insert({classname:"English",teacher:"Mr WWW"}) 
  6. WriteResult({ "nInserted" : 1 }) 
  7. > db.class.insert({classname:"English",teacher:"Mr SSS"}) 
  8. WriteResult({ "nInserted" : 1 }) 
  9. > db.class.insert({classname:"French",teacher:"Mr SSS"}) 
  10. WriteResult({ "nInserted" : 1 }) 
  11. > db.class.find({}) 
  12. "_id" : ObjectId("54fc562e3fc8173ba3302e71"), "classname" : "English""teacher" : "Mr AAA" } 
  13. "_id" : ObjectId("54fc56373fc8173ba3302e72"), "classname" : "English""teacher" : "Mr ZZZ" } 
  14. "_id" : ObjectId("54fc56413fc8173ba3302e73"), "classname" : "English""teacher" : "Mr WWW" } 
  15. "_id" : ObjectId("54fc564e3fc8173ba3302e74"), "classname" : "English""teacher" : "Mr SSS" } 
  16. "_id" : ObjectId("54fc56603fc8173ba3302e75"), "classname" : "French""teacher" : "Mr SSS" } 
  17. > db.class.findOne({classname:"English"}) 
  18. "_id" : ObjectId("54fc562e3fc8173ba3302e71"), 
  19. "classname" : "English"
  20. "teacher" : "Mr AAA" 
  21. > db.class.find({classname:"English"}) 
  22. "_id" : ObjectId("54fc562e3fc8173ba3302e71"), "classname" : "English""teacher" : "Mr AAA" } 
  23. "_id" : ObjectId("54fc56373fc8173ba3302e72"), "classname" : "English""teacher" : "Mr ZZZ" } 
  24. "_id" : ObjectId("54fc56413fc8173ba3302e73"), "classname" : "English""teacher" : "Mr WWW" } 
  25. "_id" : ObjectId("54fc564e3fc8173ba3302e74"), "classname" : "English""teacher" : "Mr SSS" } 
  26. >

博文出处:http://my.oschina.net/u/1156339/blog/384073

 

责任编辑:Ophira 来源: oschina博客
相关推荐

2009-12-28 10:42:01

MPLS技术

2009-12-21 14:37:14

2010-02-25 14:46:31

2010-04-01 16:54:06

Oracle体系结构

2009-12-23 15:27:44

2010-09-25 12:59:33

2011-11-30 14:12:05

JavaJVM虚拟机

2023-12-26 09:33:47

2010-05-31 10:11:32

瘦客户端

2009-06-26 15:58:28

EJB

2009-09-11 10:38:03

LINQ体系结构

2012-02-06 17:22:44

MySQL

2009-07-15 13:46:26

Swing体系结构

2014-07-23 09:33:52

2015-09-30 10:36:03

eSpace UC客户华为

2023-09-12 23:08:09

2010-04-01 16:40:17

Oracle数据库

2020-04-15 21:43:22

JVMJavaVMware

2010-08-16 12:54:21

ASON路由技术

2010-09-25 13:38:23

Inside JVM
点赞
收藏

51CTO技术栈公众号