黄东旭关于基础软件产品价值的思考
875
2023-04-27
Mongodb体系结构与客户端基本操作及注意细节
说到Mongodb的体系结构,免不了与关系型数据库做个对比。这里以MySQL为例,我们进行一些比较:
从逻辑结构上对比:
MySQL层次概念 ***层次概念
数据库(database) 数据库(database)
表(table) 集合(collection)
记录(row) 文档(document)
在***中没有行、列、关系的概念,集合中的文档相当于一条记录,这体现了模式自由的特点。
从数据存储结构上对比:
MySQL的每个数据库存放在一个与数据库同名的文件夹中,MySQL如果使用MyISAM存储引擎,数据库文件类型就包括.frm、.MYD(存放数据,D为Data)、.MYI(存放索引,I为Index)。
***的默认数据目录时/data/db,它负责存储所有***的数据文件。在***内部,每个数据库都包含一个.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等。
***客户段基本操作:
首先当然是要确认***的mongod服务是打开的。详细见我之前的博客。
打开***客户端的方法时运行***的bin目录下的mongo。
接下来介绍几个基本操作,以及一个细节。
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 >
直接用即可。
这里我们依旧首先验证一个小细节。什么样子的表操作才会导致表的生成。通过上面的命令。我们在还没有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集合已经有了。这说明在建立集合时,必须想新的集合中插入有效数据,才能真正建立集合。
总结上面的两个细节就是,在***中建立数据库时,只要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字符串都可以表示为字符串类型的数据符号: 在***中不支持这种类型,将自动转义成字符串ObjectId: ***独有,对象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" } >
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。