说到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。
- [neil@neilhost Downloads]$ pstree -p | grep mongod
- |-mongod(3556)-+-{mongod}(3557)
- | |-{mongod}(3558)
- | |-{mongod}(3559)
- | |-{mongod}(3563)
- | |-{mongod}(3564)
- | |-{mongod}(3565)
- | |-{mongod}(3566)
- | |-{mongod}(3567)
- | `-{mongod}(3568)
- [neil@neilhost Downloads]$ cd /usr/local/mongodb/bin/mongo
- bash: cd: /usr/local/mongodb/bin/mongo: 不是目录
- [neil@neilhost Downloads]$ sudo /usr/local/mongodb/bin/mongo
- MongoDB shell version: 2.6.8
- connecting to: test
- Welcome to the MongoDB shell.
- For interactive help, type "help".
- For more comprehensive documentation, see
- http://docs.mongodb.org/
- Questions? Try the support group
- 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就会被真正建立了。
- connecting to: test
- > show dbs
- admin (empty)
- dt1 0.078GB
- local 0.078GB
- > db
- test
- > use dt2
- switched to db dt2
- > show dbs
- admin (empty)
- dt1 0.078GB
- local 0.078GB
- > show collections
- > show dbs
- admin (empty)
- dt1 0.078GB
- dt2 (empty)
- local 0.078GB
在上面的命令中,show collections是显示当前数据库下的集合有哪些。因为现在还没有集合,所以什么都不显示。
接下来我们尝试建立集合和插入数据。
- > db.student.find()
- > show collections
- >
mongodb建立集合(表),依然是一个不需要声明着建立表的过程。即不需要create collection或create table之类操作。
直接用即可。
这里我们依旧首先验证一个小细节。什么样子的表操作才会导致表的生成。通过上面的命令。我们在还没有student集合的时候对其进行查询(find()),但是,之后显示集合的命令可以看到并没有为dt2建立student集合。
于是我直接在student表中插入一个Json对象(存储在Mongodb的单元是Bson对象,在逻辑概念上是一个文档)
- > db.student.insert({name:"Viper",age:20})
- WriteResult({ "nInserted" : 1 })
- > show collections
- student
- system.indexes
- >
这时候,当show collections的时候可以看见,student集合已经有了。这说明在建立集合时,必须想新的集合中插入有效数据,才能真正建立集合。
总结上面的两个细节就是,在MongoDB中建立数据库时,只要use数据库,并且在数据库下执行任何看似不会有任何影响的命令,如查询集合,都会使得数据库建立起来;但是,如果在数据库下,对于新建集合的文档查询是不会导致集合建立的,必须有文档数据插入集合,才能使得集合真正建立起来。这样一对细节很多人未必知道!
另外当空数据库建立集合时,会生成一个索引表,system.indexes。该数据库下的所有集合的ObjectId的索引值全都存放在这里面。
#p#
那么下面我们来分别说说增删改查。
增
前面的例子中其实已经加入了一条文档,这里我们在增加一个文档到student集合。
- > db.student.insert({name:"TA",age:18,phone:["1311234567","021-87658765"],GPA:{Math:88,English:99}})
- WriteResult({ "nInserted" : 1 })
- > db.student.find()
- { "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
- { "_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是被添加,而第二次则是被更改。
- > db.student.find()
- { "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
- { "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
- > db.student.save({_id:1,name:"happyBKs",age:0})
- WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : 1 })
- > db.student.find()
- { "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
- { "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
- { "_id" : 1, "name" : "happyBKs", "age" : 0 }
- > db.student.save({_id:1,name:"hahaBKs",age:2})
- WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
- > db.student.find()
- { "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
- { "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
- { "_id" : 1, "name" : "hahaBKs", "age" : 2 }
- >
改
更改的操作用的是update方法。
用法为: db.collection.update({...},{...})
参数有两个,***个指定要改谁,第二个指定改成什么。
但是事情没有这么简单,请看下面的例子。原本的打算是将名字为hahaBKs的记录增加一个性别字段,但是发现除了_id的所有字段都被覆盖掉了,只剩下了gender。这显然不是我们的预想的结果。
- > db.student.find()
- { "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
- { "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
- { "_id" : 1, "name" : "hahaBKs", "age" : 2 }
- >
- > db.student.update({name:"hahaBKs"},{gender:"male"})
- WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
- > db.student.find()
- { "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
- { "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
- { "_id" : 1, "gender" : "male" }
- >
那么问题出在哪里呢?这里我们需要用到的一个操作符$set,具体的用法以后的文章李会详细来说。
下面我直接给出一个解决办法的示例,如下:
- > db.student.insert({name:"TB",age:11,gender:"male",room:"301"})
- WriteResult({ "nInserted" : 1 })
- > db.student.find()
- { "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
- { "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
- { "_id" : 1, "gender" : "male" }
- { "_id" : ObjectId("54fc521d3fc8173ba3302e6e"), "name" : "TB", "age" : 11, "gender" : "male", "room" : "301" }
- > db.student.update({name:"TB"},{$set:{age:22,classid:"1515"}})
- WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
- > db.student.find()
- { "_id" : ObjectId("54fb0d853fc8173ba3302e6c"), "name" : "Viper", "age" : 20 }
- { "_id" : ObjectId("54fb10493fc8173ba3302e6d"), "name" : "TA", "age" : 18, "phone" : [ "1311234567", "021-87658765" ], "GPA" : { "Math" : 88, "English" : 99 } }
- { "_id" : 1, "gender" : "male" }
- { "_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({}),将把该集合中的所有文档全部删除!!!
- > db.class.insert({classname:"English",teacher:"Mr A"})
- WriteResult({ "nInserted" : 1 })
- > db.class.insert({classname:"Math",teacher:"Mr B"})
- WriteResult({ "nInserted" : 1 })
- > db.class.find()
- { "_id" : ObjectId("54fc54773fc8173ba3302e6f"), "classname" : "English", "teacher" : "Mr A" }
- { "_id" : ObjectId("54fc54833fc8173ba3302e70"), "classname" : "Math", "teacher" : "Mr B" }
- > db.class.remove({classname:"English"})
- WriteResult({ "nRemoved" : 1 })
- > db.class.find()
- { "_id" : ObjectId("54fc54833fc8173ba3302e70"), "classname" : "Math", "teacher" : "Mr B" }
- >
- > db.class.remove({})
- WriteResult({ "nRemoved" : 1 })
- > db.class.find()
- >
上面的例子中新建了一个class集合,并插入两个文档。其中种种,自己看吧。
查
用法两种
db.collection.find({....}),其中参数是查询对象的条件。如果find()或find({})就是全查,这和remove很类似。
db.collection.findOne({...})和find相似,但是它只会返回***个查询到的符合条件的对象。
- > db.class.insert({classname:"English",teacher:"Mr AAA"})
- WriteResult({ "nInserted" : 1 })
- > db.class.insert({classname:"English",teacher:"Mr ZZZ"})
- WriteResult({ "nInserted" : 1 })
- > db.class.insert({classname:"English",teacher:"Mr WWW"})
- WriteResult({ "nInserted" : 1 })
- > db.class.insert({classname:"English",teacher:"Mr SSS"})
- WriteResult({ "nInserted" : 1 })
- > db.class.insert({classname:"French",teacher:"Mr SSS"})
- WriteResult({ "nInserted" : 1 })
- > db.class.find({})
- { "_id" : ObjectId("54fc562e3fc8173ba3302e71"), "classname" : "English", "teacher" : "Mr AAA" }
- { "_id" : ObjectId("54fc56373fc8173ba3302e72"), "classname" : "English", "teacher" : "Mr ZZZ" }
- { "_id" : ObjectId("54fc56413fc8173ba3302e73"), "classname" : "English", "teacher" : "Mr WWW" }
- { "_id" : ObjectId("54fc564e3fc8173ba3302e74"), "classname" : "English", "teacher" : "Mr SSS" }
- { "_id" : ObjectId("54fc56603fc8173ba3302e75"), "classname" : "French", "teacher" : "Mr SSS" }
- > db.class.findOne({classname:"English"})
- {
- "_id" : ObjectId("54fc562e3fc8173ba3302e71"),
- "classname" : "English",
- "teacher" : "Mr AAA"
- }
- > db.class.find({classname:"English"})
- { "_id" : ObjectId("54fc562e3fc8173ba3302e71"), "classname" : "English", "teacher" : "Mr AAA" }
- { "_id" : ObjectId("54fc56373fc8173ba3302e72"), "classname" : "English", "teacher" : "Mr ZZZ" }
- { "_id" : ObjectId("54fc56413fc8173ba3302e73"), "classname" : "English", "teacher" : "Mr WWW" }
- { "_id" : ObjectId("54fc564e3fc8173ba3302e74"), "classname" : "English", "teacher" : "Mr SSS" }
- >
博文出处:http://my.oschina.net/u/1156339/blog/384073