MongoDB简介
- MongoDB是什么
MongoDB是一个使用C++编写的、开源的、面向文档的NoSQL(Not Only SQL)数据库,也是当前最热门的NoSql数据库之一。 - NoSQL简介
NoSQL的意思是“不仅仅是SQL”,是目前流行的“非关系型数据库”的统称。
常见的NoSQL数据库如:Redis、CouchDB、MongoDB、HBase、Cassandra等 - 为什么需要NoSQL?
简单的说,就是为了解决在web2.0时代,出现的三高要求:
1:对数据库高并发读写的需求
2:对海量数据的高效率存储和访问的需求
3:对数据库的高可扩展性和高可用性的需求
而RDB里面的一些特性,在web2.0里面往往变得不那么重要,比如:
1:数据库事务一致性
2:数据库的实时读写
3:复杂的SQL查询,特别是多表关联查询
关系型表:tbl_user
Uuid userId name
1 u1 zhangsan
2 u2
NoSQL:bson
{“uuid”:1,”userId”:”u1”,”name”:zhangsan}
{“uuid”:2,”userId”:”u2”,age:13} - CAP定理, 又被称作布鲁尔定理(Eric Brewer)
它指出对于一个分布式计算系统来说,不可能同时满足以下三点:
1:强一致性(Consistency):系统在执行过某项操作后仍然处于一致的,在分布式系统中,更新操作执行成功后所有的用户都应该读取到最新的值,这样的系统被认为具有强一致性
2:可用性(Availability):每一个操作总是能够在一定的时间内返回结果
3:分区容错性(Partition tolerance):系统在存在网络分区的情况下仍然可以接受请求并处理,这里网络分区是指由于某种原因网络被分成若干个孤立区域,而区域之间互不相通 - 根据CAP原理将数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
1:CA:单点集群,满足一致性,可用性,通常在可扩展性上不太强大,比如RDB
2:CP:满足一致性和分区容错性,通常性能不是特别高,如分布式数据库
3:AP:满足可用性和分区容错性,通常可能对一致性要求低一些,如大多数的NoSQL - BASE(Basically Available,Soft-state,Eventual consistency )
1:基本可用(Basically Available):系统能够基本运行、一直提供服务。 - NoSQL的优点
扩展简单方便,尤其是水平横向扩展
(纵向扩展是指用更强的机器;横向扩展是指把数据分散到多个机器)
读写快速高效,多数都会映射到内存操作
成本低廉,用普通机器,分布式集群即可
数据模型灵活,没有固定的数据模型
-NoSQL的缺点
不提供对SQL的支持
现有产品还不够成熟稳定,功能也还有待加强
-MongoDB特点
高性能、易于使用、易于扩展、功能丰富
面向集合存储,模式自由
支持动态查询,支持javascript表达式查询
支持索引
支持副本集复制和自动故障恢复
自动处理分片
支持二进制和大型对象数据
文件存储格式为BSON(JSON的一种扩展)
MongoDB安装和基本使用
- 安装
1:去官方下载最新的包,http://www.mongodb.org/downloads
2:然后tar zvxf 解压
3:拷贝到相应的文件夹即可
注意,奇数版是开发版 - 在Shell里面启动
1:在MongoDB的文件夹下创建dbs和logs的文件夹
2:到bin下,运行 ./mongod –dbpath ../dbs,就可以启动数据库了
当然,也可以通过 –logpath 来指定日志文件的路径,需要指定到文件。
3:可以把启动的参数设置到一个配置文件中,然后在启动的时候通过-f进行指定
4:MongoDB默认会监听27017端口,可以通过 –port来指定主端口
5:可以通过 ./mongod –help来查看启动时可以指定的参数 - 在后台启动
使用–fork选项,将会通知mongodb在后台运行。也可以配置到文件里面去,设置fork=true即可 - 关闭
1:如果是在Shell里面启动的,ctrl+c退出shell就关闭了
2:如果是在后台启动的,运行 pkill mongod
3:也可以进入javascript shell,切换到admin数据库,运行db.shutdownServer()
#MongoDB启动参数说明 - 基本配置
–quiet # 安静输出
–port arg # 指定服务端口号,默认端口27017
–bind_ip arg # 绑定服务IP,若绑定127.0.0.1,则只能本机访问,不指定默认本地所有IP
–logpath arg # 指定MongoDB日志文件,注意是指定文件不是目录
–logappend # 使用追加的方式写日志
–pidfilepath arg # PID File 的完整路径,如果没有设置,则没有PID文件
–keyFile arg # 集群的私钥的完整路径,只对于Replica Set 架构有效
–unixSocketPrefix arg # UNIX域套接字替代目录,(默认为 /tmp)
–fork # 以守护进程的方式运行MongoDB,创建服务器进程
–auth # 启用验证
–cpu # 定期显示CPU的CPU利用率和iowait
–dbpath arg # 指定数据库路径
–diaglog arg # diaglog选项 0=off 1=W 2=R 3=both 7=W+some reads
–directoryperdb # 设置每个数据库将被保存在一个单独的目录
–journal # 启用日志选项,MongoDB的数据操作将会写入到journal文件夹的文件里
–journalOptions arg # 启用日志诊断选项
–ipv6 # 启用IPv6选项
–jsonp # 允许JSONP形式通过HTTP访问(有安全影响)
–maxConns arg # 最大同时连接数 默认2000
–noauth # 不启用验证
–nohttpinterface # 关闭http接口,默认关闭2
–noprealloc # 禁用数据文件预分配(往往影响性能)
–noscripting # 禁用脚本引擎
–notablescan # 不允许表扫描
–nounixsocket # 禁用Unix套接字监听
–nssize arg (=16) # 设置信数据库.ns文件大小(MB)
–objcheck # 在收到客户数据,检查的有效性,
–profile arg # 档案参数 0=off 1=slow, 2=all
–quota # 限制每个数据库的文件数,设置默认为8
–quotaFiles arg # number of files allower per db, requires –quota
–rest # 开启简单的rest API
–repair # 修复所有数据库run repair on all dbs
–repairpath arg # 修复库生成的文件的目录,默认为目录名称dbpath
–slowms arg (=100) # value of slow for profile and console log
–smallfiles # 使用较小的默认文件
–syncdelay arg (=60) # 数据写入磁盘的时间秒数(0=never,不推荐)
–sysinfo # 打印一些诊断系统信息
–upgrade # 如果需要升级数据库 - Replicaton 参数
–fastsync # 从一个dbpath里启用从库复制服务,该dbpath的数据库是主库的快照,可用于快速启用同步
–autoresync # 如果从库与主库同步数据差得多,自动重新同步,
–oplogSize arg # 设置oplog的大小(MB)
● 主/从参数
–master # 主库模式
–slave # 从库模式
–source arg # 从库 端口号
–only arg # 指定单一的数据库复制
–slavedelay arg # 设置从库同步主库的延迟时间 - Replica set(副本集)选项:
–replSet arg # 设置副本集名称
● Sharding(分片)选项
–configsvr # 声明这是一个集群的config服务,默认端口27019,默认目录
/data/configdb
–shardsvr # 声明这是一个集群的分片,默认端口27018
–noMoveParanoia # 关闭偏执为moveChunk数据保存
MongoDB基本概念
- 数据库
MongoDB的一个实例可以拥有一个或多个相互独立的数据库,每个数据库都有自己的集合 - 集合
集合可以看作是拥有动态模式的表 - 文档
文档是MongoDB中基本的数据单元,类似于RDB的行。
文档是键值对的一个有序集合。在JS中,文档被表示成对象。 - _id
每个文档都有个特殊的“_id”,在文档所属集合中是唯一的 - JavaScript shell
MongoDB自带了一个功能强大的JavaScript Shell,可以用于管理或操作MongoDB - MongoDB和RDB的一些对比
1:都有数据库的概念
2:集合 –〉RDB的表
3:文档 –〉RDB表中的一条记录
4:文档对象里面的 key –〉 RDB表中的字段
5:文档对象里面的 value–〉 RDB表中字段的值
6:MongoDB中没有主外键的概念 - 数据库名称定义规则
1:不能是空串
2:不得含有/、\、?、$、空格、空字符等等,基本只能使用ASCII中的字母和数字
3:区分大小写,建议全部小写
4:最多为64字节
5:不得使用保留的数据库名,比如:admin,local,config
注意:数据库最终会成为文件,数据库名就是文件的名称 - 集合名称定义规则
1:不能是空串
2:不能包含\0字符(空字符),这个字符表示集合名的结束,也不能包含”$”
3:不能以”system.”开头,这是为系统集合保留的前缀 - 文档的键的定义规则
1:不能包含\0字符(空字符),这个字符表示键的结束
2:“.”和“$”是被保留的,只能在特定环境下用
3:区分类型,同时也区分大小写
4:键不能重复
注意:文档的键值对是有顺序的,相同的键值对如果有不同顺序的话,也是不同的文档 - MongoDB基本的数据类型
数据类型 描述 举例
null 表示空值或者未定义的对象 {“x”:null}
布尔值 真或者假:true或者false {“x”:true}
32位整数 shell不支持该类型,默认会转换成64位浮点数,
也可以使用NumberInt类,比如: {“x”:NumberInt(“3”)}
64位整数 shell不支持该类型,默认会转换成64位浮点数,
也可以使用NumberLong类,比如: {“x”:NumberLong(“3”)}
64位浮点数 shell中的数字就是这一种类型 {“x”:3.14,”y”:3}
字符串 UTF-8字符串 {“foo”:”bar”}
符号 shell不支持,shell会将数据库中的符号类型的数据自动转换成字符串
对象id 文档的12字节的唯一id {“id”: ObjectId()}
日期 从标准纪元开始的毫秒数 {“date”:new Date()}
正则表达式 文档中可以包含正则表达式,遵循JavaScript的语法 {“foo”:/foobar/i}
代码 文档中可以包含JavaScript代码 {“x”:function() {}}
未定义 undefined {“x”:undefined}
数组 值的集合或者列表 {“arr”: [“a”,”b”]}
内嵌文档 文档可以作为文档中某个key的value
MongoDB增删改操作
- 运行shell,命令:mongo ip:port
- 显示现有的数据库,命令:show dbs 或者 databases;
- 显示当前使用的数据库,命令:db
- 切换当前使用的数据库,命令:use 数据库名称
- 创建数据库:
MongoDB没有专门创建数据库的语句,可以使用“use” 来使用某个数据库,如果要使用
的数据库不存在,那么将会创建一个,会在真正向该库加入文档后,保存成为文件。 - 删除数据库,命令:db.dropDatabase()
- 显示现有的集合,命令:show collections 或者 tables;
- 创建集合:
在MongoDB中不用创建集合,因为没有固定的结构,直接使用db.集合名称.命令 来操作就可
以了。如果非要显示创建集合的话,用:db.createCollecion(“集合名称”); - 插入并保存文档
insert方法,可以单独插入一个文档,也可以插入多个,用“[ ]”即可。注意:
1:MongoDB会为每个没有“_id”字段的文档自动添加一个”_id”字段
2:每个Doc必须小于16MB
3:可以在shell中执行Object.bsonsize(文档名称);来查看size大小 - 删除文档:命令:remove , 可以按条件来删除
只是删除文档,集合还在,如果使用 drop命令,会连带集合和索引都删掉 - 查看集合中所有的文档,命令:db.集合名称.find();
- 查看集合中第一个文档,命令:db.集合名称.findOne({条件对象});
- 文档替换,命令: db.集合名称. update(条件,新的文档);
- 更新修改器,用来做复杂的更新操作
1:$set
:指定一个字段的值,如果字段不存在,会创建一个
2:$unset
:删掉某个字段
3:$inc
:用来增加已有键的值,如果字段不存在,会创建一个。只能用于整型、长整型、或双精度浮点型的值。
4:$push
:向已有数组的末尾加入一个元素,要是没有就新建一个数组
5:$each
:通过一次$push
来操作多个值
6:$slice
:限制数组只包含最后加入的n个元素,其值必须是负整数
7:$sort
:对数组中的元素,按照指定的字段来对数据进行排序(1为升序,-1为降序),然后再按照slice删除。注意:不能只将$slice
或者$sort
与$push
配合使用,且必须使用$each
8:$ne
:判断一个值是否在数组中,如果不在则添加进去
9:$addToSe
t:将数组作为数据集使用,以保证数组内的元素不会重复
10:$pop
:从数组一端删除元素,{$pop
:{key:1}},从末尾删掉一个,-1则从头部删除 - save方法
如果文档存在就更新,不存在就新建,主要根据”_id”来判断。 - upsert
找到了符合条件的文档就更新,否则会以这个条件和更新文档来创建一个新文档。
指定update方法的第三个参数为true,可表示是upsert - 更新多个文档
MongoDB默认只更新符合条件的第一个文档,要更新所有匹配的文档,把第4个参数
设置为true。注意:
1:只能用在$XXX的操作中
2:最好每次都显示的指定update的第4个参数,以防止服务器使用默认行为 - 查询更新了多少个文档
使用命令:getLastError ,返回最后一次操作的相关信息,里面的n就是更新的文
档的数量。形如:db.runCommand({“getLastError”:1});
MongoDB查询操作
- 指定需要返回的键
在find方法的第二个参数进行指定。默认情况下,始终会返回”_id”,可以通过设
置字段为0来表示不返回这个字段。 - 按条件查询
在find方法里面加入条件数据即可,find方法的第一个参数就是。
注意:条件数据必须是常量值,不能是另外的字段的数据
1:比较操作:$lt
,$lte
,$gt
,$gte
,$ne
2:$and
:包含多个条件,他们之间为and的关系
3:$or
:包含多个条件,他们之间为or的关系 ,$nor
相当于or取反
4:$not
:用作其他条件之上,取反
5:$mod
:将查询的值除以第一个给定的值,如果余数等于等二个值则匹配成功
6:$in
:查询一个键的多个值,只要键匹配其中一个即可 ,$nin
为不包含
7:$all
:键需要匹配所有的值
8:$exists
:检查某个键是否存在,1表示存在,0表示不存在 - 正则表达式
MongoDB使用Perl兼容的正则表达式(PCRE),比如:db.users.find({“name”:/sishuok/i}); 又比如:db.users.find({“name”:/^sishuok/}); - 查询数组
1:单个元素匹配,就跟前面写条件一样,{key:value}
2:多个元素匹配,使用 all,key:$all:[a,b],元素的顺序无所谓3:可以使用索引指定查询数组特定位置,“key.索引号”:value4:查询某个长度的数组,使用 size
5:指定子集,使用$slice
,正数是前面多少条,负数是尾部多少条,也可以指定偏移量和要返回的元素数量,比如:$slice
:[50,10]
6:可以使用$来指定符合条件的任意一个数组元素,如:{”users.$
”:1}
7:$elemMatch
:要求同时使用多个条件语句来对一个数组元素进行比较判断 - 查询内嵌文档
1:查询整个内嵌文档与普通查询是一样的
2:如果要指定键值匹配,可以使用“.” 操作符,比如:{“name.first”:”a” ,“name.last”:”b”}
3:如果要正确的指定一组条件,那就需要使用$elemMatch
,以实现对内嵌文档的多个键进行匹配操
作
注意:内嵌文档的查询必须要整个文档完全匹配 $where
查询
在查询中执行任意的JavaScript,通过编程来解决查询的匹配问题,方法返回boolean值。
function t1(){
for(var a in this){
if(a=="a"){return true;}
}
return false;
}
使用的时候:db.users.find({“where”:t1});
注意:where性能较差,安全性也是问题,所以不到万不得已,不要使用
- 查询记录条数的命令:count
1:直接使用count()的话,得到的是整个记录的条数
2:如果要获取按条件查询后记录的条数,需要指定count(true或者非0的数)
● 限制返回的记录条数的命令:limit(要返回的条数)
● 限制返回的记录条数起点的命令:skip(从第几条开始返回)
● 排序的命令:sort({要排序的字段:1为升序,-1为降序})
1:可以对多个字段进行排序
2:MongoDB处理不同类型的数据是有一定顺序的,有时候一个键有多种类型的值,其排序顺序是预先定义好
的,从小到大如下:
(1)最小值 (2)null (3)数字 (4)字符串
(5)对象/文档 (6)数组 (7)二进制数据 (8)对象id
(9)布尔类型 (10)日期型 (11)时间戳 (12)正则表达式
(13)最大值
- 分页查询:组合使用limit,skipt和sort
当然也可以使用其他方式来分页,比如采用自定义的id,然后根据id来分页
- 查询给定键的所有不重复的数据,命令:distinct
- 游标
1:获取游标,示例如下:
var c = db.users.find();
2:循环游标,可以用集合的方式,示例如下:
while(c.hasNext()){
printjson(c.next());
}
3:也可以使用forEach来循环,示例如下:
c.forEach(function(obj){
print(obj);
});
- 存储过程
1:MongoDB的存储过程其实就是个自定义的js函数
2:使用db.system.js.save({“_id”:名称,value:函数});
3:可以通过如下命令查看:db.system.js.find();
4:可以通过如下命令调用:db.eval(名称);
聚合框架
- 简介
MongoDB的聚合框架,主要用来对集合中的文档进行变换和组合,从而对数据进行分析以
加以利用。
聚合框架的基本思路是:采用多个构件来创建一个管道,用于对一连串的文档进行处理。
这些构件包括:筛选(filtering)、投影(projecting)、分组(grouping)、排序(sorting)、限制
(limiting)和跳过(skipping)。 - 使用聚合框架的方式
db.集合.aggregate(构件1,构件2…)
注意:由于聚合的结果要返回到客户端,因此聚合结果必须限制在16M以内,这是MongoDB支持的
最大响应消息的大小。 - 准备样例数据
for(var i=0;i<100;i++){
for(var j=0;j<4;j++){
db.scores.insert({"studentId":"s"+i,"course":"课程"+j,"score":Math.random()*100});
}
}
- 示例要完成的功能
找出考80分以上的课程门数最多的3个学生,是用聚合框架来完成功能的步骤:
1:找到所有考了80分以上的学生,不区分课程
{“$match
“:{“score”:{$gte
:80}}}
2:将每个学生的名字投影出来
{“$project
“:{“studentId”:1}}
3:对学生的名字排序,某个学生的名字出现一次,就给他加1
{“$group
“:{“_id”:”$studentId
“,”count”:{“$sum
“:1}}}
4:对结果集按照count进行降序排列
{“$sort
“:{“count”:-1}}
5:返回前面的3条数据
{“$limit
“:3}
最终执行的语句就是:
db.scores.aggregate(
{“$match
“:{“score”:{$gte
:80}}}
, {“$project
“:{“studentId”:1}}
, {“$group
“:{“_id”:”$studentId
“,”count”:{“$sum
“:1}}}
, {“$sort
“:{“count”:-1}}
, {“$limit
“:3}
) - 管道操作符简介
每个操作符接受一系列的文档,对这些文档做相应的处理,然后把转换后
的文档作为结果传递给下一个操作符。最后一个操作符会将结果返回。
不同的管道操作符,可以按照任意顺序,任意个数组合在一起使用。
● 管道操作符$match
用于对文档集合进行筛选,里面可以使用所有常规的查询操作符。通常会
放置在管道最前面的位置,理由如下:
1:快速将不需要的文档过滤,减少后续操作的数据量
2:在投影和分组之前做筛选,查询可以使用索引 - 管道操作符
$project
用来从文档中提取字段,可以指定包含和排除字段,也可以重命名字段。
比如要将studentId改为sid,如下:
db.scores.aggregate({“$project
“:{“sid”:”$studentId
“}}) - 管道操作符
$project
的数学表达式
比如给成绩集体加20分,如下:
db.scores.aggregate({“$project
“:{“newScore”:{$add
:[“$score
“,20]}}})
支持的操作符和相应语法:
1:$add
: [expr1[,expr2,…exprn]]
2:$subtract
:[expr1,expr2]
3:$multiply
:[expr1[,expr2,…exprn]]
4:$divice
:[expr1,expr2]
5:$mod
:[expr1,expr2] - 管道操作符
$project
的日期表达式
聚合框架包含了一些用于提取日期信息的表达式,如下:
$year
、$month
、$week
、$dayOfMonth
、$dayOfWeek
、$dayOfYear
、$hour
、$minute
、
$second
。
注意:这些只能操作日期型的字段,不能操作数据,使用示例:
{“$project
“:{“opeDay”:{“$dayOfMonth
“:”$recoredTime
“}}} - 管道操作符
$project
的字符串表达式
1:$substr
: [expr,开始位置,要取的字节个数]
2:$concat
:[expr1[,expr2,…exprn]]
3:$toLower
:expr
4:$toUpper
:expo,例如:{“$project
“:{“sid”:{$concat
:[“$studentId
“,”cc”]}}} - 管道操作符
$project
的逻辑表达式
1:$cmp
:[expr1,expr2] :比较两个表达式,0表示相等,正数前面的大,负数后面的大
2:$strcasecmp
:[string1,string2] :比较两个字符串,区分大小写,只对由罗马字符组成的字符串有效
3:$eq
、$ne
、$gt
、$gte
、$lt
、$lte
:[expr1,expr2]
4:$and
、$or
、$not
5:$cond
:[booleanExpr,trueExpr,falseExpr]:如果boolean表达式为true,返回true表达式,否则返回false表达式
6:$ifNull
:[expr,otherExpr]:如果expr为null,返回otherExpr,否则返回expr
例如:db.scores.aggregate({“$project
“:{“newScore”:{$cmp
:[“$studentId
“,”sss”]}}}) $group
用来将文档依据特定字段的不同值进行分组。选定了分组字段过后,就可以把这些字段传递给$group
函数的“_id”字段了。例如:
db.scores.aggregate({“$group
”:{“_id”:“$studentId
”}}); 或者是
db.scores.aggregate({“$group
“:{“_id”:{“sid”:”$studentId
“,”score”:”$score
“}}});$group
支持的操作符
1:$sum
:value :对于每个文档,将value与计算结果相加
2:$avg
:value :返回每个分组的平均值
3:$max
:expr :返回分组内的最大值
4:$min
:expr :返回分组内的最小值
5:$first
:expr :返回分组的第一个值,忽略其他的值,一般只有排序后,明确知道数据顺序的时候,这个操作才有意义
6:$last
:expr :与上面一个相反,返回分组的最后一个值
7:$addToSet
:expr :如果当前数组中不包含expr,那就将它加入到数组中
8:$push
:expr:把expr加入到数组中- 拆分命令:
$unwind
用来把数组中的每个值拆分成为单独的文档。 - 排序命令:
$sort
可以根据任何字段进行排序,与普通查询中的语法相同。如果要对大量的文档进行
排序,强烈建议在管道的第一个阶段进行排序,这时可以使用索引。 - 常见聚合函数
1:count:用于返回集合中文档的数量
2:distinct:找出给定键的所有不同值,使用时必须指定集合和键,例如:
db.runCommand({“distinct”:”users”,”key”:”userId”}); - MapReduce介绍
在MongoDB的聚合框架中,还可以使用MapReduce,它非常强大和灵活,但具有一定
的复杂性,专门用于实现一些复杂的聚合功能。
MongoDB中的MapReduce使用JavaScript来作为查询语言,因此能表达任意的逻辑,
但是它运行非常慢,不应该用在实时的数据分析中。 - MapReduce的HelloWorld实现的功能,找出集合中所有的键,并统计每个键出现的次数。
1:Map函数使用emit函数来返回要处理的值,示例如下:
var map = function(){
for(var key in this){
emit(key,{count:1});
}
}
this表示对当前文档的引用。
2:reduce函数需要处理Map阶段或者是前一个reduce的数据,因此reduce返回的文档必须要能
作为reduce的第二个参数的一个元素,示例如下:
var reduce = function(key,emits){
var total = 0;
for(var i in emits){
total += emits[i].count;
}
return {"count":total};
};
3:运行MapReduce,示例如下:
var mr = db.runCommand({"mapreduce":"users","map":map,"reduce":reduce,"out":"mrout"});
4:查询最终的结果,示例如下:
db.mrout.find();
- 还可以改变一下,比如统计userId中值,以及每个值出现的次数,就可以如下操作
1:修改map函数,示例如下:
var map = function(){
emit(this.userId,{count:1});
};
2:reduce函数不用改
3:重新执行
db.runCommand({"mapreduce":"users","map":map,"reduce":reduce,"out":"mrout"});
4:查看最终结果:
db.mrout.find();
- 更多MapReduce可选的键
1:finalize:function :可以将reduce的结果发送到finalize,这是整个处理的最后一步
2:keeptemp:boolean :是否在连接关闭的时候,保存临时结果集合
3:query:document :在发送给map前对文档进行过滤
4:sort:document :在发送给map前对文档进行排序
5:limit:integer :发往map函数的文档数量上限
6:scope:document :可以在javascript中使用的变量
7:verbose:boolean :是否记录详细的服务器日志
示例:
var query = {"userId":{"$gt":"u2"}}
var sort = {"userId":1};
var finalize = function(key,value){
r eturn {"mykey":key,"myV":value};
};
var mr =
db.runCommand({"mapreduce":"users","map":map,"reduce":reduce,"out":"mrout","que
ry":query,"sort":sort,"limit":2,"finalize":finalize});
- 聚合命令group
用来对集合进行分组,分组过后,再对每一个分组内的文档进行聚合。
比如要对studentId进行分组,找到每个学生最高的分数,可以如下步骤进行:
1:测试数据就用聚合框架一开始准备的数据
2:使用group,示例如下:
db.runCommand({"group":{
"ns":"scores",
"key":{"studentId":1},
"initial":{"score":0},
"$reduce":function(doc,prev){
if(doc.score > prev.score){
prev.score = doc.score;
}
}
}});
ns:指定要分组的集合
key:指定分组的键
initial:每一组的reduce函数调用的时候,在开头的时候调用一次,以做初始化
$reduce
:在每组中的每个文档上执行,系统会自动传入两个参数,doc是当前处理的文档,prev是本组前一次执行的结果文档
3:你还可以在group的时候添加条件,就是加入condition,示例如:
“condition”:{“studentId”:{“$lt
“:”s2”}}
4:同样可以使用finalizer来对reduce的结果进行最后的处理,比如要求每个学生的平均分,
就可以先按照studentId分组,求出一个总的分数来,然后在finalizer里面,求平均分:
db.runCommand({"group":{
"ns":"scores",
"key":{"studentId":1},
"initial":{"total":0},
"$reduce":function(doc,prev){
prev.total += doc.score;
},
"condition":{"studentId":{"$lt":"s2"}},
"finalize":function(prev){
prev.avg = prev.total/3;
}
}});
注意:finalize是只在每组结果返回给用户前调用一次,也就是每组结果只调用一次
5:对于分组的key较为复杂的时候,还可以采用函数来做为键,比如让键不区分大小下,就可以如下定义:
db.runCommand({"group":{
"ns":"scores",
$keyf:function(doc){
return {studentId:doc.studentId.toLowerCase()};
},
"initial":{"total":0},
"$reduce":function(doc,prev){
prev.total += doc.score;
},
"condition":{"$or":[{"studentId":{"$lt":"s2"}},{"studentId":"S0"}]},
"finalize":function(prev){
prev.avg = prev.total/3;
}
}});
注意:要使用$keyf来定义函数作为键,另外一定要返回对象的格式
理解MongoDB的文档存储
- 将文档插入到MongoDB的时候,文档是按照插入的顺序,依次在磁盘上相邻保存,因此,一个文档变大了,原来的位置要是放不下这个文档了,就需要把这个文档移动到集合的另外一个位置,通常是最后,能放下这个文档的地方。
- MongoDB移动文档的时候,会自动修改集合的填充因子(padding
factor),填充因子是为新文档预留的增长空间,不能手动设定填充因子。
1:填充因子开始可能是1,也就是为每个文档分配精确的空间,不预留增长空间
2:当有文档超长而被迫移动文档的时候,填充因子会增大
3:当集合中不再有文档移动的时候,填充因子会慢慢减小 - MongoDB进行文档移动是非常慢的移动文档的时候,MongoDB需要将文档原先占用的空间释放掉,然后将文档写入新的空间,相对费时,尤其是文档比较大,又频繁需要移动的话,会严重影响性能
MongoDB的索引
- 创建索引,命令:ensureIndex
1:创建索引时,1表示按升序存储,-1表示按降序存储
2:可以给索引指定名字,创建的时候指定 name 即可
3:可以创建复合索引,如果想用到复合索引,必须在查询条件中包含复合索引中的前N个索引列
4:如果查询条件中的键值顺序和复合索引中的创建顺序不一致的话,MongoDB可以智能的帮助我们调
整该顺序,以便使复合索引可以为查询所用
5:可以为内嵌文档创建索引,其规则和普通文档创建索引是一样的
6:一次查询中只能使用一个索引,$or特殊,可以在每个分支条件上使用一个索引
7:如果查询要在内存中排序的话,结果集不能超过32M
8:$where
,$exists
不能使用索引,还有一些低效率的操作符,比如:$ne
,$not
,$nin
等
9:设计多个字段的索引时,应该尽量将用于精确匹配的字段放在索引的前面
10:MongoDB限制每个集合上最多只能有64个索引,建议在一个特定的集合上,不要设置多个索引。
11:如果要在后台运行创建索引,添加 {background:true} - 查看已经创建的索引,命令:getIndexes
- 删除索引,命令:dropIndex
- 查看查询语句解释,命令:explain
- explain的字段说明:
1:cursor:本次查询使用的索引
2:isMultiKey:是否使用了多键索引
3:n:返回的文档数量
4:nscannedObjects:按照索引指针去磁盘查找实际文档的次数
5:nscanned:如果使用索引,就是查找过的索引条目数量;如果全表扫描,就是查
找过的文档数量
6:scanAndOrder:是否在内存中对结果集排序
7:indexOnly:是否只是用索引就能完成本次查询
8:nYields:为了让写入请求能顺利执行,本次查询暂停的次数
9:millis:本次查询所耗费的时间,单位是毫秒
10:indexBounds:描述索引的使用情况,给出了索引的遍历范围 - 指定使用的索引:hint
- 指定不使用索引,强制全表扫描:查询.hint({
$natural
:1}); - 唯一索引:在创建的时候,指定{“unique”:true}
1:唯一索引将会保证该字段的数据不会重复,如果重复插入,只会保存一条
2:索引能保存的数据值必须小于1024字节,这意味着超长的数据,不会保存到索引里,因此也就可以插入多个重复的数据了
3:复合索引也可以创建为唯一索引
4:在已有集合上创建唯一索引可能会失败,因为已经有了重复数据,此时可以指定dropDups来强制去重,但由于保留数据的不可控,因此对重要数据不建议使用
5:唯一索引会把null看作值,所以无法将多个缺少唯一索引的数据插入,这时可以指定sparse来创建一个稀疏索引,如:{“unique”:true,”sparse”:true} - 索引的集合
所有索引信息都保存在system.indexes集合众,这是一个保留集合