文章目录
MongoDB
皆来自“菜鸟教程”
概念解析
可以类比elasticsearch
sql | mongodb | 说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据表 / 集合 |
row | document | 行 / 文档 |
column | field | 列 / 域 |
index | index | 索引 |
primary key | primary key | 主键(MongoDB自动将_id字段设置为主键) |
Mysqld | mongod | 数据库服务 |
mysql | mongo | 客户端 |
数据库
MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
show dbs
命令可以显示所有数据的列表。
db
显示当前数据库对象或集合
use
连接到一个指定的数据库
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
- admin:要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
- local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
- config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
集合
集合就是 MongoDB 文档组,当第一个文档插入时,集合就会被创建。
合法的集合名
- 集合名不能是
空字符串""
。 - 集合名不能含有
\0
字符(空字符),这个字符表示集合名的结尾。 - 集合名不能以
system.
开头,这是为系统集合保留的前缀。 - 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则
千万不要在名字里出现$
。
capped collections
Capped collections 就是固定大小的collection。
它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 “RRD” 概念类似。
Capped collections 是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能和标准的 collection 不同,你必须要显式的创建一个capped collection,指定一个 collection 的大小,单位是字节。collection 的数据存储空间值提前分配的。
Capped collections 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当我们更新Capped collections 中文档的时候,更新后的文档不可以超过之前文档的大小,这样话就可以确保所有文档在磁盘上的位置一直保持不变。
由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。(MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。)
要注意的是指定的存储大小包含了数据库的头信息。
db.createCollection("mycoll", {capped:true, size:100000})
- 在 capped collection 中,你能添加新的对象。
- 能进行更新,然而,对象不会增加存储空间。如果增加,更新就会失败 。
- 使用 Capped Collection 不能删除一个文档,可以使用 drop() 方法删除 collection 所有的行。
- 删除之后,你必须显式的重新创建这个 collection。
文档
文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
需要注意的是:
- 文档中的键值对是有序的。
- 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
- MongoDB区分类型和大小写。
- MongoDB的文档不能有重复的键。
- 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
文档键命名规范:
- 键不能含有
\0
(空字符)。这个字符用来表示键的结尾。 .
和$
有特别的意义,只有在特定环境下才能使用。- 以下划线"_"开头的键是保留的(不是严格要求的)。
元数据
数据库的信息是存储在集合中。它们使用了系统的命名空间:dbname.system.*
在MongoDB数据库中名字空间 .system.* 是包含多种系统信息的特殊集合(Collection),如下:
集合命名空间 | 描述 |
---|---|
dbname.system.namespaces | 列出所有名字空间 |
dbname.system.indexes | 列出所有索引(可以创建索引。但除此之外该表信息是不可变的) |
dbname.system.profile | 包含数据库概要(profile)信息(可删除) |
dbname.system.users | 列出所有可访问数据库的用户(可修改) |
dbname.local.sources | 包含复制对端(slave)的服务器信息和状态 |
数据类型
数据类型 | 描述 |
---|---|
String | 字符串,UTF-8 编码的字符串才是合法的 |
Integer | 整型数值 |
Boolean | 布尔值 |
Double | 双精度浮点值 |
Min/Max keys | 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比 |
Array | 用于将数组或列表或多个值存储为一个键 |
Timestamp | 时间戳 |
Object | 用于内嵌文档 |
Null | 用于创建空值 |
Symbol | 符号。基本上等同于字符串类型,但它一般用于采用特殊符号类型的语言 |
Date | 日期时间 |
Object ID | 对象 ID。用于创建文档的 ID |
Binary Data | 二进制数据 |
Code | 代码类型。用于在文档中存储 JavaScript 代码 |
Regular expression | 正则表达式类型。用于存储正则表达式 |
ObjectId
ObjectId 类似唯一主键,可以很快的去生成和排序
MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象
由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,可以通过 getTimestamp 函数来获取文档的创建时间:
> var newObject = ObjectId()
> newObject.getTimestamp()
ISODate(“2017-11-25T07:21:10Z”)
ObjectId 转为字符串
> newObject.str
5a1919e63df83ce79df8b38f
命令
连接:mongodb://[username:password@]host[:port][,host:port2, ...][ /[database][?options] ]
(?options 是连接选项。如果不使用/database,则前面需要加上/。所有连接选项都是键值对name=value
,键值对之间通过 & 或 ; 隔开)
具体连接选项:https://www.runoob.com/mongodb/mongodb-connections.html
操作数据库
创建:use
(如果数据库不存在,则创建数据库,否则切换到指定数据库)
查看所有数据库:show dbs
查看当前数据库:db
删除当前数据库:db.dropDatabase()
操作集合
在 MongoDB 中,你不需要刻意创建集合。当你插入一些文档时,MongoDB 会自动创建集合。
创建:db.createCollection(name [, options])
例:创建集合hellodb.createCollection("hello")
options:
capped(布尔类型) 如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。当该值为 true 时,必须指定 size 参数。
size(数值类型) 为固定集合指定一个最大值,即字节数。如果 capped 为 true,必须要指定该字段。
max(数值类型) 指定固定集合中包含文档的最大数量。
查看所有集合show collections
删除集合db.collection_name.drop()
例:删除集合abcdb.abc.drop()
操作文档
文档的数据结构和 JSON 基本一样。
.
所有存储在集合中的数据都是 BSON 格式。
.
BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。
插入insert() 或 insertOne() 或 insertMany()
insert()
作用: 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据
格式:db.COLLECTION_NAME.insert(document)
例:在当前数据库下的 col 集合中插入一个文档
db.col.insert({title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
by: '菜鸟教程',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})
insertOne()
作用:如果 _id 主键存在则更新数据,如果不存在就插入数据
格式:db.collection_name.insertOne(<document> [,{writeConcern: <document>}] )
writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
insertMany()
作用:插入多条数据
格式:
db.collection.insertMany(
[ <document1> , <document2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
ordered:指定是否按顺序写入,默认 true,按顺序写入。
更新update()
格式:
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
- query : update的查询条件,类似sql update查询内where后面的。
- update : update的对象和一些更新的操作符(如, ,,inc…)等,也可以理解为sql update查询内set后面的
- upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
- multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
- writeConcern :可选,抛出异常的级别。
例:更新 ‘title’ 为 ‘MongoDB 教程’ 的第一个文档的 ‘title’ 为 ‘MongoDB’
db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}})
在执行 remove() 函数前先执行 find() 命令来判断执行的条件是否正确,这是一个比较好的习惯
删除remove()
格式
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
- query :(可选)删除的文档的条件。
- justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
- writeConcern :(可选)抛出异常的级别。
查询find() findOne()
格式:db.collection.find(query, projection)
- query :可选,使用查询操作符指定查询条件
- projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
db.collection.find().pretty()
:以易读的方式来读取数据
条件操作符
等于 (默认) {<key>:<value>}
小于 ($lt) {<key>:{$lt:<value>}}
小于或等于 ($lte) {<key>:{<$lte:<value>}}
大于 ($gt) {<key>:{$gt:<value>}}
大于或等于 ($gte) {<key>:{$gte:<value>}}
不等于 ($ne) {<key>:{$ne:<value>}}
and
{key1:value1, key2:value2}
or
>db.col.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
{$or:[{"by":"菜鸟教程"},{"title": "MongoDB 教程"}
// where by = "菜鸟教程" or title = "MongoDB 教程"
and 和 or 一起使用
db.col.find({
"likes": {$gt:50},
$or: [
{"by": "菜鸟教程"},
{"title": "MongoDB 教程"}
]
}).pretty()
// where likes>50 AND (by = '菜鸟教程' OR title = 'MongoDB 教程')
type
例:获取 “col” 集合中 title 为 String 类型的数据
db.col.find({"title" : {$type : 2}})
或
db.col.find({"title" : {$type : 'string'}})
读取指定数量的数据 limit()
格式:db.COLLECTION_NAME.find().limit(NUMBER)
跳过指定数量的数据 skip()
格式:db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
排序 sort()
sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
格式:db.COLLECTION_NAME.find().sort({KEY:1})
例:db.col.find({},{"title":1}).sort({"likes":-1})
操作索引
创建:createIndex()
格式:db.collection.createIndex(keys, options)
keys:{要创建索引的字段: 1 / -1 [,要创建…字段: 1 / -1, …] }
例:db.col.createIndex({"title":1,"description":-1})
options:如下图
查看集合索引db.col.getIndexes()
查看集合索引大小db.col.totalIndexSize()
删除集合所有索引db.col.dropIndexes()
删除集合指定索引db.col.dropIndex("索引名称")
聚合 aggregate()
聚合的概念:MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果
管道的概念
管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。
MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
管道操作符
$project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
$match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
$limit:用来限制MongoDB聚合管道返回的文档数。
$skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
$unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group:将集合中的文档分组,可用于统计结果。
$sort:将输入文档排序后输出。
$geoNear:输出接近某一地理位置的有序文档。
管道操作符实例
1、$project实例
db.article.aggregate(
{ $project : {
title : 1 ,
author : 1 ,
}}
);
这样的话结果中就只还有_id,tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:
db.article.aggregate(
{ $project : {
_id : 0 ,
title : 1 ,
author : 1
}});
2.$match实例
db.articles.aggregate( [
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );
$match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。
3.$skip实例
db.article.aggregate(
{ $skip : 5 });
经过$skip管道操作符处理后,前五个文档被"过滤"掉。
聚合表达式:
复制(副本集)
概念:将数据同步在多个服务器的过程
作用:防止数据丢失,复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。复制还允许您从硬件故障和服务中断中恢复数据。
原理:
mongodb各个节点常见的搭配方式为:一主一从、一主多从。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
副本集特征:
- N 个节点的集群
- 任何节点可作为主节点
- 所有写入操作都在主节点上
- 自动故障转移
- 自动恢复
副本集设置
以–replSet启动MongoDB服务器
mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0
– replSet 指定MongoDB实例的名字客户端命令 rs.initiate()
在Mongo客户端使用命令rs.initiate()来启动一个新的副本集。
> rs.initiate(
{
_id: 'rs0',
members: [
{_id: 0, host: 'localhost:27020'},
{_id: 1, host: 'localhost:27021'}
]
}
)
> rs.isMaster() #查看主从关系
- 添加副本集成员 rs.add(HOST_NAME:PORT)
假设你已经启动了一个名为mongod1.net,端口号为27017的Mongo服务。
在客户端命令窗口使用rs.add() 命令将其添加到副本集中,命令如下所示:
rs.add("mongod1.net:27017")
rs.conf()
查看副本集的配置
rs.status()
查看副本集状态
rs.isMaster()
查看主从关系
分片
概念:
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。
这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
为什么分片:
- 复制所有的写入操作到主节点
- 延迟的敏感数据会在主节点查询
- 单个副本集限制在12个节点
- 当请求量巨大时会出现内存不足。
- 本地磁盘不足
- 垂直扩展价格昂贵
Shard:
用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障Config Server:
mongod实例,存储了整个 ClusterMetadata,其中包括 chunk信息。Query Routers:
前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。
测试:
启动多个服务器:
Shard Server 1:27020
Shard Server 2:27021
Shard Server 3:27022
Shard Server 4:27023
Config Server :27100
Route Process:40000
前端路由的启动
/usr/local/mongoDB/bin/mongos
–port 40000 // 指定前端路由的端口
–configdb localhost:27100 // 指定config服务器
–fork
–logpath=/www/mongoDB/shard/log/route.log
–chunkSize 500 // 指定chunk的大小的,单位是MB,默认大小为200MB.
配置sharding(添加shard节点)
// 进入router服务器
[root@100 shard]# /usr/local/mongoDB/bin/mongo admin --port 40000
MongoDB shell version: 2.0.7
connecting to: 127.0.0.1:40000/admin
// 添加shard节点
mongos> db.runCommand({ addshard:"localhost:27020" })
{ "shardAdded" : "shard0000", "ok" : 1 }
......
mongos> db.runCommand({ addshard:"localhost:27029" })
{ "shardAdded" : "shard0009", "ok" : 1 }
// 设置分片存储的数据库
mongos> db.runCommand({ enablesharding:"test" })
{ "ok" : 1 }
mongos> db.runCommand({ shardcollection: "test.log", key: { id:1,time:1}})
{ "collectionsharded" : "test.log", "ok" : 1 }
备份与恢复
备份
mongodump -h dbhost -d dbname -o dbdirectory
-h:MongoDB 所在服务器地址,例如:127.0.0.1,当然也可以指定端口号:127.0.0.1:27017
-d:需要备份的数据库实例,例如:test
-o:备份的数据存放位置,例如:c:\data\dump,当然该目录需要提前建立,在备份完成后,系统自动在dump目录下建立一个test目录,这个目录里面存放该数据库实例的备份数据。
–collection --db:备份指定数据库的指定集合
–dbpath DB_PATH --out BACKUP_DIRECTORY:复制数据库
恢复
mongorestore -h <hostname><:port> -d dbname <path>
–host <:port>, -h <:port>:
MongoDB所在服务器地址,默认为: localhost:27017–db , -d :
需要恢复的数据库实例,例如:test,当然这个名称也可以和备份时候的不一样,比如test2–drop:
恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用哦!<path>:
mongorestore 最后的一个参数,设置备份数据所在位置,例如:c:\data\dump\test。
你不能同时指定 <path> 和 --dir 选项,–dir也可以设置备份目录。–dir:
指定备份的目录
你不能同时指定 <path> 和 --dir 选项。
监控
MongoDB中提供了mongostat
和 mongotop
两个命令来监控MongoDB的运行情况。
mongotop <num>
mongotop输出结果字段说明:
ns:包含数据库命名空间,后者结合了数据库名称和集合。
db:包含数据库的名称。名为 . 的数据库针对全局锁定,而非特定数据库。
total:mongod花费的时间工作在这个命名空间提供总额。
read:提供了大量的时间,这mongod花费在执行读操作,在此命名空间。
write:提供这个命名空间进行写操作,这mongod花了大量的时间。
JDBC
导入依赖:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.12.11</version>
</dependency>
步骤:
// 连接服务器,默认host为localhost,port为27017
MongoClient mongoClient = new MongoClient();
// 连接数据库
MongoDatabase database = mongoClient.getDatabase("mytest");
// 创建集合
database.createCollection("col");
// 获取集合
MongoCollection<Document> collection = database.getCollection("col");
// 插入文档
Document document = new Document("title", "hello").
append("name", "world").
append("age", 1);
collection.insertOne(document);
// 获取文档
FindIterable<Document> documents = collection.find();
MongoCursor<Document> iterator = documents.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 更新文档
collection.updateOne(Filters.eq("age", 1), new Document("$set", new Document("age", 10)));
// 删除文档
collection.deleteOne(Filters.eq("age", 10));
如果你的 Mongo 需要验证用户名及密码,可以使用以下代码
//连接到MongoDB服务 如果是远程连接可以替换“localhost”为服务器所在IP地址
//ServerAddress()两个参数分别为 服务器地址 和 端口
ServerAddress serverAddress = new ServerAddress("localhost",27017);
List<ServerAddress> addrs = new ArrayList<ServerAddress>();
addrs.add(serverAddress);
//MongoCredential.createScramSha1Credential()三个参数分别为 用户名 数据库名称 密码
MongoCredential credential = MongoCredential.createScramSha1Credential("username", "databaseName", "password".toCharArray());
List<MongoCredential> credentials = new ArrayList<MongoCredential>();
credentials.add(credential);
//通过连接认证获取MongoDB连接
MongoClient mongoClient = new MongoClient(addrs,credentials);
整合springboot
导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
编写配置文件
spring.data.mongodb.uri=mongodb://localhost:27017/mytest
编写实体类
@Data
@Document("User") // 可以指定集合名,若不指定则为首字母小写类名user
public class User {
@Id
private int id;
private String name;
private int age;
}
注入MongoTemplate
增insert
删remove
改upsert
查find
例:
@Test
void find(){
Query query = new Query(Criteria.where("_id").gt(1).and("age").is(100));
// query.addCriteria(Criteria.where("_id").is(1));
// 排序
query.with(Sort.by(Sort.Direction.ASC, "name"));
// 分页
int pageSize = 10;
int pageNow = 1;
long count = mongoTemplate.count(query, User.class);
query.skip((pageNow - 1) * 10).limit(pageSize);
//
List<User> userList = mongoTemplate.find(query, User.class);
userList.forEach(System.out::println);
}
@Test
void update(){
Query query = new Query(Criteria.where("name").is("czc").orOperator(Criteria.where("age").is(2)));
Update update = new Update();
update.set("name", "czc1").set("age", 20);
mongoTemplate.upsert(query, update, User.class);
}
文档的引用式关系
手动引用
这种方法把用户数据文档和用户地址数据文档分开,通过引用文档的 id 字段来建立关系
{
"_id":ObjectId("52ffc33cd85242f436000001"),
"contact": "987654321",
"dob": "01-01-1991",
"name": "Tom Benzamin",
"address_ids": [
ObjectId("52ffc4a5d85242602e000000"),
ObjectId("52ffc4a5d85242602e000001")
]
}
DBRefs
{
"_id":ObjectId("53402597d852426020000002"),
"address": {
"$ref": "address_home",
"$id": ObjectId("534009e4d852427820000002"),
"$db": "runoob"
},
"contact": "987654321",
"dob": "01-01-1991",
"name": "Tom Benzamin"
}
- $ref:集合名称
- $id:引用的id
- $db:数据库名称,可选参数
查询分析 explain()
db.users.find({gender:"M"},{user_name:1,_id:0}).explain()
结果集的字段解释:
- indexOnly: 字段为 true ,表示我们使用了索引。
- cursor:因为这个查询使用了索引,MongoDB 中索引存储在B树结构中,所以这是也使用了 BtreeCursor 类型的游标。如果没有使用索引,游标的类型是 BasicCursor。这个键还会给出你所使用的索引的名称,你通过这个名称可以查看当前数据库下的system.indexes集合(系统自动创建,由于存储索引信息,这个稍微会提到)来得到索引的详细信息。
- n:当前查询返回的文档数量。
- nscanned / nscannedObjects:表明当前这次查询一共扫描了集合中多少个文档,我们的目的是,让这个数值和返回文档的数量越接近越好。
- millis:当前查询所需时间,毫秒数。
- indexBounds:当前查询具体使用的索引。
指定索引查询 hint()
db.users.find({gender:“M”},{user_name:1,_id:0}).hint({gender:1,user_name:1})
原子操作
mongodb不支持事务
但是mongodb提供了许多原子操作,比如文档的保存,修改,删除等,都是原子操作。
$set
用来指定一个键并更新键值,若键不存在并创建。
{ $set : { field : value } }
$unset
用来删除一个键。
{ $unset : { field : 1} }
$inc
可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作。
{ $inc : { field : value } }
$push
用法:
{ $push : { field : value } }
把value追加到field里面去,field一定要是数组类型才行,如果field不存在,会新增一个数组类型加进去。
$pushAll
同$push,只是一次可以追加多个值到一个数组字段内。
{ $pushAll : { field : value_array } }
$pull
从数组field内删除一个等于value值。
{ $pull : { field : _value } }
$addToSet
增加一个值到数组内,而且只有当这个值不在数组内才增加。
$pop
删除数组的第一个或最后一个元素
{ $pop : { field : 1 } }
$rename
修改字段名称
{ $rename : { old_field_name : new_field_name } }
$bit
位操作,integer类型
{$bit : { field : {and : 5}}}
偏移操作符
> t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 3 }, { "by" : "jane", "votes" : 7 } ] }
> t.update( {'comments.by':'joe'}, {$inc:{'comments.$.votes':1}}, false, true )
> t.find() { "_id" : ObjectId("4b97e62bf1d8c7152c9ccb74"), "title" : "ABC", "comments" : [ { "by" : "joe", "votes" : 4 }, { "by" : "jane", "votes" : 7 } ] }
索引限制
索引会有额外开销
查询限制
索引不能被以下的查询使用:
- 正则表达式及非操作符,如 $nin, $not, 等。
- 算术运算符,如 $mod, 等。
- $where 子句
最大范围
- 集合中索引不能超过64个
- 索引名的长度不能超过128个字符
- 一个复合索引最多可以有31个字段
ObjectId
MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任何类型的,默认是个ObjectId对象。
.
在一个集合里面,每个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标识。
.
MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个 服务器上同步自动增加主键值既费力还费时。
Map Reduce
Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。
基本语法:
>db.collection.mapReduce(
function() {emit(key,value);}, //map 函数
function(key,values) {return reduceFunction}, //reduce 函数
{
out: collection,
query: document,
sort: document,
limit: number
}
)
使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value), 遍历 collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进行处理。
Map 函数必须调用 emit(key, value) 返回键值对。
参数说明:
- map :映射函数 (生成键值对序列,作为 reduce 函数参数)。
- reduce: 统计函数,reduce函数的任务就是将key-values变成key-value,也就是把values数组变成一个单一的值value。。
- out :统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
- query :一个筛选条件,只有满足条件的文档才会调用map函数。(query。- limit,sort可以随意组合)
- sort :和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
- limit :发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)
输出结果:
{
"result" : "post_total",
"timeMillis" : 23,
"counts" : {
"input" : 5,
"emit" : 5,
"reduce" : 1,
"output" : 2
},
"ok" : 1
}
具体参数说明:
- result:储存结果的collection的名字,这是个临时集合,MapReduce的连接关闭后自动就被删除了。
- timeMillis:执行花费的时间,毫秒为单位
- input:满足条件被发送到map函数的文档个数
- emit:在map函数中emit被调用的次数,也就是所有集合中的数据总量
- output:结果集合中的文档个数(count对调试非常有帮助)
- ok:是否成功,成功为1
- err:如果失败,这里可以有失败原因,不过从经验上来看,原因比较模糊,作用不大
GridFS
GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。
GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。
GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。
GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。
添加
mongofiles.exe -d gridfs put song.mp3
-d gridfs 指定存储文件的数据库名称,如果不存在该数据库,MongoDB会自动创建
查看
> db.fs.files.find()
{
_id: ObjectId('534a811bf8b4aa4d33fdf94d'),
filename: "song.mp3",
chunkSize: 261120,
uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41",
length: 10401959
}
> db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})
以上实例中,查询返回了 40 个文档的数据,意味着mp3文件被存储在40个区块中。
固定集合(Capped Collections)
MongoDB 固定集合(Capped Collections)是性能出色且有着固定大小的集合,对于大小固定,我们可以想象其就像一个环形队列,当集合空间用完后,再插入的元素就会覆盖最初始的头部的元素!
创建固定集合
我们通过createCollection来创建一个固定集合,且capped选项设置为true:
>db.createCollection("cappedLogCollection",{capped:true,size:10000})
还可以指定文档个数,加上max:1000属性:
>db.createCollection("cappedLogCollection",{capped:true,size:10000,max:1000})
判断集合是否为固定集合:
>db.cappedLogCollection.isCapped()
如果需要将已存在的集合转换为固定集合可以使用以下命令:
>db.runCommand({"convertToCapped":"posts",size:10000})
固定集合查询
固定集合文档按照插入顺序储存的,默认情况下查询就是按照插入顺序返回的,也可以使用$natural调整返回顺序。
>db.cappedLogCollection.find().sort({$natural:-1})
固定集合的功能特点
可以插入及更新,但更新不能超出collection的大小,否则更新失败,不允许删除,但是可以调用drop()删除集合中的所有行,但是drop后需要显式地重建集合。
在32位机子上一个cappped collection的最大值约为482.5M,64位上只受系统文件大小的限制。
固定集合属性及用法
- 属性
- 属性1:对固定集合进行插入速度极快
- 属性2:按照插入顺序的查询输出速度极快
- 属性3:能够在插入最新数据时,淘汰最早的数据
- 用法
- 用法1:储存日志信息
- 用法2:缓存一些少量的文档