MongoDB基本命令与使用
创建用户
db.createUser 创建用户的命令
user:用户名
pwd:密码
role:角色
db:所属数据库
db.createUser({
user:"latteitcjz",pwd:"latteit",roles:[{
role:"root",db:"admin"}]});
查看用户: show users 或者 db.system.users.find();
修改用户密码: db.changeUserPassword(“用户名”, “新密码”);
附:Mongo支持的角色
read | 允许用户读取指定数据库 |
---|---|
readWrite | 允许用户读写指定数据库 |
dbAdmin | 允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile |
userAdmin | 允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户 |
clusterAdmin | 只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限 |
readAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读权限 |
readWriteAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读写权限 |
userAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的userAdmin权限 |
dbAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限 |
root | 只在admin数据库中可用。超级账号,超级权限 |
使用命令测试
直接在终端输入db.auth(“用户名”,“密码”);
创建数据库
use 命令表示切换到数据库,如果数据库不存在会创建数据库。
注意:数据库创建成功后,是不显示数据库的,必须新增一个集合数据才能显示。
# use 数据库名
一、MongoDB的数据库操作
显示数据库
通过show dbs或show databases查看数据库。
注意:
- 不同角色看见的数据库是不一样,管理员看见所有数据,普通用户只能看见自己的数据库。
- 新创建数据库里面没有数据时通过show dbs或show databases是无法查看到的。需要向数据库新增一个document(语法:db.jqk.insert({“key”:“value”,“key2”:“value2”});,其中jqk表示集合名)才能看见.
删除数据库
删除数据库需要切换到需要删除的数据库中,且登录用户具有dbAdminAnyDatabase权限,执行db.dropDatabase()。
# use latte
# db.dropDatabase()
二、MongoDB的集合操作
创建集合
MongoDB使用db.createCollection()函数来创建集合。
语法格式:db.createCollection(name, options)。
name: 要创建的集合名称。
options: 可选参数, 指定有关内存大小及索引的选项。
options 可以是如下参数。
创建默认选项集合
注意:集合是存在数据库中的,use 哪个数据库,创建的集合就在哪个数据库中。
# use latte
# db.createCollection("c1")
注意:Mongo中集合名称是区分大小写的。
创建包含选项的集合
所有选项写在createCollection第二个参数{}中
# db.createCollection("c2",{"max":2,capped:true,size:100000})
执行三次,每次的key分别叫做:key1、key2、key3,会发现key3替换了key1
# db.c2.insert({key3:"value"})
查看集合
进入到集合所在数据库
# use latte
使用show tables或show collections查看
# show collections
查看集合详情信息
# db.集合名称.stats()
删除集合
进入到集合所在数据库
# use latte
删除指定集合
# db.集合名.drop()
三、MongoDB的文档操作
在MongoDB中文档是指多个键及其关联的值有序地放置在一起就是文档,其实指的就是数据,也是我们平时操作最多的部分。
MongoDB中的文档的数据结构和 JSON 基本一样。所有存储在集合中的数据都是 BSON 格式。
BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。
插入文档
插入单个文档
注意:
- 新增时不需要考虑field,如果field已经存在则向指定field中新增。如果field不存在,则在collection中新添加一个filed
- 向collection中新增数据时,如果collection不存在,则自动创建collection
向dev集合中插入单个文档。
可以使用insert/insertOne/save执行新增,语法完全相同:
db.c1.insert({
name:"张三"});
db.c1.save({
name:"张三"});
db.c1.insertOne({
name:"张三"});
区别:
当明确给定主键时,如果主键值已经存在save表示修改(全量替换),insert/insertOne会报主键重复。
命令中_id是ObjectId类型,需要通过ObjectId函数把字符串转换为ObjectId
db.c1.save({_id:ObjectId(“5e81b3ac4d4d000026004f6a”),name:“jqk”})
插入多个文档
可以使用insert/insertMany/save执行新增,区别于单条新增把新增函数参数由对象类型({})变成数组类型([{}])下面是三种写法等效:
db.c1.insert([{
name:"a"},{
name:"b"}]);
db.c1.insertMany([{
name:"a"},{
name:"b"}]);
db.c1.save([{
name:"a"},{
name:"b"}]);
更新文档
MongoDB通过update函数或者save函数来更新集合中的文档。
update函数
update() 函数用于更新已存在的文档。
语法格式:db.COLLECTION_NAME.update({查询条件},{更新内容},{更新参数(可选)})
其中更新内容为整个文档更新内容,如果更新内容中只有一个属性,除了_id以外其他属性将会被设置null。
先新增一条测试数据
db.c1.insert({
name:"张三",age:12,address:"地址"});
修改,name改成了李四,age和address都被设置null了。(mongodb中如果一个集合中某个属性所有的文档对象都为空时会删除这个属性)
db.c1.update({
name:"张三"},{
name:"李四"});
使用save函数更新,只能更新单条数据。批量更新会抛出异常。批量操作只能新增数据。
db.c1.save({
"_id":ObjectId("xxxxx"), "name":"新名字"});
更新操作符
db.集合名.update({条件},{更新逻辑},{“multi”:true|false,“upsert”:true|false});
multi: 是否批量更新多行数据。默认false
upsert: 更新逻辑匹配失败后,是否新增数据。默认false
db.集合名.update({条件},{更新逻辑}, true|false, true|false);
$set操作符
$set操作符:用来指定一个键并更新键值,若键不存在并创建。只能修改第一个document
语法格式:db.COLLECTION_NAME.update({查询条件},{更新操作符:{更新内容}})
$set的作用总结:
- 只修改特定的Field,解决update默认修改整个document情况
db.c1.update({
name:"张三"},{
$set:{
name:"王五"}});
- 默认只修改符合条件的第一个document,如果需要全部修改,添加更新参数multi:true
db.c1.update({
name:"张三"},{
$set:{
age:18}},{
multi:true});
- 如果Field不存在,可以新建一个Field
db.c1.update({
name:"张三"},{
$set:{
sex:"男"}})
- 如果没有那么是张三的数据,则新增一条数据,age是18。
db.c1.update({
name:"张三"},{
$set:{
age:18}},{
upsert:true, multi:true});
或
db.c1.update({
name:"张三"},{
$set:{
age:18}},true, true);
不推荐
$inc操作符
$inc操作符:可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作。如果给定正数表示新增,如果给定负数表示减少。
把王五的年龄减少5岁。
db.c1.update({
name:"王五"},{
$inc:{
age:-5}});
$unset操作符
$unset
操作符:主要是用来删除键。让键的值为空。在编写命令时$unset
里field取值任意,无论给定什么值都表示删除。
删除名称为王五的地址。
db.c1.update({
name:"王五"},{
$unset:{
address:"随意"}});
$push操作符
$push操作符:向文档的某个数组类型的键添加一个数组元素,不过滤重复的数据。添加时键存在,要求键值类型必须是数组;键不存在,则创建数组类型的键。
向集合c1中所有文档对象添加了数组类型属性hobby,并添加一个值写代码。如果再次执行,表示向hobby中再添加一个值写代码。默认只修改符合条件的第一条,如果需要全部修改,添加multi属性。
db.c1.update({
},{
$push:{
hobby:"写代码"}},{
multi:true});
$pop操作符
$pop操作符:删除数据元素。可取值只能是1或-1。1表示尾部删除,-1表示头部删除
删除hobby中第一个元素。其中$pop中key是要操作的数组类型属性。
db.c1.update({
name:"李四"},{
$pop:{
hobby:-1}})
$pull操作符
$pull操作符:从数组中删除满足条件的元素,只要满足条件都删除。
删除hobby中元素内容为看尚学堂视频,如果存在多个都删除。
db.c1.update({
name:"王五"},{
$pull:{
hobby:"java"}});
$pullAll操作符
$pullAll操作符:可以设置多个条件。
删除王五中hobby为写代码和看尚学堂视频的值。其中属性(hobby)取值一定要是数组类型。
db.c1.update({
name:"王五"},{
$pullAll:{
hobby:["写代码","java"]}});
$rename
$rename操作符:对键进行重新命名。任何类型的键都能重命名。
修改王五的name属性为username。
db.c1.update({
name:"王五"},{
$rename:{
name:"username"}});
删除文档
remove()函数
使用remove()函数可删除集合中的指定文档。
删除参数可取值:
省略:删除满足条件的全部文档
0:删除满足条件的全部文档
其他数字:删除满足条件的第一条。
语法格式:remove({指定删除条件},删除参数(可选参数))
删除age为空的第一条数据。
db.c1.remove({
age:{
$exists:false}},1);
删除所有age为空的数据。
db.c1.remove({
age:{
$exists:false}});
deleteOne()函数
deleteOne()函数是官方推荐删除文档的方法。该方法只删除满足条件的第一条文档。
删除age为18的第一个文档对象
db.c1.deleteOne({
age:18});
deleteMany()函数
deleteMany函数是官方推荐的删除方法。该方法删除满足条件的所有数据。
删除所有age为18的文档对象。
db.c1.deleteMany({
age:18});
删除集合中的所有文档
当remove和deleteMany条件设置为空时表示删除全部。
remove({
})
deleteMany({
})
等值条件查询和投影查询
find()函数
在MongoDB中可以使用find()函数查询文档。
语法格式为:find({查询条件(可选)},{指定投影的键(可选)})
如果未给定参数则表示查询所有数据。
db.c1.find();
查询所有name为张三的文档对象
db.c1.find({
name:"张三"})
投影操作
投影查询指的就是哪些列被显示或不被显示。写到投影(projection)里面的属性可取值为1(显示)或0(不显示)。除了_id以外其他属性取值必须是相同。
sex和hobby不显示,其他属性都显示
db.c1.find({
name:"张三"},{
sex:0,hobby:0})
显示sex和hobby,默认_id也是显示
db.c1.find({
name:"张三"},{
sex:1,hobby:1})
只显示sex和hobby。只有这种情况才能出现属性取值不一样
db.c1.find({
name:"张三"},{
_id:0,sex:1,hobby:1})
findOne()函数
findOne()函数只返回满足条件的第一条数据。
返回第一行document对象。
db.c1.findOne();
返回名字为张三的第一条数据
db.c1.findOne({
name:"张三"})
正则查询
MongoDB中查询条件也可以使用正则表达式作为匹配约束。正则表达式语法与JavaScript正则表达式语句完全相同。正则内容需要写在 / / 之前。
字段的类型适用于字符串(String)类型。
语法格式:db.COLLECTION_NAME.find({字段名:正则表达式});、
可以实现模糊查询
查询name中存在三的文档对象
db.c1.find({
name:/三/});
或
db.COLLECTION_NAME.find({字段名:{
r e g e x : 正 则 表 达 式 , regex:正则表达式, regex:正则表达式,options:正则选项}});
第二种方式比第一种方式多了正则选项,功能更多。
正则表达式格式:/xxx/
正则选项:
i - 不区分大小写以匹配大小写的情况。
m - 多行查找,如果内容里面不存在换行符号(例如 \n)或者条件上没有(start/end),该选项没有任何效果
x - 设置x选项后,正则表达式中的非转义的空白字符将被忽略。需要 r e g e x 与 regex与 regex与options语法
s - 允许点字符(即.)匹配包括换行符在内的所有字符。需要 r e g e x 与 regex与 regex与options语法
i,m,x,s可以组合使用。
示例:
录入数据
db.c1.insert({
name:"abc"});
db.c1.insert({
name:"bcd"});
db.c1.insert({
name:"ABC"});
db.c1.insert({
name:"BCD"});
只能查询包含小写b的文档对象
db.c1.find({
name:/b/})
查询时不区分大小写,只要包含b或B都能查询出来
db.c1.find({
name:{
$regex:/b/,$options:"i"}});
条件操作符
条件操作符用于比较两个表达式并从mongoDB集合中获取数据。
语法格式:find({键:{操作符:条件}})或者findOne({键:{操作符:条件}})
$gt
(>) 大于操作符,greater than 缩写。
查询所有年龄大于10的文档对象
db.c1.find({
age:{
$gt:10}});
$lt
(<) 小于操作符。less than缩写,用left记忆,左角括号
查询所有年龄小于10的文档对象
db.c1.find({
age:{
$lt:10}});
$eq
(==)等于操作符,equals
查询年龄等于8
db.c1.find({
age:{
$eq:8}});
等效于
db.c1.find({
age:8});
$ne
(!=)不等操作符 not equals
查询所有年龄不是18岁的,包含了age属性没有值的文档对象。
db.c1.find({
age:{
$ne:8}});
$gte
(>=)大于或等于操作符greater than equals
查询所有年龄大于等于8的文档对象。如果文档没有age属性无法被查询。
db.c1.find({
age:{
$gte:8}});
$lte
(<=)小于或等于操作符less than equals
查询小于等于8的文档对象。
db.c1.find({
age:{
$lte:8}});
$in
我们可以使用 i n 操 作 符 来 表 示 多 条 件 查 询 , in操作符来表示多条件查询, in操作符来表示多条件查询,in中多条件的关系为或者关系,只要满足其中一个就能被查询出来,由于$in取值为多个,所以是数组类型。
查询年龄为8或10或12的文档对象
db.c1.find({
age:{
$in:[8,10,12]}})
$nin
not in,与$in的结果取反。
只要age不是8或10或12的文档对象都能查询出来。
db.c1.find({
age:{
$nin:[8,10,12]}})
逻辑操作符
$and
我们可以使用 a n d 操 作 符 来 表 示 多 条 件 间 的 并 且 关 系 。 由 于 and操作符来表示多条件间的并且关系。由于 and操作符来表示多条件间的并且关系。由于and有多个条件,所以类型是数组类型[],每个元素都是一个条件{}
语法格式为:find({$and:[{条件一},{,条件二},…]})
查询所有年龄为18,并且名字为张三的文档对象
db.c1.find({
$and:[{
age:18},{
name:"张三"}]})
$or
我们可以使用KaTeX parse error: Expected '}', got 'EOF' at end of input: …系。 语法格式为:find({ or:[{条件一},{条件二},…]})
查询所有年龄为18或8或姓名为张三的文档对象
db.c1.find({
$or:[{
age:18},{
name:"张三"},{
age:8}]})
a n d 与 and与 and与or联合使用
一定要从逻辑上分清楚总体是and还是or。
设置完成类似SQL:通过or分成两部分,每部分里面又是and条件
select * from c1 where name='张三' and age=18 or username='王五' and age=18
MongoDB命令:
db.c1.find({
$or:[{
$and:[{
name:"张三"},{
age:18}]},{
$and:[{
username:"王五"},{
age:8}]}]});
类型操作符
$type 操作符
$type根据类型查询。可取值和JavaScript类型取值相同。
$type的取值为类型别名,类型别名就是类型的全小写名称。
number 数值
string 字符串,注意s小写
object 对象
array 数组类型
取出所有age里面是数值类型的文档对象
db.c1.find({
age:{
$type:"number"}});
$exists
可以借助$exists判断为空值的文档对象。
db.c1.find({
age:{
$exists:false}});
分页
limit(数字)查询结果中前几个
skip(数字)跳过结果中前几个
跳过签个文档对象
db.c1.find().skip(2)
只要结果中前两行
db.c1.find().limit(2)
表示查询前两个文档对象,可用用在分页上,表示第一页数据。
db.c1.find().skip(0).limit(2)
MongoDB 排序
在 MongoDB 中使用 sort() 函数对查询到的文档进行排序,sort() 函数可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
语法格式:db.COLLECTION_NAME.find().sort({排序键:1})
查询结果按照age升序排序。
db.c1.find().sort({
age:1});
也可以对字符串进行排序,排序时按照编码顺序进行排序。支持中文等。虽然支持,但是平时还是多按照数值类型的属性进行排序。
db.c1.find().sort({
name:1});
MongoDB 聚合查询
在MongoDB中我们可以通过aggregate()函数来完成一些聚合查询,aggregate()函数主要用于处理诸如统计,平均值,求和等,并返回计算后的数据结果。
语法格式:
db.COLLECTION_NAME.aggregate([{
KaTeX parse error: Expected '}', got 'EOF' at end of input: group:{_id:"分组键名",“KaTeX parse error: Expected '}', got 'EOF' at end of input: …....,别名:{聚合运算:"运算列”}}},{条件筛选:{键名:{运算条件:运算值}}}])
常见的mongo的聚合操作和mysql的查询做类比
求和 - $sum
查询dev集合中一共有多少个文档。
相当于sql语句:
SELECT count(*) AS count FROM c1
db.c1.aggregate([{
$group:{
_id:null,count:{
$sum:1}}}])
`$group`:分组,代表聚合的分组条件
_id:分组的字段,是必须有的。相当于SQL分组语法group by column_name中的column_name部分。如果根据某字段的值分组,则定义为_id:'$字段名'。如果没有分组属性取值为null
count:返回结果字段名。可以自定义,类似SQL中的字段别名。
$sum:如果取值为1表示总条数,相当于每个document中都多个count属性,count取值为1,累加和就是总条数。如果$sum取值是‘$field名’(要注意有单引号或双引号),表示对该field求累加和
如果只有$group时 agregate()里面[]可以省略,因为就一个操作符。
取总和
$sum取值必须是字符串类型,有双引号。age是属性名,前面必须有$
db.c1.aggregate([{
$group:{
_id:null,totalAge:{
$sum:"$age"}}}]);
分组后取总和
_id取值也是字符串类型,里面必须是属性名前面加上$
db.c1.aggregate([{
$group:{
_id:"$name",totalAge:{
$sum:"$age"}}}]);
条件筛选 - $match
$match和$group同级操作符,需要写在两个条件中的。
写在$group前面
$match写在$group前面表示先过滤条件,在分组。
先查询出age大于等于5的文档对象,在按照name属性进行分组,计算age列总和
db.c1.aggregate([{
$match:{
age:{
$gte:5}}},{
$group:{
_id:"$name",totalAge:{
$sum:"$age"}}}]);
写在$group后面
$match写在$group后面表示分组后在设置条件。
分组后结果集只有_id和totalAge两个属性,所以在$match中只能对这个属性进行操作。
db.c1.aggregate([{
$group:{
_id:"$name",totalAge:{
$sum:"$age"}}},{
$match:{
totalAge:{
$gte:5}}}]);
最大值 - $max
$max可以计算最大值。
db.c1.aggregate([{
$group:{
_id:"$name",maxAge:{
$max:"$age"}}}]);
最小值 - $min
$min可以计算最小值
db.c1.aggregate([{
$group:{
_id:"$name",minAge:{
$min:"$age"}}}]);
平均值 - $avg
计算出不同姓名的平均年龄
db.c1.aggregate([{
$group:{
_id:"$name",avgAge:{
$avg:"$age"}}}]);
统计结果返回数组 - $push
分组后按照分组数组进行合并,如果希望看到某个列合并之前的所有数据可以使用$push,把分组后同一组的所有值放到一个数组中
按照name进行分组,分组后把age的数据都放入到名称为allAge的数组中
db.c1.aggregate([{
$group:{
_id:"$name",allAge:{
$push:"$age"}}}])
数组字段拆分 - $unwind
$unwind会把数组列进行拆分,原来的document会根据数组属性值个数分为多个document。如果这个数组属性为空,对应document将不被显示(因为document没有数组属性)
正常数据:只有name为abc的hobby有数组类型值,且长度为3
db.c1.aggregate([{
$unwind:"$hobby"}]);
MongoDB 索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对系统的性能是非常致命的。索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
创建索引
在MongoDB中会自动为文档中的_Id(文档的主键)键创建索引,与关系型数据的主键索引类似。
我们可以使用createIndex()函数来为其他的键创建索引。在创建索引时需要指定排序规则。1按照升序规则创建索引,-1按照降序规则创建索引。
在创建索引时,需要使用具有dbAdmin或者dbAdminAnyDatabase角色的用户。
语法格式:db.COLLECTION_NAME.createIndex({创建索引的键:排序规则,…},{创建索引的参数(可选参数)})
ensureIndex();
参数说明
给c1集合中name属性创建索引,指定名称为suiyi,后台创建
db.c1.createIndex({
name:1},{
background:true,name:"suiyi"});
查看索引
查看集合索引
我们可以通过getIndexes()或者getIndexSpecs()函数查看集合中的所有索引信息。
语法格式:db.COLLECTION_NAME.getIndexse()
其中getIndexes()可以在Navicat中执行,getIndexSpecs()只能在MongoDB客户端工具中执行。
查看索引键
我们可以通过使用getIndexKeys()函数查看集合的索引键。
注意:此方法在Navicat Pernium 15中无法执行,但是可以mongodb命令行中执行
语法格式:db.COLLECTION_NAME.getIndexKeys();
查看dev集合中的索引键
查看索引大小
我们可以通过totalIndexSize()函数来查看当前集合中索引的大小,单位为字节。
语法格式:db.COLLECTION_NAME.totalIndexSize(detail)
参数解释:detail可选参数,传入除0或false外的任意数据,会显示该集合中每个索引的大小及集合中索引的总大小。如果传入0或false则只显示该集合中所有索引的总大小。默认值为false。注意:在navicat中无法显示全部索引内容,只能显示总索引大小
修改索引
MongoDB没有单独的修改索引函数,如果要修改某个索引,需要先删除旧的索引,再创建新的索引。
删除索引
删除集合中的指定索引
我们可以通过dropIndex()函数来删除指定索引。
语法格式:db.COLLECTION_NAME.dropIndex(“索引名称”)。
删除集合中的全部索引
我们可以使用dropIndexes()函数删除集合中的全部索引,_id键的索引除外。
语法格式:db.COLLECTION_NAME.dropIndexes()
删除名称为suiyi的索引。删除之前建议使用getIndexes()查看索引信息。
db.c1.dropIndex(“latte”);
重建索引
我可以使用reIndex()函数重建索引。重建索引可以减少索引存储空间,减少索引碎片,优化索引查询效率。一般在数据大量变化后,会使用重建索引来提升索引性能。重建索引是删除原索引重新创建的过程,不建议反复使用。
语法格式:db.COLLECTION_NAME.reIndex()
MongoDB中的索引类型
在MongoDB中支持多种类型的索引,包括单字段索引、复合索引、多key索引、文本索引等,每种类型的索引有不同的使用场合。
单字段索引(Single Field Index)
所谓单字段索引是指在索引中只包含了一个键。查询时,可加速对该字段的各种查询请求,是最常见的索引形式。MongoDB默认创建的_Id索引也是这种类型。我们可以使用createIndexes({索引键:排序规则})函数来创建单字段索引。
语法格式:db.COLLECTION_NAME.createIndexes({索引键名:排序规则})
只要包含只有一个属性就叫单字段字段索引。查询时只按照这个属性作为条件进行查询。
交叉索引
所谓交叉索引就是为一个集合的多个字段分别建立索引,在查询的时候通过多个字段作为查询条件,这种情况称为交叉索引。
在查询文档时,在查询条件中包含一个交叉索引键或者在一次查询中使用多个交叉索引键作为查询条件都会触发交叉索引。
给集合中多个属性创建索引,查询时这些属性中全部或一部分作为条件。
举例说明:
创建两个独立索引
db.c1.createIndex({
name:1},{
background:true});
db.c1.createIndex({
age:1},{
background:true});
查询条件中的属性都已经创建索引,称交叉索引。
db.c1.find({
$and:[{
name:"abc"},{
age:1}]});
复合索引(Compound Index)
复合索引是Single Field Index的升级版本,它针对多个字段联合创建索引,先按第一个字段排序,第一个字段相同的文档按第二个字段排序,依次类推。
语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则, 索引键名:排序规则,…});
复合索引能满足的查询场景比单字段索引更丰富,不光能满足多个字段组合起来的查询,也能满足所有能匹配符合索引前缀的查询。
创建复合索引后,按照name或按照name+age两种查询方式都可以触发这个索引。
db.c1.createIndex({
name:1,age:1},{
background:true});
查询条件顺序: 尽可能安照复合索引的字段顺序来编写查询条件顺序(如:索引-createIndex({name:1,age:1},{background:true}),查询-find({“name”:“xxx”, “age”:“yyy”}))。如果只有交叉索引可用,则按照数据的识别度排列查询条件顺序(如:同名的人数少于同年龄的人数,名字识别度高于年龄,查询条件是-{“name”:“xxx”, “age”:“yyy”}; 同年龄的人数少于同性别的人数,年龄的识别度高于性别,查询条件是-{“age”:“xxx”,“gender”:“yyy”})。
多key索引 (Multikey Index)
当索引的字段为数组时,创建出的索引称为多key索引,多key索引会为数组的每个元素建立一条索引。
语法格式:db.COLLECTION_NAME.createIndex({数组键名:排序规则});
只要是属性是数组类型,称为多key索引
db.c1.createIndex({
hobby:1},{
background:true});
索引额外属性
MongoDB除了支持多种不同类型的索引,还能对索引定制一些特殊的属性。
唯一索引 (unique index)
唯一索引会保证索引对应的键不会出现相同的值,比如_id索引就是唯一索引
语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则},{unique:true})
如果唯一索引所在字段有重复数据写入时,抛出异常。
创建索引时也需要保证属性中内容是不重复的
db.c1.createIndex({
age:1},{
background:true,unique:true});
部分索引 (partial index):
部分索引是只针对符合某个特定条件的文档建立索引,3.2版本才支持该特性。
MongoDB部分索引只为那些在一个集合中,满足指定的筛选条件的文档创建索引。由于部分索引是一个集合文档的一个子集,因此部分索引具有较低的存储需求,并降低了索引创建和维护的性能成本。部分索引通过指定过滤条件来创建,可以为MongoDB支持的所有索引类型使用部分索引。
简单点说:部分索引就是带有过滤条件的索引
语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则},{partialFilterExpression:{键名:{匹配条件:条件值}}})
示例:
db.c1.createIndex({
name:1},{
background:true,partialFilterExpression:{
age:{
$lt:5}}});
通过getIndexes()查看索引。注意:mongodb客户端工具可以正常查看,在navicat中查看只显示部分数据。
说明:部分索引只为集合中那些满足指定的筛选条件的文档创建索引。如果你指定的partialFilterExpression和唯一约束、那么唯一性约束只适用于满足筛选条件的文档。具有唯一约束的部分索引不会阻止不符合唯一约束且不符合过滤条件的文档的插入。
name为张三的人年龄不能重复。表达式不支持非操作($ne)
db.c1.createIndex({
age:1},{
background:true,unique:true,partialFilterExpression:{
name:{
$eq:"张三"}}});
稀疏索引(sparse index)
稀疏索引仅包含具有索引字段的文档的条目,即使索引字段包含空值也是如此。索引会跳过缺少索引字段的任何文档。索引是“稀疏的”,因为它不包含集合的所有文档。相反,非稀疏索引包含集合中的所有文档,为那些不包含索引字段的文档存储空值。
语法格式:db.COLLECTION_NAME.createIndex({索引键名:排序规则},{sparse:true})
注意:从MongoDB 3.2开始,MongoDB提供了创建部分索引的选项 。部分索引提供了稀疏索引功能的超集。如果您使用的是MongoDB 3.2或更高版本,则部分索引应优先于稀疏索引。
覆盖索引查询(适用于对性能要求及其高的场景)
官方的MongoDB的文档中说明,覆盖查询是以下的查询:
- 所有的查询字段是索引的一部分
- 所有的查询返回字段在同一个索引中
由于所有出现在查询中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。
因为索引存在于RAM中,从索引中获取数据比通过扫描文档读取数据要快得多。
如有如下索引:
db.stu.createIndex({
title:1,:size:1})
那么执行如下查询时,该索引会覆盖查询:
db.stu.find({
title:"dev"},{
size:1,_id:0})
也就是说,对于上述查询,MongoDB的不会去数据库文件中查找。相反,它会从索引中提取数据,这是非常快速的数据查询。
查询计划
在MongoDB中通过explain()函数启动执行计划,我们可以使用查询计划分析索引的使用情况,可通过查看详细的查询计划来决定如何优化。
语法结构:db.COLLECTION_NAME.find().explain()
删除dev集合中的所有索引。通过查询计划查看查询size键的值大于200的
为size键创建单字段索引。再次查看查询结果。
创建索引
查看执行结果
使用索引注意事项
既然索引可以加快查询速度,那么是不是只要是查询语句,就创建索引呢?答案是否定的。因为索引虽然加快了查询速度,但索引也是有代价的:索引文件本身要消耗存储空间,同时索引会加重插入、删除和修改记录时的负担,另外,数据库在运行时也要消耗资源维护索引,因此索引并不是越多越好。
那么什么情况不建议创建索引呢?例如一两千条甚至只有几百条记录的表,没必要建索引,让查询做全集合扫描就好了。至于多少条记录才算多?以万为单位来做索引。
如何创建合适的索引
建立合适的索引
为每一个常用查询结构建立合适的索引。
复合索引是创建的索引由多个字段组成,例如:
db.test.createIndex({
"username":1, "age":-1})
交叉索引是每个字段单独建立索引,但是在查询的时候组合查找,例如:
db.test.createIndex({
"username":1})
db.test.createIndex({
"age":-1})
db.test.find({
"username":"kaka", "age": 30})
交叉索引的查询效率较低,在使用时,当查询使用到多个字段的时候,尽量使用复合索引,而不是交叉索引。
复合索引的字段排列顺序
当我们的组合索引内容包含匹配条件以及范围条件的时候,比如包含用户名(匹配条件)以及年龄(范围条件),那么匹配条件应该放在范围条件之前。
比如需要查询:
db.test.find({
"username":"kaka", "age": {
$gt: 30}})
那么复合索引应该这样创建:
db.test.ensureIndex({
"username":1, "age":-1})
查询时尽可能仅查询出索引字段
有时候仅需要查询少部分的字段内容,而且这部分内容刚好都建立了索引,那么尽可能只查询出这些索引内容,需要用到的字段显式声明(_id字段需要显式忽略!)。因为这些数据需要把原始数据文档从磁盘读入内存,造成一定的损耗。
比如说我们的表有三个字段:
name, age, mobile
索引是这样建立的:
db.stu.createIndex({
"name":1,"age":-1})
我们仅需要查到某个用户的年龄(age),那可以这样写:
db.stu.find({
"name":"kaka"}, {
"_id":0, "age":1})
注意到上面的语句,我们除了”age”:1外,还加了”_id”:0,因为默认情况下,_id都是会被一并查询出来的,当不需要_id的时候记得直接忽略,避免不必要的磁盘操作。
对现有的数据大表建立索引的时候,采用后台运行方式
在对数据集合建立索引的过程中,数据库会停止该集合的所有读写操作,因此如果建立索引的数据量大,建立过程慢的情况下,建议采用后台运行的方式,避免影响正常业务流程。
db.stu.ensureIndex({
"name":1,"age":-1},{
"background":true})
索引限制
额外开销
每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。反之:使用索引的属性一定查询次数远远高于增加、删除、修改次数。
内存使用
由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。
如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。
查询限制
索引不能被以下的查询使用:
正则表达式(最左匹配除外)及非操作符,如 $nin, $not, 等。
算术运算符,如 $mod, 等。
所以,检测你的语句是否使用索引是一个好的习惯,可以用explain来查看。
最大范围
集合中索引不能超过64个
索引名的长度不能超过128个字符
一个复合索引最多可以有31个字段