-
安装配置
Windows安装:
Mongo数据模型
| 01 | 一个Mongo系统(参考上述部署)包含一组数据库 | |
| 02 | 一个 database 包含一组collection | |
| 03 | 一个 collection 包含一组document | |
| 04 | 一个 document 是一组field | |
| 05 | 一个 field 是一对key-value | |
| 06 | 一个 key 是一个名字(字符串) | |
| 07 | 一个 value 可以是一个 | |
| 08 | 基本类型,如字符串,整数,浮点数,时间戳,二进制等等 | |
| 09 | document | |
| 10 | value的数组 | |
1、下载 MongoDB
MongoDB官网下载地址:http://www.mongodb.org/downloads 我下载的是Nightly(latest & stable)
2、在C盘新建目录mongoDB,并把刚才下的Zip包中的bin解压到新建的mongoDB 中。
| 1 | mkdir c:mongodb | |
| 2 | mkdir c:mongodbbin | |
bin下的文件列表如下
| 01 | c:mongoDBbin 的目录 | |
| 02 |
| |
| 03 | 2011/09/06 14:26 <DIR> . | |
| 04 | 2011/09/06 14:26 <DIR> .. | |
| 05 | 2011/08/31 02:06 4,692,480 bsondump.exe | |
| 06 | 2011/08/31 02:09 2,347,008 mongo.exe | |
| 07 | 2011/08/31 02:14 5,054,464 mongod.exe | |
| 08 | 2011/08/31 02:17 4,709,888 mongodump.exe | |
| 09 | 2011/08/31 02:19 4,696,576 mongoexport.exe | |
| 10 | 2011/08/31 02:22 4,714,496 mongofiles.exe | |
| 11 | 2011/08/31 02:24 4,734,464 mongoimport.exe | |
| 12 | 2011/08/31 02:27 4,705,792 mongorestore.exe | |
| 13 | 2011/08/31 02:29 3,448,832 mongos.exe | |
| 14 | 2011/08/31 02:31 4,774,400 mongostat.exe | |
3、在c:mongodb下新建文件夹data
| 1 | mkdir c:mongodbdata |
4、启动mongodb
| 01 | c:mongoDBbin>mongod -dbpath c:mongodbdata | |
| 02 | Tue Sep 06 16:02:06 [initandlisten] MongoDB starting : pid=452 port=27017 dbpath | |
| 03 | =c:mongodbdata 64-bit | |
| 04 | Tue Sep 06 16:02:06 [initandlisten] db version v1.8.4-pre-, pdfile version 4.5 | |
| 05 | Tue Sep 06 16:02:06 [initandlisten] git version: f3bd113e0df642703fda8cc9fe7f6cd | |
| 06 | f6503e5e8 | |
| 07 | Tue Sep 06 16:02:06 [initandlisten] build sys info: windows (6, 1, 7601, 2, 'Ser | |
| 08 | vice Pack 1') BOOST_LIB_VERSION=1_42 | |
| 09 | Tue Sep 06 16:02:06 [initandlisten] waiting for connections on port 27017 |
| 10 | Tue Sep 06 16:02:06 [websvr] web admin interface listening on port 28017 |
嗯 MongoDB已经起了。
Linux安装:
先去MongoDB的官方下载http://www.mongodb.org/downloads,我这里下的是LINUX 32-BIT 1.8.2的版本。
下完文件名为mongodb-linux-i686-1.8.2.tgz
解压 tar xzvf mongodb-linux-i686-1.8.2.tgz
得到文件夹mongodb-linux-i686-1.8.2,把这个文件移动到$HOME目录下,然后设置PATH
不知道HOME目录是什么就在Terminal里 echo $HOME你就知道了
在$HOME目录下建个.bashrc文件,写入如下配置
export MONGODB_HOME=/home/castiel/mongodb-linux-i686-1.8.2
export PATH=$MONGODB_HOME/bin:$PATH
好了,现在再建个目录作为保存我们数据库的地方
我还是建在$HOME目录下,名字叫mongodatabase
现在我们启动Mongodb的服务,在Terminal里输入
mongod -dbpath /home/castiel/mongodatabse
后面的参数指定了我们刚新建的文件夹作为存放数据库的地方
现在服务应该跑起来了,再开个Terminal,输入mongo,就进入mongodb的shell交互界面
如果到此一切成功,那么基础环境就算是搞定了。
-
使用介绍
Mongodb中有document,collection,database
关系为database里有多个collection,每个collection里有多个document
document就是以key-value的形式存放的,value也可以是document
在shell里,db代表当前所使用的数据库,
| 1 | show dbs #显示所有数据库 | |
| 2 | db.version() #显示Mongodb的版本号 | |
以下"collection"代表任意collection
db.collection.update #mongodb的update是atomic,假如有两个人同时update同一个数据,两个都会生效,数据的状态是最后update的状态(last update win)
db.collection.remove #不加参数则全部删除,但保留index
| 1 | x = db.collection.findOne() | |
| 2 | x.num = 42 | |
| 3 | db.collection.save(x) #shell中一种比用update来更新的更好办法 |
db.drop_collection("name_of_collection") #彻底删除
db.collection.insert() #插入document
update里用的modifier
$set #用来设置一个document,如不存在则创建该document
| 1 | {"$set" : {"author.name" : "joe schmoe"}} |
$unset #用来删除一个document
$inc #用来增加一个document的value部分,value必需是数字。如不存在则创建该document
| 1 | {"$inc" : {"score" : 10000}} |
$push #用来向一个document的value部分,以数组的方式
| 1 | {$push : {"comments" :{"name" : "joe", "email" : "joe@example.com", "content" : "nice post."}} |
$ne #与$push配合使用,不存在存会添加,存在就不会添加
| 1 | {"authors cited" : {"$ne" : "Richie"}}, {$push : {"authors cited" : "Richie"}} |
$addToSet #同上,但是是单独使用的.有的时候你不确定某个document到底存不存在,用这个就不会造成数据的重复
| 1 | {"$addToSet" : {"emails" : "joe@hotmail.com"}} |
$each #与$addToSet配合使用,添加数组
| 1 | {"$addToSet" :{"emails" : {"$each" : ["joe@php.net","joe@example.com","joe@python.org"]}} |
$pop #像栈一样删除,随便从哪头开始
| 1 | {$pop : {key : 1}} | |
| 2 | {$pop : {key : -1}} | |
$pull #根据所给的KEY删除指定的document,假如有多个KEY匹配,则删除多个document
| 1 | {"$pull" : {"todo" : "laundry"}} |
假如有下面这样的一个docment
| 01 | { | |
| 02 | "_id" : ObjectId("4b329a216cc613d5ee930192"), | |
| 03 | "content" : "...", | |
| 04 | "comments" : [ | |
| 05 | { | |
| 06 | "comment" : "good post", | |
| 07 | "author" : "John", | |
| 08 | "votes" : 0 | |
| 09 | }, | |
| 10 | { | |
| 11 | "comment" : "i thought it was too short", | |
| 12 | "author" : "Claire", | |
| 13 | "votes" : 3 | |
| 14 | }, | |
| 15 | { | |
| 16 | "comment" : "free watches", | |
| 17 | "author" : "Alice", | |
| 18 | "votes" : -1 | |
| 19 | }] | |
| 20 | } | |
我们现在想要更新第一个名为good post的comments的votes,可以这样做
| 1 | db.blog.update({"post" : post_id}, {"$inc" : {"comments.0.votes" : 1}}) #看见没,可以像操作数组一样,多方便啊 |
现在我们来把这个comment的author由John改成Jim,但如果这个时候我不知道John是在数组里的第几个,即我不知道index,这个时候可以用$,即原来用index的地主改成$,就像下面这样
| 1 | db.blog.update({"comments.author" : "John"},{"$set" : {"comments.$.author" : "Jim"}}) |
update还有另一种形式,就是其第三个参数设置为true,使之成为一个upsert
| 1 | db.analytics.update({"url" : "/blog"}, {"$inc" : {"visits" : 1}}, true) |
上面的update包含了两个意思,以{url : "/blog"}进行查询,成功找到就更新,没有找到就创建
这样我们就不用自己写代码来判断存在或是不存在的情况了
update默认情况下只会更新第一个匹配的到的数据,如果要更新所有都匹配的数据,只要把它的第四个参数也设置为true就行
用findAndModify可以解决多线程争用问题,不知道这词用的对不对,反正自己明白就好了
用在update上
| 1 | ps = db.runCommand({"findAndModify" : "processes", | |
| 2 | "query" : {"status" : "READY"}, | |
| 3 | "sort" : {"priority" : -1}, |
| 4 | "update" : {"$set" : {"status" : "RUNNING"}}).value |
| 5 | do_something(ps) | |
| 6 | db.process.update({"_id" : ps._id}, {"$set" : {"status" : "DONE"}}) | |
可以用在remove上
| 1 | ps = db.runCommand({"findAndModify" : "processes", | |
| 2 | "query" : {"status" : "READY"}, | |
| 3 | "sort" : {"priority" : -1}, | |
| 4 | "remove" : true).value | |
| 5 | do_something(ps) |
对于,这三种操作:insert,remove,update,这三种速度都很快,因为他们都是fire-and-forget类型的操作,即不用等待服务器作出response,就马上可以进行下一步操作。
那也许你就会问,我都不能确定服务器收到了我的数据,那怎么办,其实是有办法的,现在我还没有看到而已。
| 1 | db.users.find({}, {"username" : 1, "email" : 1}) |
上面代码的意思是找到USERS里所有的数据里的username和email这两种KEY所对应的数据。
USERS里可能还有其它的key比如:gender,address什么的,但某些情况下我们只需要其中的一部分。
每次用find或是findOne的时候,它总是会返回_id这个key-value,不想让它返回这个怎么办,这样做
| 1 | db.users.find({}, {"username" : 1, "_id" : 0}) #这时候就只会返回username了,不会返回_id了 |
"$lt", "$lte", "$gt", "$gte" 这四个分别代表 <, <=,>, >=,它们是用来设置查询范围的,比如
| 1 | db.users.find({"age" : {"$gte" : 18, "$lte" : 30}}) #找出年龄在18<=age<=30之间的user |
$ne在这里的用法,即是不等于的意思
| 1 | db.users.find({"username" : {"$ne" : "joe"}}) #找出所有名字不是JOE的user |
$in,后接个数组,表范围
| 1 | db.raffle.find({"ticket_no" : {"$in" : [725, 542, 390]}}) #找出ticket_no为725或542或390的 |
数组里的数据类型可是不一样的
| 1 | db.users.find({"user_id" : {"$in" : [12345, "joe"]}) #找出user_id等于12345或是joe的人 |
与$in相反的就是$uin,用法一样
| 1 | db.raffle.find({"ticket_no" : {"$nin" : [725, 542, 390]}}) #找出ticket_no不是725,542,390的所有人 |
$or,$in或$uin的缺点就是只能用一个KEY作为条件,而是$or则可以多个
| 1 | db.raffle.find({"$or" : [{"ticket_no" : 725}, {"winner" : true}]}) #两个条件满足其一即可 |
可以将$or和$in结合起来一起用
| 1 | db.raffle.find({"$or" : [{"ticket_no" : {"$in" : [725, 542, 390]}},{"winner" : true}]}) |
$mod,如其名所表达的意思一样,求余数
| 1 | db.users.find({"id_num" : {"$mod" : [5, 1]}}) #条件为id_num%5=1,满足条件的有1,6,11,16等 |
$not,取反运算
| 1 | db.users.find({"id_num" : {"$not" : {"$mod" : [5, 1]}}}) #条件为id_num%5!=1.满足技术优势的有2,3,4,5,7等 |
$exists,用来确保KEY的存在
| 1 | db.c.find({"z" : {"$in" : [null], "$exists" : true}}) #这样就不会返回例如{y:null}这样KEY不是Z的数据了 |
可以用正则作为value查询值
| 1 | db.users.find({"name" : /joe/i}) | |
| 2 | db.users.find({"name" : /joey?/i}) | |
$all,即满足所有条件
| 1 | db.food.find({fruit : {$all : ["apple", "banana"]}}) #水果里必须同时有苹果和香蕉 |
$size
| 1 | db.food.find({"fruit" : {"$size" : 3}}) #fruit的size必须是3 |
$slice
| 1 | db.blog.posts.findOne(criteria, {"comments" : {"$slice" : 10}}) #前10条 |
| 2 | db.blog.posts.findOne(criteria, {"comments" : {"$slice" : -10}}) #后10条 |
| 3 | db.blog.posts.findOne(criteria, {"comments" : {"$slice" : [23, 10]}}) #以offset为23开始的10条 |
对于如下数据
| 1 | { | |
| 2 | "name" : { | |
| 3 | "first" : "Joe", | |
| 4 | "last" : "Schmoe"}, | |
| 5 | "age" : 45 | |
| 6 | } | |
可以这样来find,好处就是如果name里再加了个middle的属性,这条find还是有效的
| 1 | db.people.find({"name.first" : "Joe", "name.last" : "Schmoe"}) #对document里的document操作 |
如果是这样的find就不行了
| 1 | db.people.find({"name" : {"first" : "Joe", "last" : "Schmoe"}}) #如果更改了结构,加了个middle,就不能匹配 |
$elemMatch,用来匹配document里的document的,即embedded document,假设如下数据
| 01 | { | |
| 02 | "content" : "...", | |
| 03 | "comments" : [ | |
| 04 | { | |
| 05 | "author" : "joe", | |
| 06 | "score" : 3, | |
| 07 | "comment" : "nice post" | |
| 08 | }, | |
| 09 | { | |
| 10 | "author" : "mary", | |
| 11 | "score" : 6, | |
| 12 | "comment" : "terrible post" | |
| 13 | }] | |
| 14 | } | |
现在我们要找出作者是joe,score大于等于5的comment,就可以用$elemMatch
| 1 | db.blog.find({"comments" : {"$elemMatch" : {"author" : "joe","score" : {"$gte" : 5}}}}) |
$where,用指定的javascript代码来做查询,先插入两条数据
| 1 | db.foo.insert({"apple" : 1, "banana" : 6, "peach" : 3}) | |
| 2 | db.foo.insert({"apple" : 8, "spinach" : 4, "watermelon" : 4}) | |
然后我们找出其中值相等的的数据,这里就是spinach和watermelon,因为它们的值都是4
| 01 | db.foo.find({"$where" : function () { | |
| 02 | for (var current in this) { | |
| 03 | for (var other in this) { | |
| 04 | if (current != other && this[current] == this[other]) { | |
| 05 | return true; | |
| 06 | } | |
| 07 | } | |
| 08 | } | |
| 09 | return false; | |
| 10 | }}) | |
| 11 | #如果函数返回真,则这条document就会被作为查询结果返回回来 | |
| 12 | #非特殊情况下还是不要用这种方式来查询,因为效率很低,因为每个document都会通过$where被转成BSON格式, | |
| 13 | #交给javasript处理,然后再回到$where语句这里。 |
现在介绍下cursor,先插入100条数据,然后查询
| 1 | for(i=0; i<100; i++) { | |
| 2 | db.c.insert({x : i}); | |
| 3 | } | |
| 4 | var cursor = db.collection.find() | |
然后你就可以这样来操作结果集了
| 1 | while (cursor.hasNext()) { #hasNext用来判断还有没有数据 | |
| 2 | obj = cursor.next(); #next获得一条数据,然后当前的位置加1 | |
| 3 | do stuff #你要进行的操作 | |
| 4 | } | |
cursor同样也实现的迭代功能,即forEach
| 1 | var cursor = db.people.find(); | |
| 2 | cursor.forEach(function(x) { | |
| 3 | print(x.name); | |
| 4 | }); | |
组合用法,例如下面就是分页的一例子
| 1 | var cursor = db.foo.find().sort({"x" : 1}).limit(1).skip(10); #sort,排序,1为升序,-1为降序 |
| 2 | var cursor = db.foo.find().limit(1).sort({"x" : 1}).skip(10); #limit,限制返回数量 |
| 3 | var cursor = db.foo.find().skip(10).limit(1).sort({"x" : 1}); #skip,偏移量 |
| 4 | #以上只是建立好了查询语句,并没有真正向服务器提交查询,只有当调用了hasNext方法,查询才会被提交到服务器, |
| 5 | # 但是服务器并不回一次性将所有的查询结果一次返回,而是根据你调用next,hasNext方法 | |
| 6 | #每调用一次就返回一批数据,直到全部数据都返回 | |
这里要说明下用skip来分页的方法效率很低,应该用其它给的办法代替,比如说先得到前100条数据,然后用第100条数据里的某一属性做为查询条件,得到剩下的数据,然后再limit(100)。代码如下
| 1 | var page1 = db.foo.find().sort({"date" : -1}).limit(100) | |
| 2 | var latest = null; | |
| 3 | while (page1.hasNext()) { |
| 4 | latest = page1.next(); |
| 5 | display(latest); | |
| 6 | } | |
| 7 | // 用第100数据里的date做为查询条件,得到剩下的数据 | |
| 8 | var page2 = db.foo.find({"date" : {"$gt" : latest.date}}); | |
| 9 | page2.sort({"date" : -1}).limit(100); |
-
管理
索引index,注意不要为每个KEY都加上index,这样虽然查询快,但是插入的话速度就大大降低了
| 01 | //对people中的username建立索引,多个索引用逗号隔开,1或-1表示建立索引的顺序 | |
| 02 | db.people.ensureIndex({"username" : 1}) | |
| 03 | //对embedded document建立索引的方式 | |
| 04 | db.blog.ensureIndex({"comments.date" : 1}) | |
| 05 | //索引名是由mongodb自动建立的,你也可以指定自己想的名字,只需要传入第二个参数 |
| 06 | db.foo.ensureIndex({"a" : 1, "b" : 1, "c" : 1, ..., "z" : 1}, {"name" : "alphabet"}) |
| 07 | //unique索引,就像是关系数据库里的主键一样,值是唯一的 | |
| 08 | db.people.ensureIndex({"username" : 1}, {"unique" : true}) //现在username的值就是唯一的了 | |
| 09 | //dropIndex,用来删除索引的 | |
| 10 | db.runCommand({"dropIndexes" : "foo", "index" : "alphabet"}) | |
| 11 | //改变索引时,最长加上一个参数,即background:true,正如字面说的那在,在后台进行,不会影响当前活动的任何操作 |
| 12 | //如果是false的话,那么就会block任何对该collection的操作,直到新的索引建立完成 |
| 13 | db.people.ensureIndex({"username" : 1}, {"background" : true}) |
其实有的时候根本就不需要索引,例如你每次都要从collection中提取一半以上的数据,这个时候不用index速度更快。
使用sort时,最是对已经建立好了索引的KEY进行sort。
关于建立索引必须思考的几个问题:
弄清楚要查询的东西,为优化查询而建立索引。
建立索引时要搞清楚索引的排序(升还是降)。
你的数据库结构以后会不会拓展?是否要改变现有的索引结构。
| 1 | //这里多了个dropDups,意思是把重复的username给删掉,这是为了解决防止在建立索引之前就已经存在值重复而用的方法 |
| 2 | db.people.ensureIndex({"username" : 1}, {"unique" : true, "dropDups" : true} |
| 3 | //复合索引,就是将两对KEY-VALUE作为索引 | |
| 4 | {files_id :ObjectId("4b23c3ca7525f35f94b60a2d"),n : 1} | |
explain,可以用来查看mongodb在作查询的一些信息,比如说它用的是哪一个索引
| 1 | db.c.find({age : {$gt : 10}, username : "sally"}).explain() |
hint,如果你用explain发现mongodb用的索引不是你想用的,你可以强制使用自己想要的那个
| 1 | db.c.find({"age" : 14, "username" : /.*/}).hint({"username" : 1, "age" : 1}) |
aggregation
| 1 | db.foo.count() //计算总共有多少条数据 |
| 2 | db.foo.count({"x" : 1}) //按条件计算 |
| 3 | db.runCommand({"distinct" : "people", "key" : "age"}) //返回所有age不一样people |
authenticate
| 1 | use dbname | |
| 2 | dbname.addUser("username","pwd") | |
| 3 | dbname.addUser("username","pwd",true) //加了true就代表该用户只有只读权限 | |
| 4 | //在启动mongodb的时候加上--auth参数就可以启动用户验证 | |
| 5 | //删除用户 | |
| 6 | use admin | |
| 7 | db.system.users.remove("user":"the name of the user you want to remove") |
数据备份和还原
| 01 | $ ./mongodump -d test -o backup //-d指定要备份的数据库,-o指定保存备份数据库的文件夹 | |
| 02 | connected to: 127.0.0.1 | |
| 03 | DATABASE: test | |
| 04 | to | |
| 05 | backup/test | |
| 06 | test.x to backup/test/x.bson | |
| 07 | 1 objects | |
| 08 | $ ./mongorestore -d foo --drop backup/test/ // --drop 还原之前先清空数据库 | |
| 09 | connected to: 127.0.0.1 | |
| 10 | backup/test/x.bson | |
| 11 | going into namespace [foo.x] | |
| 12 | dropping | |
| 13 | 1 objects |
fsync,lock
| 1 | //锁定数据库,使这不能进行任何write,并强制mongodb将所有内存中的数据flush到disk上 | |
| 2 | db.runCommand({"fsync" : 1, "lock" : 1}) | |
| 3 | //解锁 | |
| 4 | db.$cmd.sys.unlock.findOne() | |
| 5 | //测试下是否已经解锁了 | |
| 6 | db.currentOp() | |
修复数据库
| 1 | //在启动mongodb服务时,加上--repair参数 | |
| 2 | mongod --repair | |
| 3 | //或是 | |
| 4 | use somedb | |
| 5 | db.repairDatabase() |
master-slave方式备份
| 01 | //启动一个master,端口号为10000 | |
| 02 | $ mkdir -p ~/dbs/master | |
| 03 | $ ./mongod --dbpath ~/dbs/master --port 10000 --master | |
| 04 | //启动一个slave,端口号为10001,数据源地址(即master)为localhost:10000 | |
| 05 | $ mkdir -p ~/dbs/slave | |
| 06 | $ ./mongod --dbpath ~/dbs/slave --port 10001 --slave --source localhost:10000 | |
| 07 |
| |
| 08 | //其它的可选项还有 | |
| 09 | --only //设置只对某一个数据库备份,而不是整个文件夹 | |
| 10 | --slavedelay //设置备份延迟 | |
| 11 |
| |
| 12 | //可以在启动slave时,不设定它的source | |
| 13 | $ ./mongod --slave --dbpath ~/dbs/slave --port 27018 | |
| 14 | //而后在shell里指定 | |
| 15 | use local | |
| 16 | db.sources.insert({"host" : "localhost:27017"}) | |
| 17 | //也可以修改 | |
| 18 | db.sources.insert({"host" : "prod.example.com:27017"}) //现在的source | |
| 19 | db.sources.remove({"host" : "localhost:27017"}) //移除以前的 |
最后
以上就是高高鞋垫最近收集整理的关于MongoDB笔记的全部内容,更多相关MongoDB笔记内容请搜索靠谱客的其他文章。
发表评论 取消回复