当前位置: 首页 > 科技观察

Mongodb架构及客户端基本操作及注意细节

时间:2023-03-14 09:55:14 科技观察

说到Mongodb架构,免不了要和关系型数据库类比。这里以MySQL为例,做一些比较:从逻辑结构上比较:MongoDB没有行、列、关系的概念,集合中的一个文档相当于一条记录,体现了schema自由的特点。从数据存储结构比较: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@neilhostDownloads]$pstree-p|grepmongod|-mongod(3556)-+-{mongod}(3557)||-{mongod}(3558)||-{mongod}(3559)||-{mongod}(3563)||-{mongod}(3564)||-{mongod}(3565)||-{mongod}(3566)||-{mongod}(3567)|`-{mongod}(3568)[尼尔@neilhostDownloads]$cd/usr/local/mongodb/bin/mongobash:cd:/usr/local/mongodb/bin/mongo:不是目录[neil@neilhostDownloads]$sudo/usr/local/mongodb/bin/mongoMongoDBshellversion:2.6.8connectingto:test欢迎使用MongoDBshell。对于interactivehelp,输入“help”。更全面的文档,见http://docs.mongodb.org/Questions?Trythesupportgrouphttp://groups.google.com/group/mongodb-user然后介绍几个基本操作,并详细介绍.showdbs表示显示mongodb中所有的数据库。刚安装mongodb时,默认有admin和local两个数据库,不用管。db指的是当前工作环境所在的数据库。每次进入mongo的时候。默认进入的数据库是test,这是一个隐式存储的数据库。如果需要进入特定的数据库或者想新建一个数据库,只需要“使用数据库名”即可。在mongodb中,不需要createdatabase的操作,想用就用,mongodb会自动为我们创建数据库,就像一个服务周到的“黑管家”。这里,我使用dt2新建一个数据库dt2,客户端立即将实际工作环境转移到dt2中。但是,如果你showdbs,你会发现数据库并没有真正建立起来。是否需要创建一个新表并插入一些数据?不需要,你只需要在当前数据库dt2中输入任意一个小的操作命令,比如显示当前数据库的集合,dt2就真正建立起来了。connectingto:test>showdbsadmin(empty)dt10.078GBlocal0.078GB>dbtest>usedt2switchedtodbdt2>showdbsadmin(empty)dt10.078GBlocal0.078GB>showcollections>showdbsadmin(empty)dt10.078GBdt2(empty)showlocal0.078GB上面的命令collections是为了显示当前数据库下的集合。由于还没有集合,因此不会显示任何内容。接下来我们尝试创建集合并插入数据。>db.student.find()>showcollections>mongodb创建集合(表),仍然是创建表不需要声明的过程。即不需要createcollection或createtable等操作。直接使用即可。这里我们还是先验证一个小细节。什么样的表操作会导致表的生成。通过上面的命令。我们在没有学生集合的时候查询(find()),但是然后显示集合的命令可以看到没有dt2的学生集合。于是我直接在student表中插入了一个Json对象(Mongodb中存储的单位是Bson对象,逻辑概念上是文档)>db.student.insert({name:"Viper",age:20})WriteResult({"nInserted":1})>showcollectionsstudentsystem.indexes>这时候showcollections可以看到studentcollection已经存在了。这意味着在创建集合时,必须将有效数据插入到新集合中才能真正创建集合。总结以上两个细节,在MongoDB中建库时,只要使用数据库,在数据库下执行任何看似没有任何影响的命令,比如查询集合,就会建立数据库;但是,如果你在数据库中的以下情况下,对新集合的文档查询将不会导致集合的建立,必须将文档数据插入到集合中才能使集合真正建立起来。很多人可能都不知道这样一对的细节!另外,空数据库创建集合时,会生成索引表system.indexes。这个数据库下所有集合的ObjectId的索引值都存储在里面。#p#然后说说增删改查。在前面的示例中,实际上已经添加了一个文档,这里我们将文档添加到学生集合中。>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的学生集合中的两个文档具有不同的“表结构”。这是NoSQL数据库和关系型数据库最重要的区别之一(其他的区别之前也提到过,ACID特性(支持事务),并发)。在第二篇文档中我们可以看到key-value的值可以是数字、字符串等一些基本类型,也可以是数组(其实可以理解为栈。下面的博客文章value为数组时会引入push.和pop,所以理解为类似栈的list更合适),比如上面的phone键。更厉害的是document,也就是插入的Json对象的keyvalue的值可以是另外一个Json对象,比如上面的GPAkey。其实这些数组和json对象更像是python语法中的列表和字典,哈哈哈哈。..这里我整理一下key-value的value数据类型:null:表示一个空值或者一个不存在的值Boolean类型:true和false,比如{male:true}32位整数:Mongodb的控制台使用JSengineInput,andJSonlysupported64-bitfloating-pointnumbers,so32-bitintegerswillbeautomaticallyescapeto64-bitintegers:同上,willbeautomaticallyescapeto64-bitfloating-pointnumbers64-bit浮点数:Mongodb控制台数字的默认类型。如{salary:23871.12}String:UTF-8字符串可以表示为string类型的数据符号:MongoDB不支持该类型,会自动转义为stringObjectId:MongoDB独有,当对象id最文档中最近的12位十六进制ID。(Timestamp|Machine|PID|Counter)日期:注:使用时新增。如{birthday:newDate()}正则表达式:文档键值可以包含正则表达式,正则表达式用JS语法表示。如:{key:/ho/i}代码:JS文件可以包含在文件中。如{key:function(){/*........*/}}(可以是没有函数名的匿名函数)数组:文档中key的值可以用数组表示,数组也可以是嵌套数组。嵌入文档:文档可以包含其他文档,或者它们可以作为值嵌入到父文档中。比如{x:{name:"happyBKs",age:2}}插入Json对象,我们可以看到每一个新插入的文档都会被赋予一个_id,可以理解为记录的主人,是key由mongodb自动生成,它的值是一个具体的ObjectId对象,是一个96位的二进制数,由机器码、机器进程号、时间、当前命名空间号四部分自动生成,***。当然,如果你愿意,也可以在插入Json对象时指定_id的值。只要没有主键冲突,就可以正常插入。添加一条记录除了insert方法还有一种方法,那就是save。它的作用是当你指定了Json的_id,而集合中已经存在这个_id,那么它会更新对应的文档;否则,插入一个新文档。请看下面的例子,_id为1的Json对象,添加第一次保存,更改第二次保存。>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的记录中添加一个gender字段,但是发现除了_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"}>上面的例子是添加一个名为TB的对象,然后改变它。改变的内容包括将原来agekey的值从11改为22,并增加一个新的keyclassid这里赋值“1515”需要用到的运算符是$set,用于设置值的关键。删除的方法是remove。用法是在db.collection.remove({...})参数中指定需要删除对象的条件。注意当参数为空时,或者是一个空的Json对象。即db.collection.remove()和db.collection.remove({}),会删除集合中的所有文档!!!>db.class.insert({classname:"English",teacher:"MrA"})WriteResult({"nInserted":1})>db.class.insert({classname:"Math",teacher:"MrB"})WriteResult({"nInserted":1})>db.class.find(){"_id":ObjectId("54fc54773fc8173ba3302e6f"),"classname":"English","teacher":"MrA"}{"_id":ObjectId("54fc54833fc8173ba3302e70"),"classname":"Math","teacher":"MrB"}>db.class.remove({classname:"English"})WriteResult({"nRemoved":1})>db.class.find(){"_id":ObjectId("54fc54833fc8173ba3302e70"),"classname":"Math","teacher":"MrB"}>>db.class.remove({})WriteResult({"nRemoved":1})>db.class.find()>在上面的例子中,创建了一个新的类集合并插入了两个文档。其中,你自己看看。有两种查询方法db.collection.find({....}),其中参数为查询对象的条件。如果find()或find({})是全搜索,它与remove非常相似。db.collection.findOne({...})类似于find,但它只会返回第一个符合条件的查询对象。>db.class.insert({classname:"English",teacher:"MrAAA"})WriteResult({"nInserted":1})>db.class.insert({classname:"English",teacher:"MrZZZ"})WriteResult({"nInserted":1})>db.class.insert({classname:"English",teacher:"MrWWW"})WriteResult({"nInserted":1})>db.class.insert({classname:"English",teacher:"MrSSS"})WriteResult({"nInserted":1})>db.class.insert({classname:"French",teacher:"MrSSS"})WriteResult({"nInserted":1})>db.class.find({}){"_id":ObjectId("54fc562e3fc8173ba3302e71"),"classname":"English","teacher":"MrAAA"}{"_id":ObjectId("54fc56373fc8173ba3302e72"),"classname":"English","teacher":"MrZZZ"}{"_id":ObjectId("54fc56413fc8173ba3302e73"),"classname":"English","teacher":"MrWWW"}{"_id":ObjectId("54fc564e3fc8173ba3302e74"),"classname":"English","teacher":"MrSSS"}{"_id":ObjectId("54fc56603fc8173ba3302e75"),"classname":"French","teacher":"MrSSS"}>db.class.findOne({classname:"English"}){"_id":ObjectId("54fc562e3fc8173ba3302e71"),"classname":"English","teacher":"MrAAA"}>db.class.find({classname:"English"}){"_id":ObjectId("54fc562e3fc8173ba3302e71"),"classname":"English","teacher":"MrAAA"}{"_id":ObjectId("54fc56373fc8173ba3302e72"),"classname":"English","teacher":"MrZZZ"}{"_id":ObjectId("54fc56413fc8173ba3302e73"),"classname":"English","teacher":"MrWWW"}{"_id":ObjectId("54fc564e3fc8173ba3302e74"),"classname":"English","teacher":"MrSSS"}>博客来源:http://my.oschina.net/u/1156339/blog/384073