mongo.docs
2017-01-04 09:48:45 49 举报
AI智能生成
mongo
作者其他创作
大纲/内容
1. Introduction
document database
关键特点
高性能
支持嵌入式数据模型,减少数据库IO次数
索引支持快速查询,可以包含embedded document和array的key
丰富的查询语句
丰富的聚合操作
全文查询和地理位置查询
高可用性
自动故障切换
数据冗余
replica set
一组与mongo服务器具有相同的数据集的服务,并提供数据增加和删除
水平扩展性
通过机器集群分布式存储数据
在一个mongo的集群中,通过key来分配数据的读写
多种存储引擎
WiredTiger
MMAPv1
mongo提供编程接口以使用第三方的存储引擎
每一条record(key:value)就是一个document
document是类似于json object
database and collections
mongo数据库存储bson格式的document到collection
databases
存储包含documents的collections
use mydb
在mongo shell选择要使用的数据库
database会在第一次存储数据时创建
collections
mongo存储documents到collection
collection会在第一次存储数据或创建索引时创建
db.createCollections()
明确的创建collection
如果不需要明确的指定相应的参数去创建collection,则可以在第一次插入数据时创建collection
document volidation
在存储docuemnt时,不需要使用相同的模式
即不需要相同的字段名,字段类型,字段个数
在3.2后的版本中,可以在插入或更新时强制document校验规则
在需要修改docuemnt的结构时(增加字段,删除字段,改变值),会直接更新整个document的结构
只读视图
从3.4版本开始支持从collection或其他视图创建只读视图
创建视图
db.runCommand( { create: <view>, viewOn: <source>, pipeline: <pipeline> } )
db.runCommand( { create: <view>, viewOn: <source>, pipeline: <pipeline>, collation: <collation> } )
db.createView(<view>, <source>, <pipeline>, <collation> )
新版本的mongo shell支持该命令
操作
只读,写视图会报错
视图会使用基于的collection的视图
如果底层的collection是分片的,视图会考虑分片
可以通过指定$lookup和$graphLookup在创建视图时进行分片
视图是在对collection查找时生成的,
mongo在读视图时当成对底层collcetion的聚合操作
不支持如下操作
db.collection.mapReduce()
全文搜索
geoNear命令和$geoNear pipeline stage
如果聚合操作不使用_id字段,则视图中无_id字段
视图不能改名
字符串在视图上的比较是基于视图的默认collection
试图通过视图改变底层collection的
创建视图时避免使用敏感数据,在执行计划和db.getCollectionInfos()都会被看到
删除视图
db.collection.drop()
没尝试,感觉应该是db.view.drop()
Capped collections
固定大小的collection
用于支持高吞吐量的插入和恢复操作
工作方式类型于循环缓存,当空间满时,会覆盖最旧的数据
操作
插入顺序
保证插入命令的保存顺序
查询不需要索引以插入顺序返回文档
由于不需要以插入方式索引返回,因此可以有很高的插入效率
自动清理最陈旧数据
_id索引
默认存在_id字段和_id索引
限制和推荐做法
更新
需要更新的话,最好创建一个对应的索引,防止全表扫描
document大小
在更新操作会修改document大小时,会操作失败
删除
document不可删除
如果删除全部document需要使用drop()方法,重建capped collection
分片
不可分片
查询效率
使用自然排序自动获取最近插入的数据
$out聚合
不可用
使用
创建
db.createCollection(<name>, { capped: <boolean>,
autoIndexId: <boolean>,
size: <number>,
max: <number>,
storageEngine: <document>,
validator: <document>,
validationLevel: <string>,
validationAction: <string>,
indexOptionDefaults: <document> } )
capped
true
size
collection最大大小,最小值为4096bytes
size为256的整数倍
max
document最大大小
查询
db.cappedCollection.find()
默认mongo会保证按插入顺序排序
可以使用sort指定排序方式
isCapped()
是否capped
转换collection为capped collection
db.runCommand({"convertToCapped": "<collection_name>", size: 100000});
该操作包含一个全局写锁,直到转换完成
执行过程
mongo会将所有的document按自然顺序排序后写入新的capped collection
如果指定的size过小,会覆盖旧的数据
内部调用
cloneCollectionAsCapped创建新的capped collection并导入数据
删除原collection
renameCollection将capped collection重命名
在分片集群中不支持convertToCapped命令
索引不会在转换过程中重建,需要手工重建
转换过程中会自动创建_id为索引
documents
mongo存储数据记录使用bson格式
数据结构
key:value对
字段名
字段名只能为string类型
限制
_id为保留字段,且为主键、不可变,可以是除了数组以外的其他类型
不可以$开头
不可包含.
不可包含null字符
大部分的接口在同下结构下不支持同名字段,若需要同名字段,可查看驱动文档
字段值限制
索引包含bson结构字段值的长度限制为1024bytes
点符号
mongo使用.获取bson结构的数组或嵌入的文档中的字段
array
<array>.<index>
index起始值为0
嵌入式文档
<embedded document>.<field>
限制
大小限制
最大bson document大小为16M
防止单条记录在进程中过多占用内存,或在传输过程中过多占用带宽
可以使用GridFS API存储大于16M的数据
字段顺序
_id永远是第一个字段
使用$rename更改字段名,可能会导致document重新排序
在2.6版本后,mongo会尝试保护字段顺序
_id字段
在创建collection时,默认会创建_id字段,并创建唯一索引
_id字段是document的第一个字段,若第一个字段不是_id,mongo会自动移动开始
可以是除array以外的其他bson类型
bson types
类型
Type Number Alias Notes
Double 1 “double”
String 2 “string”
Object 3 “object”
Array 4 “array”
Binary data 5 “binData”
Undefined 6 “undefined” Deprecated.
ObjectId 7 “objectId”
特点:小、唯一、快速生成和排序
格式
1-4字节时间戳
5-8字节机器示码
8-9字节进程ID
10-12字节随机数
collection中所有的document都有_id字段,若插入时无此字段,则会自动生成此类型的_id
Boolean 8 “bool”
Date 9 “date”
Null 10 “null”
Regular Expression 11 “regex”
DBPointer 12 “dbPointer” Deprecated.
JavaScript 13 “javascript”
Symbol 14 “symbol” Deprecated.
JavaScript (with scope) 15 “javascriptWithScope”
32-bit integer 16 “int”
Timestamp 17 “timestamp”
64-bit integer 18 “long”
Min key -1 “minKey”
Max key 127 “maxKey”
对应的数值及alias值可用于$type值查询使用
对比/排序指令
对不同bson类型的值进行对比并由低到高排序时,按如下顺序排序
MinKey (internal type)
Null
Numbers (ints, longs, doubles)
Symbol, String
Object
Array
BinData
比较
根据字段长度进行比较
by the BSON one-byte subtype
逐字节比较
ObjectId
Boolean
Date
Timestamp
Regular Expression
MaxKey (internal type)
当排序的字段不存在是,将字段值视为null
4. CURD操作
insert操作
insert方法
insertOne
在3.2版本中新增
插入一条document,返回该document的_id字段
db.collection.insertOne(
<document>,
{
writeConcern: <document>
}
)
insertMany
在3.2版本中新增
插入多条document
db.collection.insertMany(
{ [ <document 1> , <document 2>, ... ] },
{
writeConcern: <document>,
ordered: <boolean>
}
)
ordered
是否按顺序插入,默认为true
insert
插入一条或多条document
db.collection.insert(
<document or array of documents>,
{
writeConcern: <document>,
ordered: <boolean>
}
)
ordered
是否按顺序插入,默认为true
true,若其中一条失败,则不继续执行
false,若其中一条失败,则继续执行
操作
若collection不存在,创建collection
若插入时忽略_id字段,则会自动生成ObjectId类型的_id
在写单条记录时,都是原子操作的
query操作
query方法
find
db.collection.find( <query filter>, <projection> )
query
过滤条件
查询过滤条件
相等
{ <field1>: <value1>, ... }
查询操作符
{ <field1>: { <operator1>: <value1> }, ... }
比较运算符
$eq
$gt
$gte
$lt
$lte
$ne
$in
在执行相等操作时,用$in而不是$or,将条件放入数组中
$nin
逻辑运算符
$or
将条件放入数组当中
$and
$not
$nor
元素类型匹配
$exists
存在指定字段
$type
存在指定类型的字段
Evaluation
$mod
对指定字段的值进行模操作,并需要匹配对应的值
$regex
匹配正则表达式
$text
全文搜索
$where
文档需要满足javascript表达式
地址位置计算
$geoWithin
$geoIntersects
$near
$nearSphere
数组
$all
数组必须包含所有字段
$elemMatch
匹配数组中至少一个文档匹配指定条件
$size
数组字段需要匹配指定大小
Bitwise
$bitsAllSet
$bitsAnySet
$bitsAllClear
$bitsAnyClear
Projection
$
$elemMatch
$meta
$slice
嵌入式文档查询
db.users.find( { favorites: { artist: "Picasso", food: "pizza" } } )
db.users.find( { "favorites.artist": "Picasso" } )
数组
数组内个数精确匹配
db.users.find( { badges: [ "blue", "black" ] } )
匹配数组中一个
db.users.find( { badges: "black" } )
匹配指定数组位置的元素
db.users.find( { "badges.0": "black" } )
数组中至少有一个元素满足所有条件
db.users.find( { finished: { $elemMatch: { $gt: 15, $lt: 20 } } } )
数级中一个或多个元素满足全部条件
db.users.find( { finished: { $gt: 15, $lt: 20 } } )
使用数组索引匹配嵌入式文档的字段
db.users.find( { 'points.0.points': { $lte: 55 } } )
数组中所有文档中至少一个对应字段的值满足条件
此时不需要知道文档的索引
db.users.find( { 'points.points': { $lte: 55 } } )
匹配数组中所有文档中至少一个文档满足所有条件
db.users.find( { points: { $elemMatch: { points: { $lte: 70 }, bonus: 20 } } } )
匹配数组中所有文档,只要一个或不同文档中的字段满足所有条件即可
db.users.find( { "points.points": { $lte: 70 }, "points.bonus": 20 } )
Projection Document
{ field1: <value>, field2: <value> ... }
value
1/true,显示字段
2/false,隐藏字段
用于在返回的文档中是不显示字段值
除_id字段外,不可同时即有展示类型、有又隐藏类型,
Projection
展示指定字段和_id字段
db.users.find( { status: "A" }, { name: 1, status: 1 } )
展示指定字段,不显示_id字段
db.users.find( { status: "A" }, { name: 1, status: 1, _id: 0 } )
展示除隐藏字段以外的字段
db.users.find( { status: "A" }, { favorites: 0, points: 0 } )
展示指定字段和嵌入式字段
db.users.find(
{ status: "A" },
{ name: 1, status: 1, "favorites.food": 1 }
)
展示除隐藏的嵌入式文档字段外的字段
db.users.find(
{ status: "A" },
{ "favorites.food": 0 }
)
展示指定字段和数组中文档中指定的字段
db.users.find( { status: "A" }, { name: 1, status: 1, "points.bonus": 1 } )
展示指定字段和数组所有文档中最后一个字段
$slice
后为索引
只能用此种方式指定返回数组对应索引位置的文档
db.users.find( { status: "A" }, { name: 1, status: 1, points: { $slice: -1 } } )
Null/无此字段
查询字段为Null或无此字段
db.users.find( { name: null } )
若使用sparse索引,则只返回Null字段
查询字段存在且为Null
db.users.find( { name : { $type: 10 } } )
查询不存在此字段的文档
db.users.find( { name : { $exists: false } } )
cursor
db.collection.find()返回cursor,则需要迭代cursor获取文档
若执行时没有指定var关键字,则会直接打印前20条记录
操作
var myCursor = db.users.find( )
循环操作
while (myCursor.hasNext()) {
print(tojson(myCursor.next()));
}
while (myCursor.hasNext()) {
printjson(myCursor.next());
}
myCursor.forEach(printjson)
转换为数组
var myCursor = db.users.find()
var documentArray = myCursor.toArray()
var myDocument = documentArray[3]
行为
超过十分钟不活动的cursor会被自动关闭,可以使用db.users.find().noCursorTimeout()永久打开
使用该方法,需要手工关闭cursor或遍历完所有的数据
corsor隔离
在操作cursor时可能会有其他操作在该文档上,导致文档结构或索引发生变化,cursor可能将同一文档返回多次。
若要保证文档只返回一次,可以使用 cursor.snapshot()
cursor批量操作
mongodb批量返回cursor查询结果,批量大小限制为最大bson文档大小。
第一批返回101条文档,或文档大小达到1M,随后返回批量大小最大为4M
batchSize
设置每个批次返回文档数据量的大小
使用1或负值等同于使用limit()
limit
返回文档的最大数量
0-无返回限制
负值-同设置正值相同,但返回第一批数据后自动关闭cursor
cursor信息
db.serverStatus()
从最近一次服务器重启到现在
所有超时的cursor
所有设置noTimeout方法的打开的cursor
所有打开的pinned cursor
所有打开的cursor
projection
限制返回记录条数
update操作
方法
db.collection.updateOne(
<filter>, <update>,
{
upsert: <boolean>,
writeConcern: <document>
}
)
即使匹配的文档数有多条,也只更新一条文档
db.collection.updateMany(
<filter>, <update>,
{
upsert: <boolean>,
writeConcern: <document>
}
)
更新所有匹配的文档
db.collection.replaceOne(
<filter>, <replacement>,
{
upsert: <boolean>,
writeConcern: <document>
}
)
即使匹配的文档数有多条,也只替换一条文档
db.collection.update(
<query>, <update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
更新或替换一条或全部匹配的文档
其他方法
db.collection.findOneAndReplace(
<filter>,
<replacement>,
{
projection: <document>,
sort: <document>,
maxTimeMS: <number>,
upsert: <boolean>,
returnNewDocument: <boolean>
}
)
db.collection.findOneAndUpdate(
<filter>,
<update>,
{
projection: <document>,
sort: <document>,
maxTimeMS: <number>,
upsert: <boolean>,
returnNewDocument: <boolean>
}
)
db.collection.findAndModify({
query: <document>,
sort: <document>,
remove: <boolean>,
update: <document>,
new: <boolean>,
fields: <document>,
upsert: <boolean>,
bypassDocumentValidation: <boolean>,
writeConcern: <document>
});
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
db.collection.bulkWrite(
[ <operation 1>, <operation 2>, ... ],
{
writeConcern : <document>,
ordered : <boolean>
}
)
操作
在mongodb中单个文档的操作都是原子的
_id字段一旦设置,就不可以更新或替换
当更新一个文档超过为它分配的空间后,mongo会将他转移到其他地方存储
字段顺序
mongo会按字段的插入顺序保存到文档中
_id永远会排在第一个字段
更新字段名称可能会改变文档中字段的排序
upsert
为true时,当collection中无此文档时,会插入该文档
delete操作
方法
db.collection.remove(
<query>,
<justOne>
)
db.collection.deleteOne(
<filter>,
{
writeConcern: <document>
}
)
db.collection.deleteMany(
<filter>,
{
writeConcern: <document>
}
)
操作
删除操作不删除索引,即使删除全部文档
原子性
所有针对单个文档的操作都是原子的
Bulk Write操作
在3.2版本中新增
方法
db.collection.bulkWrite(
[ <operation 1>, <operation 2>, ... ],
{
writeConcern : <document>,
ordered : <boolean>
}
)
operation
insertOne
db.collection.bulkWrite( [
{ insertOne : { "document" : <document> } }
] )
updateOne
db.collection.bulkWrite( [
{ updateOne :
{
"filter" : <document>,
"update" : <document>,
"upsert" : <boolean>
}
}
] )
updateMany
db.collection.bulkWrite( [
{ updateMany :
{
"filter" : <document>,
"update" : <document>,
"upsert" : <boolean>
}
}
] )
子主题
db.collection.bulkWrite([
{ deleteOne : { "filter" : <document> } }
] )
deleteMany
db.collection.bulkWrite([
{ deleteMany : { "filter" : <document> } }
] )
子主题
db.collection.bulkWrite([
{ replaceOne :
{
"filter" : <document>,
"replacement" : <document>,
"upsert" : <boolean>
}
}
] )
操作
对于一系列的顺序操作,mongo会连续的操作。
如果其中一个报错,mongo会返回并中断操作后续操作
对于无序的操作,mongo会并行执行(并不能保证)
当其中报错时,其他操作会继续执行
默认执行顺序操作,设置ordered为flase可并行执行
在一个分片的collection上执行顺序操作要比无序操作慢,
是由于每个操作必须等前一个操作完成才行
分片collection操作策略
提前拆分collection
当集合为空时,collection只有一个shard并包含一个初始的chunk
mongodb必须接收数据,创建分片,将不同的chunk发到shard
无序写入
为提供分片集群的写入速度将ordered设置为false,mongo会并发操作
避免单调操作
如果数据的shard key是单调递增,则会导致数据全部写入最后的shard中
则集群的容量处理能力无法超过单片shard
SQL vs mongo
概念
sql : mongo
database : database
table : collection
row : document
column : field
index : index
table join : embeded document linking
primary key : primary key
aggregation : aggregation pipeline
Read Concern
3.2新增
隔离级别
majority
读取已被写入主副本集的不可回退的数据
local
默认。返回在查询时最近可用数据,即使数据没有被持久的存储到主副本集中,即返回的数据可能会被回滚
Write Concern
mongo对单点mongo、复本集或分片集群写操作的确认级别
在分片集群,写操作会分配到对应shard上
{ w: <value>, j: <boolean>, wtimeout: <number> }
w-确认写操作已经被发送到指定的mongod实例或mongo实例指定的tags
number
1-单点mongo或复本集中主节点,默认值
0-写操作不需要确认
majority
写操作已经被发送到选举出的主要节点,包括主节点
tag set
写操作确认被发送到副本集中指定tag
j-确认写操作已经被记录到日志中
mongo返回已经写入日志的节点的数量,包括主节点
wtimeout-在指定的时间内防止无限的阻塞操作
以毫秒为单位,仅限于w大于1
原子、持久、分布式操作
查询计划、性能和分析
CURD概念
原子处理和事务
当一个写操作影响多个文档的时候,每个文档的执行是原子的,
但是多个文档不是原子的,其他的操作可能交差其中
$isolated操作符
使用该操作符,则当多个文档的写操作开始修改第一个文档时,则会阻止其他处理程序对文档的写操作
不能在分片集群中使用
当异常发生时,不会进行回滚操作
并发控制
防止并发的冲突和不一致性
唯一索引
指定一个确认的期望值
独立性、并发性、recency
隔离性
未提交读
writeConcern使用local会使客户端看到未提交的数据
readConcern使用local会使客户端读到随后会被回滚的数据
未提交读和单个文档的原子性
对于正在更新的字段,读操作是看不到被更改的字段的,但是可以看到改之前的字段
在单节点的mongod中,一系列的读和写操作是串行执行的
在副本集中,只有不回退的读和写操作是串行执行的
未提交读和多文档写
单个写操作涉及多个文档时,对每个文档的操作都是原子的,但整个的写操作不是原子的
游标快照
当在执行查询,如果有更新操作导致文档移动或索引被修改,则可能导致返回多条重复记录
可以全用snapshot()保证查询返回的不重复
使用唯一索引查询可以避免查询返回数据的不重复
单调写
mongo保证对于单点mongod、复本集、分片集群,同一个应用的连续写操作是按顺序执行的
分布式查询
集群读
用户直接将操作发送到集群的一个mongo实例上,集群的读操作对于用户来说是透明的
当从collection中读取数据时,如果指定了collection's 分片key,mongo会直接指定对应的分片读取数据,这样会更有效率
未指定shard key时,会将查询分发到所的有shard中,这种分发再收集的机会比较低效,这种操作尽量在日常工作中避免
复本集读
从从复本中读取数据不会影响主复本数据
从不同的复本集读会导致非单调的读取
可以对每个连接或每个操作进行复本集读设置
分布式写
集群写
mongo从config database读取集群元数据,将写操作路由到各个分片
mongo集群是以shard key进行分片的,然后将不同的chunk发布到不同的shard
shard key的设置会影响写的性能
更新单条记录必须有shard key或_id字段,更新多条记录如果有shard key则会使用广播且更有效率
如果shard key的值是递增或递减的,则会插入到单个shard中
则单个shard的容量会成为整个集群的瓶颈
复本集写
在复本集中,所有的写操作都会汇总的主复本集中,并将操作记录到log和oplog中,oplog是可循环写的
从复本集会异步的循环从主副本集中同步oplog,并将操作记录到本地
两阶段提交
mongo只提供单个文档的原子处理,对于多文档处理,只提供类事务处理
在执行两阶段提交或回滚时,mongo可以返回执行中的中间状态的数据
通过一个中间表,加一个中间状态。以中间表的中间状态为准,逐步处理完成事务
回退
保存历史信息
确认回退标示
进行回退处理
回退完成更新标示
事务处理类似于无状态处理,所有的数据都保存在数据库中,
应用只针对各个状态进行处理,应用不保存数据,只处理数据,因此在某种程序上可以实现事务
从复本集读取指定的数据
readConcern
local,读取的数据为最新数据,但数据可能在其他复本可能未完全处理完成
majority,读取的数据为历史数据,由于其他复本集未执行完成,因此读取的数据为未更新的数据
执行计划
缓存刷新
目录操作,类似生成索引或删除collection会刷新执行计划缓存
执行计划缓存在mongo重启或关闭时会消失
查询优化
总序
单个索引的排序并不重要,复合索引的排序很重要
查询条件会决定是否有效的使用索引以及是否使用索引
覆盖查询
所有的查询条件字段都在索引中
所有返回的结果字段都在索引中
如果索引覆盖了所有的查询条件和查询结果,则mongo只需要索引就可以完成处理
限制
索引字段
如果索引的字段中存在数据,则不能使用覆盖查询
如果索引字段是嵌入文档,虽然会使用索引,但同时会检查文档以满足查询需要
分片
如果索引中不包含分片key,则在分片collection上的查询不会使用覆盖查询
可以使用db.collection.explain()来确认是否是覆盖查询
性能评估
db.currentOp()
对当前的操作生成报告
评估查询性能
cursor.explain(verbosity)
db.collection.explain(verbosity)
verbosity
queryPlanner
默认值
返回queryPlanner信息
executionStats
选择最佳计划并执行计划,返回计划执行的统计数据
allPlansExecution
选择最佳计划并执行计划,返回计划执行的统计数据。同时也会返回在选择计划时,生成的候选计划的统计数据
优化查询性能
创建索引支持查询
索引key是二binData会更有效
二进制子类型值为1~7、128~135
byte数组长度为0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, or 32
减少返回文档数量,以降低网络使用
cursors的按组返回多个文档
limit
仅返回需要的字段
用$hint调用指定的索引
使用$inc操作符增加或减小数据
避免从服务端查询后再写回到服务端
避免多个写冲突
写操作性能
所有的insert、remove操作都会在对应的索引上对对应的key进行insert或remove操作
当更新操作修改索引的key时,可能会导致更新索引的子集
更新操作可能会导致文档变大,而被mongo移动位置,导致更新索引,而这些移动操作是最耗费性能的
日志
为保证mongo正常运行,在写操作之前会记录操作日志。在进行写操作时若遇到异常,则会可以日志文件进行恢复
优化
将日志文件和数据文件划分到不同磁盘
若指定了j(写确认)选项,mongo会降低写日志操作,该参数会增加写操作的负载
使用commitIntervalMs参数控制写周期
降低该值会增加写操作的频率、
增加该值会降低写入次数,但会增加写操作异常时可能未写入日志的可能性
执行计划
查询方式
db.collection.explain()
explain命令
处理阶段
COLLSCAN-集合扫描
IXSCAN-索引扫描
FETCH-检索数据
SHARD_MERGE-从分片合并数据
查询计划输出
queryPlanner
分片mongo生成shards array,包含各shard的执行信息
单节点mongo执行计划如下
explain.queryPlanner.namespace
命名空间<database>.<collection>
explain.queryPlanner.indexFilterSet
bool,是否使用索引过滤器
explain.queryPlanner.winningPlan
被查询优化器选中的执行计划
explain.queryPlanner.winningPlan.stage
当前计划的执行阶段
explain.queryPlanner.winningPlan.inputStage
子阶段
explain.queryPlanner.winningPlan.inputStages
子阶段数组
explain.queryPlanner.rejectedPlans
候选执行计划数组,被抛弃的执行计划
executionStats
要查看执行状态信息需要在两种模式下查询
executionStats
allPlansExecution
要获取在所有的执行计划(候选计划)的数据统计,必须使用该项
单节点mongo执行计划如下
explain.executionStats
完整的选中的执行计划的统计信息
explain.executionStats.nReturned
满足条件的文档数
相当于cursor.explain()返回的n字段
explain.executionStats.executionTimeMillis
选择执行计划和查询数据的总时间
相当于cursor.explain()返回的mills字段
explain.executionStats.totalKeysExamined
扫描索引的个数
相当于cursor.explain()返回的totalKeysExamined字段
explain.executionStats.totalDocsExamined
扫描文档的个数
相当于cursor.explain()返回的nscannedObjects字段
explain.executionStats.executionStages
详细完整的选中执行的执行计划
explain.executionStats.executionStages.works
查询执行阶段,执行的work united个数
explain.executionStats.executionStages.advanced
优化返回给上一阶段的结果集数
explain.executionStats.executionStages.needTime
explain.executionStats.executionStages.needYield
在存储层请求n次后,查询系统会放弃锁
explain.executionStats.executionStages.isEOF
执行阶段是否已经到达流尾
explain.executionStats.executionStages.inputStage.keysExamined
explain.executionStats.executionStages.inputStage.docsExamined
explain.executionStats.allPlansExecution
所有的执行计划(包括选中和候选计划)
serverInfo
host
port
version
gitVersion
Sharded Collection
返回每个分片的执行计划和服务器信息
explain.queryPlanner.winningPlan.shards
explain.queryPlanner.winningPlan.shards
覆盖查询
totalDocsExamined为0
$or
如果使用$or表达试,则会存在or阶段
排序阶段
如果使用索引进行排序,则无排序阶段;若没有使用索引进行排序,则会出现排序阶段
查询性能分析
根据查询条件,使用唯一性较高的字段所在的索引
5. 聚合
综述
三种聚合方式
aggregation pipeline
可以在分片collection上操作
map-reduce function
比aggregation pipeline更灵活,比aggregation pipeline更低效、更复杂
可以在分片collection上操作
single purpose aggregation methods
指定目的的聚合操作
db.collection.count()
db.collection.group()
db.collection.distinct()
在3.2版本之后,在索引上可以直接进行聚合操作,2.6-3.0之间可以使用索引,但还会去查询文档
使用$match,$limit,$skip在聚合操作之前,对结果集进行过滤、限制
在$match上使用$sort操作和在索引上进行查询具体相同的排序结果
聚合管道
聚合管道优化
字段规划优化
聚合操作会选择聚合相关的字段进行操作,以减少数据在管道的传输
管道顺序优化
$sort+$match优化
优化器会把$match放到$sort前,对结果集进行过滤后再排序
$skip+$limit优化
优化器会将$limit放在$skep之前
例
{ $skip: 10 },
{ $limit: 5 }
{ $limit: 15 },
{ $skip: 10 }
在分片中,这项优化会减少各分片的返回结果
$redact+$match优化
优化器会将$match中使用索引的部分移动到$redact之前,对结果集先进行过滤
$project+$skip/$limit优化
优化器会将$skep/$limit移动到$project之前
管道合并优化
$sort+$limit合并
将$limit操作合并到$sort中,减少排序量
$limit+$limit合并
将$limit整合为其中较少的一个
$skip+$skip合并
将两个$skip的值相加合并成为一个$skip
$match+$match合并
将两个$match使用$and合并成为一个$match
$lookup+$unwind合并
优化器会将$unwind合并到$lookup中
例
{
$lookup: {
from: "otherCollection",
as: "resultingArray",
localField: "x",
foreignField: "y"
}
},
{ $unwind: "$resultingArray"}
在一个非分片的collection中,使用左连接查询
from
同一数据库内的集合
localField
本collection中的字段
对于不存在对应字段的文档,字段值使用NULL代替
foreignField
collection(from)中的字段,对locaField和foreignField进行相等操作,
对于不存在对应字段的文档,字段值使用NULL代替
as
在本地文档中增加的数组的字段名,数组中存放匹配的collection(from)中的文档,
若本地文档中已有此字段,则直接覆盖
{
$lookup: {
from: "otherCollection",
as: "resultingArray",
localField: "x",
foreignField: "y",
unwinding: { preserveNullAndEmptyArrays: false }
}
}
聚合管道限制
结果大小限制
聚合操作可以返回一个游标或将结果存储到collection中
结果集中每个文档都需要遵守bson文件大小限制,现在是16M
若单个文档超过16M,在聚合操作时可以正常进行,但在返回结果集时会报错
若未对返回的邮件或结果集设置参数,则会返回一个大的文档,包括整个结果集
若结果集超过16M则会报错
内存限制
管道阶段仅可以使用100M内存
使用allowDiskUse参数,使管道操作可以将数据写入临时文件
分片collection聚合操作
行为
若$match操作指定了shard key,则会只在指定的分片上执行$match操作,数据的合并会在主分片上执行
如果操作不必须在主分片上操作,为防止主分片过载,结果集将会被随机路由到一个分片对结果进行合并
$out和$lookup操作必须在主分片上执行合并操作
优化
如果聚合操作被分成两部分,管理操作会在充分考虑和优化的情况下,将尽可能多的步骤交给同一分片操作
例
db.zipcodes.aggregate( [
{ $group:
{
_id: { state: "$state", city: "$city" },
pop: { $sum: "$pop" }
}
},
{ $sort: { pop: 1 } },
{ $group:
{
_id : "$_id.state",
biggestCity: { $last: "$_id.city" },
biggestPop: { $last: "$pop" },
smallestCity: { $first: "$_id.city" },
smallestPop: { $first: "$pop" }
}
},
{ $project:
{ _id: 0,
state: "$_id",
biggestCity: { name: "$biggestCity", pop: "$biggestPop" },
smallestCity: { name: "$smallestCity", pop: "$smallestPop" }
}
}
] )
1. group
按state,city进行聚合操作
2. sort
正序排序
3. group
按state,取最大人口和最低人口
4. project
不显示_id字段
使用state为_id字段
将biggestCity、biggestPop嵌入到biggestCity
将smallestCity、smallestPop嵌入到smallestCity
db.users.aggregate(
[
{ $project : { month_joined : { $month : "$joined" } } } ,
{ $group : { _id : {month_joined:"$month_joined"} , number : { $sum : 1 } } },
{ $sort : { "_id.month_joined" : 1 } }
]
)
1. project
将joined的日期类型转换为月份,并将key转换为month_joined
2. group
按month_joined进行聚合操作,计算每个月加入人数
3. sort
按month_joined进行排序
db.users.aggregate(
[
{ $unwind : "$likes" },
{ $group : { _id : "$likes" , number : { $sum : 1 } } },
{ $sort : { number : -1 } },
{ $limit : 5 }
]
)
1. unwind
将likes数组进行拆解,在文档其他元素不变的情况下,对各个元素生成一个文档
2. group
按likes元素进行分组,number字段展示每组共有多少人
3. sort
按number进行倒序排序
4. limit
展示前五条记录
MapReduce
总述
所有的map-reduce函数都是javascript函数
输入输出的collection可以是分片的
当返回结果集时,文档大小必须在bson文件大小限制之内(16M)
MapReduce和分片collection
分片collection做为输入
mongo自动的将任务并发的分发到各个分片
mongo会等待所有的分片执行完成
分片collection做为输出
如果输出的colletion不存在,mongo会自动创建colletion并自动分片
如果collection未创建或为空,mongo会将第一阶段的结果生成的chunk分发初始化各个分片
mongo会并行的给各分片分发一个chunk用于后处理工作。
在后处理工作中,各分片会去其他分片拉取属于自己chunk的数据,并保存到输出collection中
MapReduce并发性
锁
1. 在读阶段会生成读锁。每一百条文档会放弃一次
2. 在向临时collection插入文档时会为每个写操作生成写锁
3. 如果输出collection不存在,则在创建输出collection时会生成写锁
4. 如果输出collection存在,则在输出事件(merge, replace, reduce)时会生成写锁
这个写锁是全局锁,所有在mongo实例上的写操作会被阻塞
map-reduce例
prototype
{
_id: ObjectId("50a8240b927d5d8b5891743c"),
cust_id: "abc123",
ord_date: new Date("Oct 04, 2012"),
status: 'A',
price: 25,
items: [ { sku: "mmm", qty: 5, price: 2.5 },
{ sku: "nnn", qty: 5, price: 2.5 } ]
}
map-function
var mapFunction2 = function() {
for (var idx = 0; idx < this.items.length; idx++) {
var key = this.items[idx].sku;
var value = {
count: 1,
qty: this.items[idx].qty
};
emit(key, value);
}
};
reduce-function
var reduceFunction2 = function(keySKU, countObjVals) {
reducedVal = { count: 0, qty: 0 };
for (var idx = 0; idx < countObjVals.length; idx++) {
reducedVal.count += countObjVals[idx].count;
reducedVal.qty += countObjVals[idx].qty;
}
return reducedVal;
};
finalize-function
var finalizeFunction2 = function (key, reducedVal) {
reducedVal.avg = reducedVal.qty/reducedVal.count;
return reducedVal;
};
map-reduce
db.orders.mapReduce( mapFunction2,
reduceFunction2,
{
out: { merge: "map_reduce_example" },
query: { ord_date:
{ $gt: new Date('01/01/2012') }
},
finalize: finalizeFunction2
}
)
增量MapReduce
综述
1. 在当前collection执行任务,将结果输出到多个collection中
2. 当有更多的数据需要处理,用子任务处理
1. 使用query只处理增量的数据
2. 使用out将结果输出到已有的collection
例
prototype
db.sessions.save( { userid: "a", ts: ISODate('2011-11-03 14:17:00'), length: 95 } );
db.sessions.save( { userid: "b", ts: ISODate('2011-11-03 14:23:00'), length: 110 } );
db.sessions.save( { userid: "c", ts: ISODate('2011-11-03 15:02:00'), length: 120 } );
db.sessions.save( { userid: "d", ts: ISODate('2011-11-03 16:45:00'), length: 45 } );
db.sessions.save( { userid: "a", ts: ISODate('2011-11-04 11:05:00'), length: 105 } );
db.sessions.save( { userid: "b", ts: ISODate('2011-11-04 13:14:00'), length: 120 } );
db.sessions.save( { userid: "c", ts: ISODate('2011-11-04 17:00:00'), length: 130 } );
db.sessions.save( { userid: "d", ts: ISODate('2011-11-04 15:37:00'), length: 65 } );
mapFunction
var mapFunction = function() {
var key = this.userid;
var value = {
userid: this.userid,
total_time: this.length,
count: 1,
avg_time: 0
};
emit( key, value );
};
reduceFunction
var reduceFunction = function(key, values) {
var reducedObject = {
userid: key,
total_time: 0,
count:0,
avg_time:0
};
values.forEach( function(value) {
reducedObject.total_time += value.total_time;
reducedObject.count += value.count;
}
);
return reducedObject;
};
finalizeFunction
var finalizeFunction = function (key, reducedValue) {
if (reducedValue.count > 0)
reducedValue.avg_time = reducedValue.total_time / reducedValue.count;
return reducedValue;
};
第一次mapReduce
db.sessions.mapReduce( mapFunction,
reduceFunction,
{
out: "session_stat",
finalize: finalizeFunction
}
)
增量数据
db.sessions.save( { userid: "a", ts: ISODate('2011-11-05 14:17:00'), length: 100 } );
db.sessions.save( { userid: "b", ts: ISODate('2011-11-05 14:23:00'), length: 115 } );
db.sessions.save( { userid: "c", ts: ISODate('2011-11-05 15:02:00'), length: 125 } );
db.sessions.save( { userid: "d", ts: ISODate('2011-11-05 16:45:00'), length: 55 } );
增量mapReduce
db.sessions.mapReduce( mapFunction,
reduceFunction,
{
query: { ts: { $gt: ISODate('2011-11-05 00:00:00') } },
out: { reduce: "session_stat" },
finalize: finalizeFunction
}
);
在out处的reduce相当执行了reduceFunction和finalizeFunction
Map调试
prototype
{
_id: ObjectId("50a8240b927d5d8b5891743c"),
cust_id: "abc123",
ord_date: new Date("Oct 04, 2012"),
status: 'A',
price: 250,
items: [ { sku: "mmm", qty: 5, price: 2.5 },
{ sku: "nnn", qty: 5, price: 2.5 } ]
}
定义mapFunction
var map = function() {
emit(this.cust_id, this.price);
};
定义reduceFunction
var emit = function(key, value) {
print("emit");
print("key: " + key + " value: " + tojson(value));
}
调用map处理单文档并验证key/value
var myDoc = db.orders.findOne( { _id: ObjectId("50a8240b927d5d8b5891743c") } );
map.apply(myDoc);
emit
key: abc123 value:250
调用map处理多文档并验证key/value
var myCursor = db.orders.find( { cust_id: "abc123" } );
while (myCursor.hasNext()) {
var doc = myCursor.next();
print ("document _id= " + tojson(doc._id));
map.apply(doc);
print();
}
Reduce调试
reduce返回对象必须与map生成emit的对象相同
reduceFunction
var reduceFunction2 = function(keySKU, valuesCountObjects) {
reducedValue = { count: 0, qty: 0 };
for (var idx = 0; idx < valuesCountObjects.length; idx++) {
reducedValue.count += valuesCountObjects[idx].count;
reducedValue.qty += valuesCountObjects[idx].qty;
}
return reducedValue;
};
sampleArray
var myTestObjects = [
{ count: 1, qty: 5 },
{ count: 2, qty: 10 },
{ count: 3, qty: 15 }
];
调用reduce
reduceFunction2('myKey', myTestObjects);
valueArray中元素的顺序不影响reduce输出结果
sampleArray
var values1 = [
{ count: 1, qty: 5 },
{ count: 2, qty: 10 },
{ count: 3, qty: 15 }
];
var values2 = [
{ count: 3, qty: 15 },
{ count: 1, qty: 5 },
{ count: 2, qty: 10 }
];
reductFunction
var reduceFunction2 = function(keySKU, valuesCountObjects) {
reducedValue = { count: 0, qty: 0 };
for (var idx = 0; idx < valuesCountObjects.length; idx++) {
reducedValue.count += valuesCountObjects[idx].count;
reducedValue.qty += valuesCountObjects[idx].qty;
}
return reducedValue;
};
调用reduce
reduceFunction2('myKey', values1);
reduceFunction2('myKey', values2);
reduce是幂等的
reduceFunction
var reduceFunction2 = function(keySKU, valuesCountObjects) {
reducedValue = { count: 0, qty: 0 };
for (var idx = 0; idx < valuesCountObjects.length; idx++) {
reducedValue.count += valuesCountObjects[idx].count;
reducedValue.qty += valuesCountObjects[idx].qty;
}
return reducedValue;
};
sampleKey
var myKey = 'myKey';
sampleValue
var valuesIdempotent = [
{ count: 1, qty: 5 },
{ count: 2, qty: 10 },
reduceFunction2(myKey, [ { count:3, qty: 15 } ] )
];
sampleValue1
var values1 = [
{ count: 1, qty: 5 },
{ count: 2, qty: 10 },
{ count: 3, qty: 15 }
];
调用reduce
reduceFunction2(myKey, valuesIdempotent);
reduceFunction2(myKey, values1);
聚合参考
快速参考手册
stages
$project
显示或隐藏返回的字段
$match
$redact
$limit
$skip
$unwind
$group
$sample
$sort
$geoNear
$lookup
$out
$indexStats
表达式
布尔表达试
$and
$or
$not
Set表达试
比较表达式
$com
$eq
$gt
$gte
$lt
$lte
$ne
数学表达式
$abs
$add
$ceil
$divide
$exp
$floor
$ln
$log
$log10
$mod
$multiply
$pow
$sqrt
$substrack
$trunc
字符串表达式
$concat
$substr
$toLower
$toUpper
$strcasecmp
全文搜索表达式
数组表达式
变量表达式
$map
$let
日期表达式
$dayOfYear
$dayOfMonth
$dayOfWeek
$year
$month
$week
$hour
$minute
$second
$millisecond
$dateToString
条件表达式
$cond
$ifNull
Accumulator
$sum
$avg
$first
$last
$max
$min
$push
$addToSet
$stdDevPop
$stdDevSamp
聚合命令
命令
aggregate
count
distinct
group
mapReduce
方法
db.collection.aggregate()
db.collection.group()
db.collection.mapReduce()
命令对比
命令
aggregate
mapReduce
group
描述
为特定的目的设计,提供性能和可用性
使用一系列管道对结果进行过滤
处理大的数据集
分组功能
关键特色
相同的管道可以重复执行
管道不用为每一个输入产生一个输出
对于连续增长的数据,可以执行复杂的增量处理
可以对已经存在的字段进行分组或定制一个keyf javascript函数
灵活性
聚合管道支持有限的操作符和表达式
可以使用$project对字段进行重定位
使用map、reduce、finalize函数提供灵活的聚合逻辑
定制reduce、finalize函数提供灵活的聚合逻辑
结果集
可以返回多种形式的结果集或将结果写入到colleciton中
可以返回多种形式的结果集
返回分组后的结果集
分片
支持非分片和分片collection
支持非分片和分片collection
仅支付非分片的结果集
SQL vs Mongo
where <=> $match
GROUP BY <=> $group
HAVING <=> $match
SELECT <=> $project
ORDER BY <=> $sort
LIMIT <=> $limit
SUM() <=> $sum
COUNT() <=> $sum
join <=> $lookup
3.2 中新增
6. 全文搜索
综述
每个collection只能有一个text index,但该索引可以覆盖多个字段
全文索引
db.stores.createIndex( { name: "text", description: "text" } )
在name和description上创建索引
全文搜索操作符
查询框架
使用$text在全文索引上执行全文搜索
$text会使用空格或出现最多的标点符号待搜索字符进行分隔
对分隔后的词使用or进行查询
使用$meta操作符会对查询结果返回一个相关性分数
可以根据此分数进行排序
聚合框架
进行聚合操作时,使用$match和$text组合查询
进行排序时,在$sort中使用$meta操作符
在聚合操作中使用全文搜索
限制
在包含$text的$match必须在第一阶段
test操作符仅能出现一次
text操作符不能出现在$or或$not操作符中
默认text操作符不返回相关性评分,只有在使用$meta时才返回
指定语种
在$text中指定$language操作符
9. 索引
综述
mongo为每个collection的_id字段默认创建唯一索引
索引类型
单字段索引
复合索引
多键(数组)索引
地理位置索引
2d index
2dsphere index
全文索引
哈希索引
索引特性
唯一索引
partial索引
sparse索引
TTL索引
time to live
索引使用
覆盖索引
索引交叉
单字段索引
创建升序索引
db.records.createIndex( { score: 1 } )
1-升序
-1-降序
在单字段索引中排序并不重要,mongo可以自动的进行升/降序的查询
在嵌入式字段上创建索引
db.records.createIndex( { "location.state": 1 } )
state是一个字段而不是一个文档
在嵌入式文档上创建索引
db.records.createIndex( { location: 1 } )
location是一个文档,而不是一个字段
复合索引
mongo强制限制复合索引字段数为31个
创建复合索引
db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )
不能使用哈希索引字段创建复合索引
利用复合索引查询时,查询条件必须包含索引的第一个字段
在复合索引中,索引的排序会影响查询操作的排序操作是否被支持
多键(数组)索引
当索引中字段存在数组时,会将数据的每个元素当做键插入到索引中
数组中可以为标量值(字符、数字)、嵌入式文档
创建索引
db.coll.createIndex( { <field>: < 1 or -1 > } )
限制
在复合多键索引中,每个文档中最多只能有一个字段类型为数组
向复合索引中插入文档时,每个文件所包含索引中的字段中,只能有(任意)一个字段是数组
不能将一个多键索引做为分片key索引
2.6及之后版本中:
如果分片key索引为复合索引的第一个字段,该索引可以为复合多键索引(其他的字段不能是分片key的一部分)
哈希索引不能为多键索引
多键索引不支持覆盖查询
当多键索引对数组字段时,若查询字段的值为数组时,会对文档数组内数组进行完整的匹配,包括顺序
若查询字段的值为单个值,则所有包含该值的文档都会被查出
多键索引边界
多键索引的交叉边界
如果使用$eleMatch对同一字段进行两次范围,mongo会对查询条件中交叉的地方进行合并
多键索引的复合边界
在数组字段上的复合索引
嵌入文档数组字段上的复合索引
使用.(点)去指定要查询数组内文档的字段
非数组字段及数组中字段的复合索引
此时对于数组中查询,使用的为或操作,即只要数组中文档满足其中一个或全部条件即可
若要同时满足两个条件需要使用$eleMatch
数组字段在索引上的复合索引
在多键索引上对同一数组的字段进行查询,每个文档的数组必须符合全部查询条件
1. 除了字段名,其他的路径名必须全部相同
2. 必须使用$eleMatch操作符
不使用$eleMatch
在进行复合查询时,如果没有$eleMatch,会导致mongo不强制使用所有字段
mongo经常会约束索引的第一个字段
在不完全的路径中使用$eleMatch
如果对数组中文档的子文档的字段加索引,若在查询时使用$eleMatch同时满足所有条件
则必须在$eleMatch中指定最终字段,即使路径相同也不能放到$eleMatch中用于指定字段名
混合$eleMatch
对数组中不同的文档的子字段创建索引时,即使对各个最终路径使用了$eleMath操作符
mongo也只能对索引的第一个字段进行范围查询
全文搜索
综述
全文搜索可以对所有的字符串字段或字符串数组进行索引
创建索引
每个collection最多只能有一个全文索引
指定字段,并指定字段的值为text
db.reviews.createIndex(
{
subject: "text",
comments: "text"
}
)
通配符索引
可以使用$**代码对整个colltion下的所有字符串字段、字符串数组进行索引
db.collection.createIndex( { a: 1, "$**": "text" } )
使用通配符复合索引,则必须指定除通配符之外的字段为查询条件
如果文档中不存在索引中的字段,则不会生产对该文档的引用,在插入时也不会对该文档进行处理
限制
每个collection只能有一个全文索引
不能在全文搜索中指定hint()
不能对全文索引进行排序
复合索引
复合索引可以包含一些排序的字段各一个全文索引字段,则限制如下:
不能包含任何特殊类型:数组地址位置索引、多键索引
如果复合索引中存在key在全文索引之前,则在查询中必须包含对应key的查询条件
存储需求和性能消耗
全文索引会很大
在相同的数据量上,创建一个全文索引与创建一个多键索引时间相同,比创建排序索引要耗时的多
在一个很大的collection上创建全文索引,要确认已经把文件描述符读写个数限制的很大
全文索引会影响插入操作
全文索引不会存储短语或邻近的词组,当整个collection都在内存中时,查询会非常的高效
为全文索引指定语言
在创建索引时,使用default_language指定不同的语言,默认为英语
为多种语言创建全文索引
对文档设置语言
在文档或子文档中增加language字段指定语言
指定的语言会覆盖全文索引的默认语言
在嵌入式文档中指定语言会覆盖嵌入式文档的语言或索引的默认语言
对于嵌入式文档没有包含language字段
如果文档有language字段,则使用文档的语言
其他情况使用默认语言
在创建索引时使用language_override字段,该字段的值用于指定文档中的对应的字段的值为全文索引的语言
为全文索引创建名称
索引默认名称为<field1>_text_<array>.<field2>_text_<upset>.<field3>text
为全文索引指定索引名
db.collection.createIndex(
{
content: "text",
"users.comments": "text",
"users.profiles": "text"
},
{
name: "MyTextIndex"
}
)
为全文索引指定权重
db.blog.createIndex(
{
content: "text",
keywords: "text",
about: "text"
},
{
weights: {
content: 10,
keywords: 5
},
name: "TextIndex"
}
)
未指定权重,则默认值为1
限制描述记录数
创建复合索引时,指定非全文key在前,全文key在后,以限制查询时的条目扫描
db.inventory.createIndex(
{
dept: 1,
description: "text"
}
)
2dsphere索引
综述
支持范围查询、交集查询、附近查询
2dsphere索引支持数据存储为GeoJSON格式的坐标对
2dsphere索引默认不处理字段为空的文档,同时忽略sparse:true选项
在创建复合索引时,如果2dsphere索引key在其他key之后,2dsphere决定该文档是否会被索引
geoNear命令或$geoNear操作符限制
要求collection只能有一个2dspher或2d索引
不能将2dsphere作为分片key
但可以在分片collection上创建索引时使用其他字段做为分片key
2dsphere索引支持的字段只能是坐标对或geoJSON格式的数据,其他的数据类型会报错
创建2dsphere索引
db.collection.createIndex( { <location field> : "2dsphere" } )
2dsphere索引不要求第一个字段必须是2dsphere类型
地理位置查询
2dsphere索引支持spherical查询,2d索引支持spherical和flat查询
使用2dsphere索引查询比2d索引的性能和精确度更高
GeoJSON对象
{ type: "<GeoJSON type>" , coordinates: <coordinates> }
type-GeoJSON类型
Point
{ type: "Point", coordinates: [ 40, 5 ] }
coordinates必须是单个位置坐标
LineString
{ type: "LineString", coordinates: [ [ 40, 5 ], [ 41, 6 ] ] }
coordinates必须是数组,必须是2个或多个位置坐标
Polygon
coordinates必须是一个LineRing的数组,Line是一个封闭的LineString,
由四个或多个位置组成的数组,其中第一个和最后一个位置必须相同
第一个必须是外部的环,其他必须是内部的环
MultiPoint
{
type: "MultiPoint",
coordinates: [
[ -73.9580, 40.8003 ],
[ -73.9498, 40.7968 ],
[ -73.9737, 40.7648 ],
[ -73.9814, 40.7681 ]
]
}
coordinates必须是由坐标厅组成的数组
MultiLineString
{
type: "MultiLineString",
coordinates: [
[ [ -73.96943, 40.78519 ], [ -73.96082, 40.78095 ] ],
[ [ -73.96415, 40.79229 ], [ -73.95544, 40.78854 ] ],
[ [ -73.97162, 40.78205 ], [ -73.96374, 40.77715 ] ],
[ [ -73.97880, 40.77247 ], [ -73.97036, 40.76811 ] ]
]
}
coordinates必须是由LineString坐标组成的数组
MultiPolygon
{
type: "MultiPolygon",
coordinates: [
[ [ [ -73.958, 40.8003 ], [ -73.9498, 40.7968 ], [ -73.9737, 40.7648 ], [ -73.9814, 40.7681 ], [ -73.958, 40.8003 ] ] ],
[ [ [ -73.958, 40.8003 ], [ -73.9498, 40.7968 ], [ -73.9737, 40.7648 ], [ -73.958, 40.8003 ] ] ]
]
}
coordinates必须是由Polygon组成的数组
GeometryCollection
{
type: "GeometryCollection",
geometries: [
{
type: "MultiPoint",
coordinates: [
[ -73.9580, 40.8003 ],
[ -73.9498, 40.7968 ],
[ -73.9737, 40.7648 ],
[ -73.9814, 40.7681 ]
]
},
{
type: "MultiLineString",
coordinates: [
[ [ -73.96943, 40.78519 ], [ -73.96082, 40.78095 ] ],
[ [ -73.96415, 40.79229 ], [ -73.95544, 40.78854 ] ],
[ [ -73.97162, 40.78205 ], [ -73.96374, 40.77715 ] ],
[ [ -73.97880, 40.77247 ], [ -73.97036, 40.76811 ] ]
]
}
]
}
coordinates-坐标
longitude 经度
latitude 纬度
2d索引
哈希索引
哈希索引分解嵌入式文档计算哈希值,但哈希索引并不支持多键索引
mongo使用哈希后的分片key支持哈希索引
创建索引
db.collection.createIndex( { _id: "hashed" } )
不能在复合索引中增加哈希字段,为哈希索引指定唯一属性
索引特色
TTL索引
TTL索引是一个特殊的单字段索引,在对应字段到达失效时间后,mongo会删除掉对应colletion的文档
子主题
db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )
在date类型的字段或存在date类型字段的数据上创建TTL索引,expireAfterSeconds字段用于指定失效间隔
数据失效
TTL索引在对应的字段在指定时间到达后将对应的文档指定为失效
若字段为数组,则选择其中最小时间的字段
如果对应字段不是日期,则文档永远不会失效
如果文档不包含对应字段,则文档永远不会失效
数据删除
mongo有一个后台进程会读取对应字段的值,在对应文档超时后进行删除
唯一索引
创建索引
db.collection.createIndex( <key and index type specification>, { unique: true } )
行为
限制
对于已经存在重复数据的collection,无法创建唯一索引
唯一索引并不限制在数组字段中存在多个相同key:value的情况
如果文档中并不存在对应的字段,唯一索引会存储null值,同时仅允许存在一个这样的文档
局部索引
局部索引只对于符合指定过滤表达式的字段进行索引
创建索引
db.collection.createIndex(keys, options)中options字段中使用partialFilterExpression指定表达式
允许的过滤条件
等于表达式
$exist
$gt、$lt等
$type
$and
例
db.restaurants.createIndex(
{ cuisine: 1, name: 1 },
{ partialFilterExpression: { rating: { $gt: 5 } } }
)
行为
查询语句的查询条件必须覆盖partialFilterExpression字段指定值的范围内
局部索引可以使用$exist:true完成稀疏索引的功能
限制
不能创建除了表达式其他字段都相同的索引
_id字段不能为局部索引
分片key不能为局部索引
当局部索引和唯一索引同时使用时,唯一性只对符合表达式的文档有效
稀疏索引
稀疏索引只包含存在该字段的文档,即使字段的值是null
创建索引
db.collection.createIndex(keys, options)中options字段中使用sparse并标识为true
db.addresses.createIndex( { "xmpp_id": 1 }, { sparse: true } )
行为
如果对于查询或排序操作,稀疏索引返回的是不完整的结果集,则除非使用hint(),否则不会使用稀疏索引
2dsphere (version 2), 2d, geoHaystack, text索引总是稀疏的
复合稀疏索引只对至少包含一个字段的文件进行索引
如果复合稀疏索引中存在全文索引,则只对存在全文索引字段的文档进行索引
唯一稀疏索引不允许存在重复的字段,但允许不存在对应字段
创建索引
综述
默认情况下,创建索引会阻塞所有需要数据库全局读写锁的操作,
数据库全将该collection设置为读写不可用,直到索引创建完成
子主题
db.collection.createIndex(keys, options)在option中设置background为true
行为
可以同时在后台创建多个索引
即使后台创建索引,创建索引的连接也会被阻塞直接索引被创建完成
在创建过程中,查询不会使用索引,只有创建完成后才可以被使用
性能
后台创建索引使用增量方法创建,比前台创建慢
如果索引空间比内存还大,则创建过程比前台创建慢的多
中断索引创建
在mongod重启时,会停止索引的创建,mongod会将不完整的索引删除。
使用storage.indexBuildRetry设置,mongod是在重启时是否会重建索引,默认情况下在mongo在重启时会重建索引
在复本上创建索引
在2.6版本后,可以在复本上在后台创建索引
在子复本集中的索引创建,会在主复本创建完成后执行,如果主复本在后台创建索引,子复本也会两样如此
在一个子复本上创建一个大的索引时,比较合适的办法是将重启复本,并将复本设置为独立状态下创建索引
查看创建操作
db.currentOp()
中断创建操作
db.killOp()
在复本集上创建索引
综述
对于复本集,mongo会先创建主复本的索引,再创建子复本的索引
在分片集群中会发送createIndex()到各个主复本集中,再复制到子复本中
思考
确认oplog足够大,以执行创建索引或重建索引
该程序影响复本集中的一个,而不是同时对所有复本集产生影响
在2.6版本之前,子复本集的索引创建会由后台创建转变为前台创建
在子复本集上创建索引
停止一个子复本
对该子复本创建索引
重启该子复本
在主复本集上创建索引
直接在主复本集上创建索引
将当前主复本集转换为子复本集,再在该复本集上创建索引
后台创建索引会影响主复本集的写性能
交叉索引
在2.6版本中增加
当查询条件符合两个单字段索引时,mongo会同时使用两个索引进行数据查询
当查询条件符合两个索引的首字段时,mongo会同时使用两个索引进行数据查询
当查询条件符合两个索引的首字段时,若排序方式为子字段,则可以使用交叉索引查询
管理索引
查看已存在索引
db.collection.getIndexes()
查看整个数据库的索引
db.getCollectionNames().forEach(function(collection) {
indexes = db[collection].getIndexes();
print("Indexes for " + collection + ":");
printjson(indexes);
});
删除索引
db.collection.dropIndex()
删除所有索引
db.collection.dropIndexes()
修改索引
要修改索引,只能先删除再重建索引(除TTL索引)
重建所有索引,包括_id索引
db.collection.reIndex()
测量索引使用
获取索引使用统计数据
$indexStats
使用explain()获取查询计划
使用hint()控制索引
在find()之后使用hint()可以使mongo使用指定的索引进行查询
在hint()中使用$natural阻止mongo使用任何索引
索引使用策略
创建索引支持查询
如果所有的查询只使用同一个字段,创建单个字段索引
如果查询在只在一个字段上,或会叠加另一个字段,则创建复合索引
使用索引对查询结果排序
如果没有使用索引排序,当排序操作使用到32M内存时,将会中止
如果一个索引是单字段的,则正反排序都是可用的
如果是复合索引,则排序方式必须符合索引字段的顺序以及排序的顺序或反序
使用复合索引,若排序条件中存在索引中的前n个字段,则可以使用索引排序
使用复合索引,若查询条件中存在索引的前n个字段,排序使用其他字段,则可以使用索引排序
确认索引尺寸适合内存
查询索引大小
db.collection.totalIndexSize()
确认索引的大小和工作集占用内存的大小与实际内存大小是否符合
如果索引随着数据的插入而增加,且仅查询最近插入的数据,则mongo仅需要在内存中保持部分数据
创建高选择性的查询
选择性是一种使用索引去收缩查询结果范围的能力
指定索引的字段查询,收缩查询返回文档的数量
0 条评论
下一页