您的当前位置:首页正文

mongo db笔记

2024-11-13 来源:个人技术集锦

 在MongoDB中,要操作一个表并不需要先创建它,可以直接往集合中插入数据,如果集合不存在,会自动创建这个集合。

db.testcollection.insert({"database":"no_sql"}) //集合相当于一张表;

Config:当MONGO用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。

体系结构:

Admin:从权限角度来看,这是ROOT数据库,在ADMIN数据库中添加的用户会自动继承所有数据库权限,一些特定的服务器端命令也只能从这个数据库运行,如列出所有的数据库,关闭服务器。

Local:该数据库永远不会被复制,可以用来存储限于本地单台服务器的任意集合;

Config:当MongoDB用于分片设置时,config数据库在内部使用。用于保存分片的相关信息;

每个数据库文件最大2GB,

文档中的KEY类似于数据库中的字段,下划线_开头的键是保留的,$和.有特殊意义。不能有空格;键可以是任意UTF8字符;

同一个文档中,键是不能重复的。集合不能以system命名;system.namespaces这个集合保存着所有数据集合的信息;

db.createCollection("persion") #创建一个集合;默认为use test ; db;

GridFS大文件协议;JSON:只有null,bool,数字,字符串,数组和对象;

32位整数在shell中不可用,因为js仅支持64位浮点。所有32位整数会被自动转换;

64位整数:

-----------------------------------------data types-----------

null{"X":null}      bool{"x":true}

32位整数   64位整数   64位浮点{"X":12}shell中的浮点都是这种类型 [mongo的三种数字类型]

字符串     符号       对象ID{"X":ObjectId()} 对象id是文档的12字节的唯一ID

日期{"X":new Date()}

正则{"X":/persioninfo/i}

代码{"x":function(){...}}

二进制数据;

其它类型:最大值,最小值,未定义{"x":undefined}

数组{"x":["a","b","c"]},内嵌文档:{"x":{"name":"zhangshan"}}

--------------------------------------------------------------

typeof Date()  //string

typeof new Date() //object  注意统一:要么对象,要么字串,不然更新,查找不统一的时间,处理起来相就不便了。

 

ObjectId是“_id"的默认类型,MongoDb采用ObjectId,12字节,24位字串。(时间戳,机器码,PID,计数)(pid为进程标识)。

---------------------------------------------------------------

post.comment=[] //增加一个评论属性;空数组;

mongo shell:

personbaseinfo={“name":"tom","age":18}

db.persion.insert(personbaseinfo)

person.age = 20 //改上一个

db.persion.findOne()

db.person.update({"name":"tom"},personbaseinfo) //前一个为查询条件,后一个为更新的变量;

db.person.remove({"name":"tom"}) //删除,根据一个条件;

进行插入更新操作前,先把数据保存在变量中好些:

person = {"name":"zhanogshangfeng","age":8};

db.person.insert(person);

db.

MongoDB在执行插入时,首先会将插入的数据转换成BSON格式,然后MongoDb数据库会对BSON进行解析,并检查是否存在_id键。单文档不能超过16M。查看文档的大小,用 Object.bsonsize(doc)查看;

mongoimport:数据导入工具;

组织集合的一种贯例是使用 . 分隔不同命令空间的子集合,blog.posts.为了使组织结构更清晰;

-----------------------------------------------

find自动显示20个文档;

db.personlinfo.find()//先查询;

p = {"name":"zhangshang","age":8}

db.personinfo.insert(p);

 

插入原理:先转BSON,对BSON进行解析,并检查是否存在_id键。

启动时加 --objcheck 在插入之前先检查文档的有效性;

删除: db.p.remove() , db.p.drop() //第二个连索引都删了;

person.findOne()和FIND,一个是临时赋值,一个是可以永久使用;

更新: 1.先 var p = db.person.findOne();

而后更改: p.*,p.username =

     delete p.name =

然后改完变量后,更新:

3.db.person.update({"name":"tom"},p) //更新条件为name=tom

改变整个结果:更新的方法:1.先findOne(),赋值,而后.***属性赋值;而后再delete *.per*删除多余的不要的,最后.update({条件},{刚才的}),从而完成更新;

修改器: {"$inc":{"age":1}}

{"$set":{"address":"816 park street"}}  $set修改器用来修改指定一个键的值;如果键值不存在,则自动创建它;甚至可以修改数据类型;

用$set修改器设置内嵌文档: {"$set":{"address.street":"822 street.}}//修改address内嵌文档

注意这里的区别:address.street 中间有点;

"age":9,“address":{"city":"beijing","street":"8267 park street"}

$inc修改器只用于整数,长整或双精度浮点数。

数组修改器:数组操作只能在值为数组的键上。$push操作指定的键,如果键(键的值为数组)存在,会在己有的数组末尾加入一个元素,如果键不存在就会创建一个新的数组;

{$push:{"favbook":"MongoDB"}}

"favbook":["mongoDB"]} //#如果键存在,则加,["Mongdo","hBase"]

还可以使用$ne来判断一个值是否在数组中,如果不在,则添加;

.update({"favbook":{"$ne":"adv math"}},{$push:{"favbook":"adv Math"}})

{"$addToSet":{"email":"xxx"}}  //存在就不加,不存在则添加进去;

一次添加多个邮箱信息:

db.personinfo.update(,{"$addToSet":{"emails":{"$each":{"@163.com","@yahoo.com","qq@.com"}}}})

{"$pop":{"emails":1}}出栈,尾删

{"$pop":{"emails":-1}}开头删。

{"$pull":{"emails":"xhang@163.com"}} //删除163.com的邮箱;

数组的下标表示: {"emails.0":"xxx"} //用于set...等数组操作;

$定位器:{"$set":{"address.$.street":"571 park street"}} 用来替代address.1.street定位的

update()的第三个参数upsert为真,有更新,没有则创建;代表是否查询不到的创建;

save()是一个shell函数,可以在文档不存在时插入,存在时更新,save()函数只有一个参数,参数为文档。

第四个参数:修改多个文档;修改的结果为多行,一定要把第四个多行打开;想要知道影响的记录条目数,调用 db.runCommand({getLastError:1})   n表示条数;

find不带参数查询所有的文档,第一个参数指定了要返回哪些文档;就是所谓的查询条件;

db.person.find({"age":20}),第二个字段指定字段

db.person.find({},{"age":1})

 

db.personinfo.update({"favbook":{"$ne":"Math"}},{$push:{"favbook":"Math"}}) //第一个为查询条件,第二为没有则加,$addToSet完成同样的功能,没有则追加;

.limit().sort({})

ps = db.runCommand({"findAndModify...

 

db.person.find({"age":{"$gt":20}})  查询年龄大于20的

in只需要满足()内的某一个值即可,$all必须满足[]内的所有值;

{"$all":["tiger","horse"]}

db.animal.find({"animal":["tiger","lion","horse","elephant"]})

由于mongodb反范式,则可以通过$exists查是否存在某字段;

db.person.find({"age":{"$exists":true}})

db.person.find({"age":{"$in":k[null],"$exists":true}) //查询age为null的文档;

{"$mod":[8,1]} //取余8余1的,比如9,,则为真

$ne不等于:{"age":{"$ne":9}}

$in 包含  {"age":{"$in":[8,9]}}

$nin 不包含  {"age":{"$nin":[8,9]}}

$size 数组长度{{"age":{"$size":3}}}

.find({"name":/s/}) //查询名字中带S的正则查询;

db.person.find({"$where":"this.age>20"})

定义一个函数:

    f = function () { return this.age > 20}

    而后

    db.person.find(f)

 

{"$where":"this.x+this.y==9"})

this.代指当前文档对象;

count查询记录条数;

db.person.find.count()

 

db.person.find().skip(2).limit(5).count(true) //显示实际的记录数;

find().sort({"age":1,"age":-1})

使用skip()略过少量的文档速度可以,但如果过多,就会慢;

db.runCommand({"distinct":"person","key":"age"}) 去重;

group()分组

定义函数: $keyf

var cursor = db.test.find() //获取游标

while(cursor.hasNext()){

... printjson(cursor.next())

... }

forEach接口:

cursor.forEach(function(obj){

    print(obj.x)

})

存储过程:db.system.js

db.system.js.save({"_id":"addNum",

    value:function(x,y){

        reuturn x+y

}})

调用存储过程:db.eval('addNum(6,52)')

db.eval(function(){return 6+2;

 

db.createCollection("mywebLog",{"capped":true,"size":1000000,”max“:100,"autoIndexId":true})  //创建固定集合,限制集合的个数;

db.mywebLog.validate()

 

每种编程语言表示文档的方法不一样:JS中,文档被表示为对象:

 

{"greeting":"hello,world!"}

gteeting称为键;包含键/值对;

子集合:使用子集合来组织数据非常高效。

 

db   查看当前使用的数据库; use db

在JS中,Date日期应使用new Date()

 

mongo --nodb  启动时不连接DB数据库

------------------------------------

conn = new Mongo("some-host:30000")

db = conn.getDB('myweb')

在shell中连接

------------------------------------

在shell中执行脚本:

mongo script.js script2.js

 

 

后再跟上脚本名称: mongo --quiet server-1:3000/foo script.js

 

script2.js script.js

load("script.js") //在交互环境中加载.js

-----------------------------------------

use foo ==== db.getSisterDB('blog')

show dbs   =======   db.getMongo()

show collections     db.getCollectionNames()

------------------------------------------

typeof 函数

------------------------------------------

db.getCollection('version');

------------------------------------------

var x = {y:2}

x.y  //2

x['y']  //2  语法上相等;

------------------------------------------

db.foo.batchInsert([{},{}]) //批量插入,接受一个文档数组参数

------------------------------------------

new Date().getTime() //产生一个时间戳;13位 1520411478760

替换文档: 先findOne()给变量,然后p.属性={}文档;

然后 delete p.属性

相当于修改好变量的值,然后 update({条件},p); //一次更新;

 

原子性的修改器:更新器是种特殊的键,用来指定复杂的更新操作;

$Set 用来指定一个字段的值,如果不存在,则创建;也可以改变类型,也可

 

以用$unset删除;

$push 向己有数组末尾加入一个元素;没有就创建新;

db.blog.posts.update({"title":"a blog",{"$push":{"comments":

 

{"name":"joe","...}

$pop {"key"-1}

$pull 按特定条件删

db.raffle.find({"$or":[{"ticket_no"725},{"winner":true}]})//

 

{"$or":[{},{}]}//or后面接数组;

{"$mod":[5,2]}

{"$not":}

"$in":[4,5]}

"$exists":true

.find({"name":/joe/i})

{$all:["apple","banna"]}

db.food.find({"fruit.2":"peach"})

 

findOne(criteria,{"comments":{"$slice":10}}) //返回10条评论;

$elemMatch 不会匹配非数组元素;{"$elemMatch":10,"$lt":20}

while(cursor.hasNext()){

    obj = cursor.next();

}

var cursor = db.blog.find();

cursor.forEach(function(x){

    print(x.name);

});

 

db.blog.find()._addSpecial("$maxscan",20)

 

在多个键上建立的索引就是复合索引。只有在基于多键排序时,方向才变得

 

重要;

覆盖索引:当一个索引包含用户请求的所有字段,可以认为这个索引覆盖了

 

本次查询。

如果在覆盖索引上执行explain(),"indexOnly"字段的值要为true;

 

db.entries.find({"created_at":{"$lt":hourAge}}).hint({$natural":1})

用{"$natural":1}强制做数据库全表扫描。它可以指定文档按照磁盘上的顺

 

序排列。

如果要得到最新的(最早的)文档,这个就很好用。

唯一性索引: db.users.ensureIndex({"username":1},{"unique":true});

己有文档去重:db.users.ensureIndex({"age":1},{"unique":true})//

sparse index 稀疏索引{"unique":true,"sparse":true}

hint()强制进行全表扫描;

所有的数据库索引信息存在system.indexes集合中。这是保留集合,不能插

 

入或删除,只能通过ensureIndex或dropIndexes对其操作;

db.person.getIndexes() 查看给定集合上的所有索引信息;

固定集合不能被分片

db.runCommand({"convertToCapped":"test","size":10000})//普通集合转

 

成固定集合;

 

自然排序:

db.person.find().sort({"$natural":1});

没有_id索引的集合,创建集合时指定autoIndexId选项为false,创建集合时

 

就不会自动在_id上创建索引。

db.runCommand({text:"hn",search:"\"ask hn \" ipod})

db.blog.ensureIndex({"date":1,"post":"text"})

 

mongofiles put foo.txt  //存储

mongofiles get foo.txt  // 取出

list,search,delete 一共5种操作;

--------------------------------------

PyMongo

from pymongo import Connection

import gridfs

db = Connection().test

fs = gridfs.GridFS(db)

file_id = fs.put("hello world", filename = "foo.txt")

fs.list()

 

fs.get(file_id).read()

-----------------------------------------

db.fs.files.distinct("filename")

 

$matc用于对文档集合进行筛选,之后就可以在筛选得到的文档子集上做聚合

 

 

db.runCommand({"distinct":"people","key":"age"})

 

客户端不能在备份节点执行写操作,默认情况下,客户端不能从备份节点中

 

读取数据,在备份节点上显示地执行setSlaveOk之后,客户端就可以从备份

 

节点中读取数据了。

 

mongod --replSet spock -f mongod.conf --fork   // --replSet 后跟

 

name ;

mongoDB中,每个集合最多可以创建64个索引,它支持能在RDBMS中找到各种

 

索引,升,降,唯一,复合键索引,地理空音索引被支持,mongodB的二级索

 

引用B树b-tree实现。

副本集:如果一个读密集型的应用,可以把数据库读操作分散到副本集集群

 

的各台机器上。

副本集的从节点是只读的;副本集支持自动故障转移。

对于高容量,低价值的数据,fire-and-forget风格的写操作是很理想的选择

 

提升单一的硬件来进行扩展称为垂直扩展或向上扩展。、

水平扩展是将数据库分布到多台机器上。

mongod服务器进程使用一个自定义二进制协议从网络套接字上接收命令。

 

mongodump,mongorestore 备份和恢复;

mongoexport,mongoimport导入导出json,csv,tsv数据

mongosniff 观察发送到数据库的操作;把网络上传输的bson转换为易于人们

 

阅读的shell语句。

mongostat 持续轮询mongodb和系统以便提供有帮助的统计信息。

bsondump,mongofiles;

$push,$addToSet这两个操作都是向数组添加一个元素,但后者会保证唯一性

 

,防重复。

 

db.numbers.ensureindex({name:1}); //创建索引

db.numbers.getIndexes() //查看索引

 

db.stats()

 

db.runCommand({dbstats:1})

db.runCommand({collstats:'numbers'})

 

db.products.ensureIndex({sulg:1},{unique:true})

查询指定产品的分类:

db.categories.find({_id:{$in:product['category_ids']}});

 

db.blog.find({num:{$in:[2,3,5]}})

 

文件大小上限,2GB。

 

插入文件就创建了集合,但是有各种各样的集合,可能执行

 

db.createCollection("users")创建集合。db.createCollection("users",

 

{sze:20000})

 

db.products.renameCollection("store_products") //重命名集合;

 

db.user.actions.find().sort({"$natural":-1});

 

bson规定了三种数字类型,double,int,long,也就是说bson可以编码各种

 

ieee浮点数值,以及各种8字节以内的带符号的整数;js天生支持一种Number

 

,等于ieee的双精浮点;如果要在shell中将一个数字保存为整数,需要使用

 

NumberLong(),NumberInt();

db.number.save({n:5});

db.number.save({n:NumberLong(5)});

db.number.find({n:{$type:1}}) //根据类型来查

db.number.find({n:{$type:18}) //

 

mongodb.v2.0的bson文档限制大小16M。

find()返回的是游标对象,而findOne()返回的是一个文档。仅仅要一个文档

 

,就用findOne(),如果需要返回多个文档,就需要使用find(),该方法会返回

 

一个游标,你需要进行迭代。

 

db.users.find({last_name:/^Ba/}) //查询以Ba开头的名字

 

db.orders.find({'line_items.sku':1,'purcharse_date':{$gte:new Date

 

(2009,0,1)}})

 

.toArray().map(function(doc){ return doc['user_id']})

 

.toArray()  //返回数组; [{},{}]

------------------------------------------------------------------

db.blog.find({num:{$lt:4}}).map(function(doc){ return doc['num']})

[ 0, 1, 2, 3 ] //doc就是当前的文档.然后输出'num字段

------------------------------------------------------------------

{$in:[x,x,x,x]},{$nin:['balck','blue']}

{$in:[x,x,x,x]},{$all:['balck','blue']}

{$or:['balck','blue']}

{$ne:'greeting'}

布尔操作:ne,not,and,exists

db.user.find(last_name:{$not:/^B})

db.user.find({address:{$eleMatch:{name:'home',state:'NY'}})

要将多个条件限制在同一个子文档上,可以用$elemMatch操作符;需要匹配

 

子文档的多个属性才用。

db.user.find({addresses:{$size:3}})

 

db.blog.find({$where:"function(){ return this.num<4;}"})  //$where

 

函数;

 

db.reviews.find({user_id:ObjectId("****"),text:/best|worst/i}) //查

 

指定用户评论中含best,worst单词的。

db.user.find({_id:{$type:2}}) //找出id类型为字符串的文档;

 

投影:约束结果集;

db.products.find({},{reviews:{$slice:12}})  //返回头12篇评论

db.products.find({},{reviews:{$slice:-5}})  //返回倒数第5篇

db.products.find({},{reviews:{$slice:[24,12]}})  //跳过24,返回12

 

db.products.find({},{reviews:{$slice:[24,12]}},'_id':1)//显示12篇

 

评论及id;

 

group最少需要三个参数:第一个key,定义如何对数据进行分组,第二个参数

 

是对结果做聚合的js函数,叫reduce函数,第三个分组参数是reduce函数的

 

初始文档;

results = db.reviews.group({

key:{user_id:true},

initial:{reviews:0,votes:0.0},

reduce:function(doc,aggregator){

    aggregator.reviews+=1;

    aggregator.votes +=doc.votes;

}

finalize:function(doc){

    doc.average = doc.votes/doc.reviews;

}

})

finalizer终结器:在group命令返回前应用于每个分组结果上。

 

map = function(){

    var shipping_month = this.purchase_date.getMonth

 

()+'-'+this.purchase_data.getFullYear();

var items = 0;

this.line_items.forEach(function(item){

items+=item.quantity;

});

emit(shipping_month,

 

{order_total:this.sub_total,items_total:items});

//说明:this总是指向正在迭代的文档,随后调用emit(),这是每个映射函

 

数必须要调用的特殊方法,emit()第一个参数是分组依据的键,第二个通常

 

是包含要执行reduce的值的文档。

 

reduce ,用于执行聚合的JS函数,该函数接受两个参数,正被迭代的当前文

 

档和用于存储聚合结果的聚合器文档。reduce函数不返回任何内容,只不过

 

修改聚合器对象。

finalize:在返回结果集之前应用每个结果文档的JS函数,该函数支持对分组

 

操作的结果进行后置处理。

比较时间: filter = {purchase_date :{$gte: new Date(2010,1,1)}}

 

db.blog.distinct("tags") //去重

db.blog.distinct("tags",category_id:12) //去重,显示分类去重;

 

group,distinct数据库命令,结果集受16M数据库大小的影响。

group不会处理多于1万个唯一键;

 

-------------------------------------------------

db.collection.mapReduce(

   function() {emit(key,value);},  //map 函数

   function(key,values) {return reduceFunction},   //reduce 函数

   {

      out: collection,

      query: document,

      sort: document,

      limit: number

   }

)

 

-------------------------------

    map :映射函数 (生成键值对序列,作为 reduce 函数参数)。

    reduce 统计函数,reduce函数的任务就是将key-values变成key-value

 

,也就是把values数组变成一个单一的值value。。

    out 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删

 

除)。

    query 一个筛选条件,只有满足条件的文档才会调用map函数。(query

 

。limit,sort可以随意组合)

    sort 和limit结合的sort排序参数(也是在发往map函数前给文档排序)

 

,可以优化分组机制

    limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort

 

的用处不大)

 

-------------------------------

 

每种编程语言表示文档的方法不一样:JS中,文档被表示为对象: {"greeting":"hello,world!"}

gteeting称为键;包含键/值对;

子集合:使用子集合来组织数据非常高效。

 

db   查看当前使用的数据库; use db

在JS中,Date日期应使用new Date()

 

mongo --nodb  启动时不连接DB数据库

------------------------------------

conn = new Mongo("some-host:30000")

db = conn.getDB('myweb')

在shell中连接

------------------------------------

在shell中执行脚本:

mongo script.js script2.js

 

load("script.js") //在交互环境中加载.js

-----------------------------------------

use foo ==== db.getSisterDB('blog')

show dbs   =======   db.getMongo()

show collections     db.getCollectionNames()

------------------------------------------

typeof 函数

------------------------------------------

db.getCollection('version');

------------------------------------------

var x = {y:2}

x.y  //2

x['y']  //2  语法上相等;

------------------------------------------

db.foo.batchInsert([{},{}]) //批量插入,接受一个文档数组参数

------------------------------------------

new Date().getTime() //产生一个时间戳;13位 1520411478760

替换文档: 先findOne()给变量,然后p.属性={}文档;

然后 delete p.属性

相当于修改好变量的值,然后 update({条件},p); //一次更新;

 

原子性的修改器:更新器是种特殊的键,用来指定复杂的更新操作;

$Set 用来指定一个字段的值,如果不存在,则创建;也可以改变类型,也可以用$unset删除;

$push 向己有数组末尾加入一个元素;没有就创建新;

db.blog.posts.update({"title":"a blog",{"$push":{"comments":{"name":"joe","...}

$pop {"key"-1}

$pull 按特定条件删

db.raffle.find({"$or":[{"ticket_no"725},{"winner":true}]})//{"$or":[{},{}]}//or后面接数组;

{"$mod":[5,2]}

{"$not":}

"$in":[4,5]}

"$exists":true

.find({"name":/joe/i})

{$all:["apple","banna"]}

db.food.find({"fruit.2":"peach"})

 

findOne(criteria,{"comments":{"$slice":10}}) //返回10条评论;

$elemMatch 不会匹配非数组元素;{"$elemMatch":10,"$lt":20}

while(cursor.hasNext()){

    obj = cursor.next();

}

var cursor = db.blog.find();

cursor.forEach(function(x){

    print(x.name);

});

 

db.blog.find()._addSpecial("$maxscan",20)

 

在多个键上建立的索引就是复合索引。只有在基于多键排序时,方向才变得重要;

覆盖索引:当一个索引包含用户请求的所有字段,可以认为这个索引覆盖了本次查询。

如果在覆盖索引上执行explain(),"indexOnly"字段的值要为true;

 

db.entries.find({"created_at":{"$lt":hourAge}}).hint({$natural":1})

用{"$natural":1}强制做数据库全表扫描。它可以指定文档按照磁盘上的顺序排列。

如果要得到最新的(最早的)文档,这个就很好用。

唯一性索引: db.users.ensureIndex({"username":1},{"unique":true});

己有文档去重:db.users.ensureIndex({"age":1},{"unique":true})//

sparse index 稀疏索引{"unique":true,"sparse":true}

hint()强制进行全表扫描;

所有的数据库索引信息存在system.indexes集合中。这是保留集合,不能插入或删除,只能通过ensureIndex或dropIndexes对其操作;

db.person.getIndexes() 查看给定集合上的所有索引信息;

固定集合不能被分片

db.runCommand({"convertToCapped":"test","size":10000})//普通集合转成固定集合;

 

自然排序:

db.person.find().sort({"$natural":1});

没有_id索引的集合,创建集合时指定autoIndexId选项为false,创建集合时就不会自动在_id上创建索引。

db.runCommand({text:"hn",search:"\"ask hn \" ipod})

db.blog.ensureIndex({"date":1,"post":"text"})

 

mongofiles put foo.txt  //存储

mongofiles get foo.txt  // 取出

list,search,delete 一共5种操作;

--------------------------------------

PyMongo

from pymongo import Connection

import gridfs

db = Connection().test

fs = gridfs.GridFS(db)

file_id = fs.put("hello world", filename = "foo.txt")

fs.list()

 

fs.get(file_id).read()

-----------------------------------------

db.fs.files.distinct("filename")

 

$matc用于对文档集合进行筛选,之后就可以在筛选得到的文档子集上做聚合。

 

db.runCommand({"distinct":"people","key":"age"})

 

客户端不能在备份节点执行写操作,默认情况下,客户端不能从备份节点中读取数据,在备份节点上显示地执行setSlaveOk之后,客户端就可以从备份节点中读取数据了。

 

mongod --replSet spock -f mongod.conf --fork   // --replSet 后跟name ;

mongoDB中,每个集合最多可以创建64个索引,它支持能在RDBMS中找到各种索引,升,降,唯一,复合键索引,地理空音索引被支持,mongodB的二级索引用B树b-tree实现。

副本集:如果一个读密集型的应用,可以把数据库读操作分散到副本集集群的各台机器上。

副本集的从节点是只读的;副本集支持自动故障转移。

对于高容量,低价值的数据,fire-and-forget风格的写操作是很理想的选择。

提升单一的硬件来进行扩展称为垂直扩展或向上扩展。、

水平扩展是将数据库分布到多台机器上。

mongod服务器进程使用一个自定义二进制协议从网络套接字上接收命令。

 

mongodump,mongorestore 备份和恢复;

mongoexport,mongoimport导入导出json,csv,tsv数据

mongosniff 观察发送到数据库的操作;把网络上传输的bson转换为易于人们阅读的shell语句。

mongostat 持续轮询mongodb和系统以便提供有帮助的统计信息。

bsondump,mongofiles;

$push,$addToSet这两个操作都是向数组添加一个元素,但后者会保证唯一性,防重复。

 

db.numbers.ensureindex({name:1}); //创建索引

db.numbers.getIndexes() //查看索引

 

db.stats()

 

db.runCommand({dbstats:1})

db.runCommand({collstats:'numbers'})

 

db.products.ensureIndex({sulg:1},{unique:true})

查询指定产品的分类:

db.categories.find({_id:{$in:product['category_ids']}});

 

db.blog.find({num:{$in:[2,3,5]}})

 

文件大小上限,2GB。

 

插入文件就创建了集合,但是有各种各样的集合,可能执行db.createCollection("users")创建集合。db.createCollection("users",{sze:20000})

 

db.products.renameCollection("store_products") //重命名集合;

 

db.user.actions.find().sort({"$natural":-1});

 

bson规定了三种数字类型,double,int,long,也就是说bson可以编码各种ieee浮点数值,以及各种8字节以内的带符号的整数;js天生支持一种Number ,等于ieee的双精浮点;如果要在shell中将一个数字保存为整数,需要使用NumberLong(),NumberInt();

db.number.save({n:5});

db.number.save({n:NumberLong(5)});

db.number.find({n:{$type:1}}) //根据类型来查

db.number.find({n:{$type:18}) //

 

mongodb.v2.0的bson文档限制大小16M。

find()返回的是游标对象,而findOne()返回的是一个文档。仅仅要一个文档,就用findOne(),如果需要返回多个文档,就需要使用find(),该方法会返回一个游标,你需要进行迭代。

 

db.users.find({last_name:/^Ba/}) //查询以Ba开头的名字

 

db.orders.find({'line_items.sku':1,'purcharse_date':{$gte:new Date(2009,0,1)}})

 

.toArray().map(function(doc){ return doc['user_id']})

 

.toArray()  //返回数组; [{},{}]

------------------------------------------------------------------

db.blog.find({num:{$lt:4}}).map(function(doc){ return doc['num']})

[ 0, 1, 2, 3 ] //doc就是当前的文档.然后输出'num字段

------------------------------------------------------------------

{$in:[x,x,x,x]},{$nin:['balck','blue']}

{$in:[x,x,x,x]},{$all:['balck','blue']}

{$or:['balck','blue']}

{$ne:'greeting'}

布尔操作:ne,not,and,exists

db.user.find(last_name:{$not:/^B})

db.user.find({address:{$eleMatch:{name:'home',state:'NY'}})

要将多个条件限制在同一个子文档上,可以用$elemMatch操作符;需要匹配子文档的多个属性才用。

db.user.find({addresses:{$size:3}})

 

db.blog.find({$where:"function(){ return this.num<4;}"})  //$where函数;

 

db.reviews.find({user_id:ObjectId("****"),text:/best|worst/i}) //查指定用户评论中含best,worst单词的。

db.user.find({_id:{$type:2}}) //找出id类型为字符串的文档;

 

投影:约束结果集;

db.products.find({},{reviews:{$slice:12}})  //返回头12篇评论

db.products.find({},{reviews:{$slice:-5}})  //返回倒数第5篇

db.products.find({},{reviews:{$slice:[24,12]}})  //跳过24,返回12篇

db.products.find({},{reviews:{$slice:[24,12]}},'_id':1)//显示12篇评论及id;

 

group最少需要三个参数:第一个key,定义如何对数据进行分组,第二个参数是对结果做聚合的js函数,叫reduce函数,第三个分组参数是reduce函数的初始文档;

results = db.reviews.group({

key:{user_id:true},

initial:{reviews:0,votes:0.0},

reduce:function(doc,aggregator){

    aggregator.reviews+=1;

    aggregator.votes +=doc.votes;

}

finalize:function(doc){

    doc.average = doc.votes/doc.reviews;

}

})

finalizer终结器:在group命令返回前应用于每个分组结果上。

 

map = function(){

    var shipping_month = this.purchase_date.getMonth()+'-'+this.purchase_data.getFullYear();

var items = 0;

this.line_items.forEach(function(item){

items+=item.quantity;

});

emit(shipping_month,{order_total:this.sub_total,items_total:items});

//说明:this总是指向正在迭代的文档,随后调用emit(),这是每个映射函数必须要调用的特殊方法,emit()第一个参数是分组依据的键,第二个通常是包含要执行reduce的值的文档。

 

reduce ,用于执行聚合的JS函数,该函数接受两个参数,正被迭代的当前文档和用于存储聚合结果的聚合器文档。reduce函数不返回任何内容,只不过修改聚合器对象。

finalize:在返回结果集之前应用每个结果文档的JS函数,该函数支持对分组操作的结果进行后置处理。

比较时间: filter = {purchase_date :{$gte: new Date(2010,1,1)}}

 

db.blog.distinct("tags") //去重

db.blog.distinct("tags",category_id:12) //去重,显示分类去重;

 

group,distinct数据库命令,结果集受16M数据库大小的影响。

group不会处理多于1万个唯一键;

 

-------------------------------------------------

db.collection.mapReduce(

   function() {emit(key,value);},  //map 函数

   function(key,values) {return reduceFunction},   //reduce 函数

   {

      out: collection,

      query: document,

      sort: document,

      limit: number

   }

)

 

-------------------------------

    map :映射函数 (生成键值对序列,作为 reduce 函数参数)。

    reduce 统计函数,reduce函数的任务就是将key-values变成key-value,也就是把values数组变成一个单一的值value。。

    out 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。

    query 一个筛选条件,只有满足条件的文档才会调用map函数。(query。limit,sort可以随意组合)

    sort 和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制

    limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)

-------------------------------

如果有a-b的复合索引,那么仅针对a的索引就是冗余的。复合索引里键的顺序很重要。

 

后台索引:虽然索引构建仍会占用写锁,但构建任务会停下来允许其他读写操作访问数据库。db.values.ensureIndex({open:1,close:1},{background:true}});

db.values.reIndex(); //重建指定集合上的所有索引;

 

开启服务器慢查询剖析器:

use stocks;

db.setProfilingLevel(2)

db.setProfilingLevel(1,50)//禁用将第一个参数设为0,第二个参数为毫秒

 

db.value.getIndexKeys() //列出索引

用explain(true)查看查询计划

 

复制:二种:1.主从;2.副本集;

 

最小的副本集由三个节点组成,二个一等节点,外加一个仲裁节点。

 

---------------------------

创建副本集:

三台机器只是dbpath不同。端口不同;

D:\MongoDB\Server\3.6\bin>mongod --replSet myapp --dbpath c:\data\node1 --port 40000

D:\MongoDB\Server\3.6\bin>mongod --replSet myapp --dbpath c:\data\node1 --port 40001

D:\MongoDB\Server\3.6\bin>mongod --replSet myapp --dbpath c:\data\node1 --port 40002

 

连接到非仲裁节点后运行:

1.rs.initiate()

2.rs.add("localhost:40001")

3.rs.add("localhost:40002",{arbiterOnly:true});

 

db.isMaster()

rs.status()

rs.remove("localhost:4000")

--------------------------------

cfg = rs.conf()

cfg.members[0].host = "你的IP 或者域名"

rs.reconfig(cfg)

--------------------------------

查看日志:

use local

db.oplog.rs.find();

local数据库里保存了所有的副本集元数据和oplog。当然,这个数据库本身不能被复制,正如其名,local数据库里的数据对本地节点而言是唯一的,因此不该复制;

-------------------------

用脚本配置:

config = {_id:"myapp",members:[]}

config.members.push({_id:0,host:'localhost:4000'})

config.members.push({_id:1,host:'localhost:4001'})

config.members.push({_id:2,host:'localhost:4002',arbiterOnly:true})

rs.initiate(config)

-------------------------

db.runCommand({replSetInitiate:config});

rs.slaveOk() //允许副本查询

-------------------------

rs.help() //查看所有的副本集命令;

配置文档选项:

_id,host,arbiterOnly,priority(0-1000),votes,hidden(bool,在isMaster命令生成的响应中不会出现该结点,因为mongodB驱动依赖于isMaster来获取副本集的拓扑情况,隐藏一个成员能避免驱动自动访问它。buildIndexes(bool),slaveDelay()指定从节点迟延的秒数;

tags,getLastErrorDefaults,getLastErrorModes.

 

配置文件:use local;      db.system.replset.find()

配置:

-----------------------------------------

use local;

config = db.system.replset.findOne();

config.members[1].host = "localhost:4000";

rs.reconfig(config)

------

rs.reconfig(config,{force:true}})//强制重新加载配置

-----------------------------------------

连接到从结点的时候,加connection(host,slave_ok=>true)避免无意向从节点进行写操作;

写关注:insert(doc,{w:2,wtimeout:500}) //2台服务器,500毫秒

2.0最多支持12个成员副本集,如果还想更多,则需要分片集群;

数据集标签:每个成员标签文档有两个键值对,第一个标识了数据中心,第二个是指定节点服务器所在机器的名称。

 

分片集群由分片,mongos路由器和配置服务器组成:

 

配置服务器:全局集群配置,每个数据库、集合和特定范围数据的位置,一份变更记录,保存了数据在分片之间进行迁移的历史信息。

 

mongos进程向配置服务器写入时,会使用两阶段提交,这能保证配置服务器之间的一致性,在各种生产环境的分片部署中,必须运行三个配置服务器。

分片是基于范围的,分片集合里的每个文档都必须落在指定键的某个值范围里,mongodb使用的所谓的分片键,让每个文档在这些范围里找到自己的位置。

 

分片配置:

--configsvr

--shardsvr

 

-----------------------------------------脚本

mongod --shardsvr --replSet shard-a --dbpath c:\data\rs-a-1 --port 3000 --logpath c:\data\rs-a-1.log --fork --nojournal

mongod --shardsvr --replSet shard-a --dbpath c:\data\rs-a-2 --port 3001 --logpath c:\data\rs-a-2.log --fork --nojournal

mongod --shardsvr --replSet shard-a --dbpath c:\data\rs-a-3 --port 3002 --logpath c:\data\rs-a-3.log --fork --nojournal

 

mongod --shardsvr --replSet shard-b --dbpath c:\data\rs-b-1 --port 4000 --logpath c:\data\rs-b-1.log --fork --nojournal

mongod --shardsvr --replSet shard-b --dbpath c:\data\rs-b-2 --port 4001 --logpath c:\data\rs-b-2.log

mongod --shardsvr --replSet shard-b --dbpath c:\data\rs-b-3 --port 4002 --logpath c:\data\rs-b-3.log --fork --nojournal

 

 

mongod --configsvr --replSet conf --dbpath c:\data\config-1 --port 27019 --logpath c:\data\config-1.log

mongod --configsvr --replSet conf --dbpath c:\data\config-2 --port 27020 --logpath c:\data\config-2.log

mongod --configsvr --replSet conf --dbpath c:\data\config-3 --port 27021 --logpath c:\data\config-3.log

然后初始化三台配置服务器:

rs.initiate()

rs.add("123") //注意要把3台都加成配置服务器,配置服务器没有仲裁;

配置完成后,查看状态,成功后,才能用 mongos命令;

mongos --configdb conf/localhost:27019,localhost:27020,localhost:27021 --logpath c:\data\mongos.log --port 5000

 

高版本的变化,即配置服务器也要指定副本集,然后mongos要指定conf/localhost:27019 配置集:conf:/localhost:27019,localhost:27029...

 

然后连接mongos的端口,成功后,可见mongs> 提示符;

sh.addShard("shard-a/localhost:3000,localhost:3001")

sh.addShard("shard-b/localhost:4000,localhost:4001")

然后将二个副本集加入分片,注意不要加仲裁节点。后功后即可看到状态;

rs.status()

-------------------------------

mongos> sh.status()

--- Sharding Status ---

  sharding version: {

        "_id" : 1,

        "minCompatibleVersion" : 5,

        "currentVersion" : 6,

        "clusterId" : ObjectId("5aa632424911a3657d360c7f")

  }

  shards:

        {  "_id" : "shard-a",  "host" : "shard-a/localhost:3000,localhost:3001",

  "state" : 1 }

        {  "_id" : "shard-b",  "host" : "shard-b/localhost:4000,localhost:4001",

  "state" : 1 }

  active mongoses:

        "3.6.2" : 1

  autosplit:

        Currently enabled: yes

  balancer:

        Currently enabled:  yes

        Currently running:  no

        Failed balancer rounds in last 5 attempts:  0

        Migration Results for the last 24 hours:

                No recent migrations

  databases:

        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }

-----------------------------------------

查看:

 db.getSiblingDB("config").shards.find()

---------------------------------------------

mongos> db.getSiblingDB("config").shards.find()

{ "_id" : "shard-a", "host" : "shard-a/localhost:3000,localhost:3001", "state" :

 1 }

{ "_id" : "shard-b", "host" : "shard-b/localhost:4000,localhost:4001", "state" :

 1 }

---------------------------------------------

use admin

db.runCommand({listshards:1})

---------------------------------------------

下一部:开启分片;

sh.enableSharding("cloud-docs"); //指定数据库开始分片

 

db.getSiblingDB("config").databases.find()

//在不改变当前数据库的情况下,切换到config数据库下进行查找;

show dbs 为admin,config,必须是同级数据库。且在同一台服务器上。

 

下一步:定义片键:

sh.shardCollection("cloud-docs.blog",{username:1,_id:1}) //把数据库cloud-docs的blog集合的username,_id作为片键分片;

 

db.getSiblingDB("config").collections.find()

 

use config

db.chunks.count()

 

db.chunks.findOne()

 

use config

db.changelog.find() //查看相关的更新日志

 

db.spreasheets.ensureIndex() //开启分片索引;

--------------------------------------------

仲裁节点开销很少,不需要自己的服务器。

副本集的每个成员,无论是副本节点还是仲裁节点,都需要放在不同的机器上

副本集的仲裁节点是很轻量级的,配置服务器也可能共用,硬件要求是配置集群中的配置服务器都必须放在不同的机器上;

-----------

生产环境中,4台服务器:分片A*2,分片B*2,然后把配置服务器分布在这4台中,然后把仲裁服务器分布在AB二台中;

 

数据中心恢复6台:前4台按标准主数据,后二台(一个存分片A,一个分片B),分片A中加载配置服务器;这二台就作为备份服务器;

 

监控:

db.serverStatus(),db.currentOp()

添加分片和开始配置的时候一样的命令;

删除分片:

use admin

db.runCommand({removeshard:shard-1/localhost:3000,localhost:4000});

查看分片:

use config

db.databases.find()

 

db.runCommand({moveprimary:"test",to:"shard-0-test-rs"});

 

分片数据的备份与恢复:

连接到mongos,然后mongodump后再用mongorestore恢复。

 

mongodump -h localhost --port 4000 -d web -c blog

mongorestore -h localhost --port 4000 -d web -c blog

 

在向mongodb写入时,服务器默认每60S与磁盘强制同步一次,称为后台刷新(background flush)。

 

lsof | grep mongo | grep data | wc -l

ulimit -Hn 查看硬限制

vim /etc/security/limits.conf

 

mongod soft nofile 2048

mongod hard nofile 10240

 

日志会降低写操作的性能,可以在被动副本上开启日志。

 

mongoimport -d stocks -c values --type csv --headerline stocks.csv

 

mongoexport -d stocks -c values -o stocks.csv

 

mongodump -h localhost --port 27017

mongorestore -h localhost --port 27017

 

mongod --repair

db.runCommand({repairDatabase:1})

 

--fork 让进程以守护方式运行。

 

当子对象总是出现在父对象的上下文中时,使用内嵌文档;否则,保存在单独的集合里。

 

文章示例:

{_id:ObjectId("xx"),

title:"",

text:""}

 

评论:

{_id:ObjectId("4xxx"),

post_id:ObjectId("xxx"),

username:"zjone",

text:"indeed"

}

 

db.comments.ensureIndex({post_id:1}) //加索引

 

mongodb使用数组键来表示多对多的关系:

 

{_id:ObjectId("a"),title:"epites"}

{_id:ObjectId("b",title:"green flower"}

同时属于这两上分类的文档看起来是这样的:

{_id:ObjectId("y"),

    name:"Dragon Orchid",

    category_ids:[ObjectId("a"),ObjectId("b")]

}

 

先找到各个ID,然后用$in操作符查categories集合:

 

db.categories.find({id:{$in:product['category_ids']}})

 

db.comments.find({path:/^4d692/})

 

命名空间会超过24000,一旦超过这个值,就必须分配新的数据库。

 

mongofiles -d images list //列出数据库images里的文件;

---------------------------------------------------------------------------------

以下为命令行第二次收集脚本:

 

创建副本集:

---------------------------------

三台机器只是dbpath不同。端口不同;

D:\MongoDB\Server\3.6\bin>mongod --replSet myapp --dbpath c:\data\node1 --port 40000

D:\MongoDB\Server\3.6\bin>mongod --replSet myapp --dbpath c:\data\node1 --port 40001

D:\MongoDB\Server\3.6\bin>mongod --replSet myapp --dbpath c:\data\node1 --port 40002

 

------------------------------

连接到非仲裁节点后运行:假设当前4000,则

1.rs.initiate()

2.rs.add("localhost:40001")

3.rs.add("localhost:40002",{arbiterOnly:true});

一共三台,二台副本集,一个仲裁;

------------------------------------------------

db.isMaster()

rs.status()

rs.remove("localhost:4000")

rs.slaveOk() //允许副本查询

----------------------------------------------

分片:

------------------------------------------------------

mongod --shardsvr --replSet shard-a --dbpath c:\data\rs-a-1 --port 3000 --logpath c:\data\rs-a-1.log --fork --nojournal

mongod --shardsvr --replSet shard-a --dbpath c:\data\rs-a-2 --port 3001 --logpath c:\data\rs-a-2.log --fork --nojournal

mongod --shardsvr --replSet shard-a --dbpath c:\data\rs-a-3 --port 3002 --logpath c:\data\rs-a-3.log --fork --nojournal

 

mongod --shardsvr --replSet shard-b --dbpath c:\data\rs-b-1 --port 4000 --logpath c:\data\rs-b-1.log --fork --nojournal

mongod --shardsvr --replSet shard-b --dbpath c:\data\rs-b-2 --port 4001 --logpath c:\data\rs-b-2.log

mongod --shardsvr --replSet shard-b --dbpath c:\data\rs-b-3 --port 4002 --logpath c:\data\rs-b-3.log --fork --nojournal

--------------------------------------------------------

加了二个副本集,不同副本集的名称;然后所有的副本集加了--shardsvr

mongod --configsvr --replSet conf --dbpath c:\data\config-1 --port 27019 --logpath c:\data\config-1.log

mongod --configsvr --replSet conf --dbpath c:\data\config-2 --port 27020 --logpath c:\data\config-2.log

mongod --configsvr --replSet conf --dbpath c:\data\config-3 --port 27021 --logpath c:\data\config-3.log

加了三个配置服务器,也是副本集,而后每个配置服务器加了--configsvr 标识运行;

---------------------------------------------------------

注意副本集:

rs.initiate()

rs.add("123") //依次加三台配置服务器,配置服务器没有仲裁节点。注意上面二个分片也是;

任何的副本集都要初始化才能用。所有的分片配置完成后,用rs.status()查看副本集的状态;

而后所有的配置成功后,启用分片mongos.

------------------------------------------------------------------------

显示全文