具体在什么场景下需要使用到MongoDB和需要注意到什么?我们一文知晓。
MongoDB简介
MongoDB是一个开源的、高性能、无模式的文本型数据库,当初的设计就是用于简化和方便扩展,是NoSQL(不仅仅是数据库)数据库中的产品的一种,是最像关系型数据库的非关系型数据库。
它支持的数据结构非常松散,是一种类似于JSON的格式叫做BSON,所以它既可以存储比较复杂的数据类型,又相当的灵活。MongoDB中的记录是一个文档,它是由字段和值(键值对,field:value)组成的数据结构,他的文档类似于JSON对象,就是一个文档认为就是一个对象,字段的数据类型是字符型,它的值除了使用基本的一些类型外,还可以包括其他文档、普通数组和文档数组。
对比表
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | / | 表连接、MonogoDB不支持 |
/ | 嵌入文档 | MongoDB通过嵌入式文档来替代多表连接 |
promary key | promary key | 主键、MongoDB自动将_id字段设置为主键 |
业务应用场景
在我们传统的关系型数据库,比如Mysql已经运用的非常的广泛了,但在数据操作的“三高”需求以及对应的Web2.0的网站需求面前,就显得有点力不从心了。
三高需求:
1.高并发(High performance),对数据库高并发读写的需求。比如双十一的时候,数据量是巨大的,对IO要求极为苛刻,那Mysql显得力不从心了。
2.高数据(Huge Storage),对海量数据的高效率存储和访问的需求,比如我们的微信,每天的数据量都是TB级别的,如果使用Mysql存储就难以高效了。
3.高可用及高可扩展(High Scalability&High Availability),对数据的高可扩展和高可用的需求。
而MongoDB可应对“三高”需求。
具体的应用场景可以以下查看:
使用场景 | 描述 |
---|---|
社交场景 | 存储用户信息机发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能 |
游戏场景 | 存储游戏用户信息,装备、积分等直接内嵌文档的形式存储,方便查询和高效率存储和访问 |
物流场景 | 存储订单信息,因为订单状态在运送过程中会不断更新,如果使用内嵌数组的方式存储,方便将订单变更读取出来 |
物联网场景 | 接入智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析 |
视频直播 | 存储用户信息、点赞互动信息等等 |
在这些应用场景中,数据操作方面的共同特点是:
- 数据量大
- 读写操作都很频繁
- 价值较低的数据,对事务性要求不高(这是缺点,比如转账等等)
所以,对于这些数据,我们更适合使用MongoDB来实现数据的存储
。
那什么时候选择MongoDB呢?
犹豫是否选择他可以考虑一下问题:
- 应用不需要事务及复杂的join支持
- 新应用下面,需求不断变化、数据摸摸暂时无法确定但又想快速迭代开发
- 应用读写频繁,需要在2k及以上的读写QPS下
- 应用大数据存储,需要TB甚至PB级别的数据存储
- 应用发展的速度非常快,需要可以快速的水平扩展
- 应用要求的数据比较安全,不能丢失的
- 高可用,应用需要5个9的高可用。
- 应用需要大量的地址位置查询、文本查询等等。
如果上诉有1个符合,可以考虑MongoDB,但有2个及以上的符合,选择MongoDB绝不会后悔
单机部署
windows 安装
下载地址为: https://www.mongodb.com/try/download/community 选择自己合适的版本、系统镜像。
版本规范:
一般来说,版本都是X.Y.Z三部分结构:主版本号(Major)、次版本号(Minor)、修订号(Patch)是最常见的格式,遵循语义化版本规范(SemVer)。
MongoDB的版本,minor为奇数的时候,当前版本为开发版本,比如1.5.2,1.7.1等等。
当minor偶数的时候表示当前版本为偶数版,如:1.2.3,1.6.5等等。
patch是修正的版本号,数字越大越好,表示缺陷越少。
我是下载一个解压版本,直接解压到本地目录即可。
然后在同级目录下创建data\db
文件夹,比如我的就是D:\Program Files\mongodb-windows-x86_64-8.0.8\mongodb-win32-x86_64-windows-8.0.8\data\db
,
这样起来的命令,就是在解压后的bin文件夹下面运行mongod --dbpath="D:\Program Files\mongodb-windows-x86_64-8.0.8\mongodb-win32-x86_64-windows-8.0.8\data\db"
D:\Program Files\mongodb-windows-x86_64-8.0.8\mongodb-win32-x86_64-windows-8.0.8\bin>mongod --dbpath="D:\Program Files\mongodb-windows-x86_64-8.0.8\mongodb-win32-x86_64-windows-8.0.8\data\db"
{"t":{"$date":"2025-05-13T15:36:34.055+08:00"},"s":"I", "c":"CONTROL", "id":23285, "ctx":"thread1","msg":"Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'"}
{"t":{"$date":"2025-05-13T15:36:34.056+08:00"},"s":"I", "c":"CONTROL", "id":5945603, "ctx":"thread1","msg":"Multi threading initialized"}
那怎么去链接他呢?因为在MongoDB 5.0+ 推荐使用 mongosh(新版 Shell),旧版可能仍使用 mongo。所以新版本的是需要安装mongosh,官方下载地址:https://www.mongodb.com/try/download/shell
然后配置下环境变量,这样就可以在任何地方进行启动了。
C:\Users\Administrator>mongosh --version
2.5.0
C:\Users\Administrator>mongosh
Current Mongosh Log ID: 6822f872071b3c17c3b5f898
Connecting to: mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.5.0
Using MongoDB: 8.0.8
Using Mongosh: 2.5.0
mongosh 2.5.1 is available for download: https://www.mongodb.com/try/download/shell
For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/
------
The server generated these startup warnings when booting
2025-05-13T15:36:34.556+08:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
2025-05-13T15:36:34.557+08:00: This server is bound to localhost. Remote systems will be unable to connect to this server. Start the server with --bind_ip <address> to specify which IP addresses it should serve responses from, or with --bind_ip_all to bind to all interfaces. If this behavior is desired, start the server with --bind_ip 127.0.0.1 to disable this warning
------
test> show dbs
admin 40.00 KiB
config 48.00 KiB
local 72.00 KiB
安装Linux版本
1.添加MongoDB官方yum源。sudo vi /etc/yum.repos.d/mongodb-org-4.4.repo
以4.4为例子,其他版本请修改版本号。
[mongodb-org-4.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.4.asc
2.安装mongoDB sudo yum install -y mongodb-org
3.启动服务并设置开机自启
sudo systemctl start mongod
sudo systemctl enable mongod
4.验证安装
[root@VM-24-6-centos opt]# mongo --version
MongoDB shell version v4.4.29
Build Info: {
"version": "4.4.29",
"gitVersion": "f4dda329a99811c707eb06d05ad023599f9be263",
"openSSLVersion": "OpenSSL 1.0.1e-fips 11 Feb 2013",
"modules": [],
"allocator": "tcmalloc",
"environment": {
"distmod": "rhel70",
"distarch": "x86_64",
"target_arch": "x86_64"
}
}
[root@VM-24-6-centos opt]# systemctl status mongod
● mongod.service - MongoDB Database Server
Loaded: loaded (/usr/lib/systemd/system/mongod.service; enabled; vendor preset: disabled)
Active: active (running) since Tue 2025-05-13 16:45:32 CST; 15s ago
Docs: https://docs.mongodb.org/manual
Main PID: 31004 (mongod)
CGroup: /system.slice/mongod.service
└─31004 /usr/bin/mongod -f /etc/mongod.conf
5.开启端口、防火墙以及确认MongoDB监听所有网络接口。
vim /etc/mongod.conf
net:
port: 27017
bindIp: 0.0.0.0 # 修改为 0.0.0.0 或指定服务器IP
接着重启systemctl restart mongod
6.测试
C:\Users\Administrator> mongosh "mongodb://175.178.79.250:27017"
Current Mongosh Log ID: 6823084865720ee1d5b5f898
Connecting to: mongodb://175.178.79.250:27017/?directConnection=true&appName=mongosh+2.5.0
MongoNetworkError: connect ECONNREFUSED 175.178.79.250:27017
PS C:\Users\Administrator> mongosh "mongodb://175.178.79.250:27017"
Current Mongosh Log ID: 682308a16cf231d0f5b5f898
Connecting to: mongodb://175.178.79.250:27017/?directConnection=true&appName=mongosh+2.5.0
Using MongoDB: 4.4.29
Using Mongosh: 2.5.0
mongosh 2.5.1 is available for download: https://www.mongodb.com/try/download/shell
For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/
------
The server generated these startup warnings when booting
2025-05-13T16:53:49.065+08:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
2025-05-13T16:53:50.163+08:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
2025-05-13T16:53:50.163+08:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never'
2025-05-13T16:53:50.163+08:00: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. We suggest setting it to 'never'
------
基本常用命令
熟悉任何数据库的都知道,所有的数据库无非就是增删改查吧。我们基于背景来学习吧。我们来存储一个评论的表。
数据库:articledb,有这一些字段:
字段名称 | 字段含义 | 字段类型 | 备注 |
---|---|---|---|
_id | ID | ObjectId或String | Mongo的主键字段,自动生成的 |
articleid | 文章id | string | |
content | 评论内容 | string | |
userid | 评论人ID | string | |
nickname | 评论人昵称 | string | |
createdatetime | 评论的日期时间 | Date | |
likenum | 点赞数 | Int32 | |
replynum | 回复数 | Int32 | |
state | 状态 | String | 0:不可见,1:可见 |
parentid | 上级ID | String | 如果为0表示文章的顶级评论 |
选择和创建数据库
语法格式为 use 数据库名称
如果数据库不存在则自动创建,如果存在则选择这个数据库,例如创建评论库:use articledb
test> use articledb
switched to db articledb
articledb> show dbs
admin 40.00 KiB
config 48.00 KiB
local 72.00 KiB
现在articledb好像没看到在数据集合里面是吧,那是因为现在里面还没有数据,这个只是存在于内存里面,等有数据之后才会存在去磁盘中去持久化。 我们也看到有admin、config、local这三个默认的数据库,有什么用呢?
- admin:从权限的角度来看,这是一个root数据库,要是将一个用户添加到这个数据库里面,这个用户就自动继承所有数据库的权限,一些特定的服务器命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器等等。
- local:这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合。
- config:当Mongo用于分片设置的时候,config数据库在内部使用,用户保存分片的相关信息,后面讲到分布式的时候会提。
集合操作
集合,就类似于关系型数据库里面的表,可以显示的创建也可以隐式的创建。
比如我们要创建一个“my”的集合,就是db.createCollection("my")
articledb> db.createCollection("my")
{ ok: 1 }
articledb> show collections
my
articledb> show dbs
admin 40.00 KiB
articledb 8.00 KiB
config 96.00 KiB
local 72.00 KiB
可以看出创建完成之后,我们再次的‘show dbs’,这时候articledb就有值了。
那我这个集合不用了,如何删除呢?其实也是比较简单的。就是db.collection.drop()
或者db.集合名.drop()
articledb> db.my.drop()
true
articledb> show collections
articledb> show dbs
admin 40.00 KiB
config 96.00 KiB
local 72.00 KiB
明显可以看出,删除完成之后,dbs里面也没有articledb库了。
文档CURD
文档(document)的数据结构和json基本一样,所有存储在集合中的数据都是BSON(二进制的json)格式。
文档的插入
分为单个文档的插入和多个文档的插入。
1.单个文档的插入:
使用insertOne()或者save()方法向集合插入文档,语法如下:
test> db.comment.insertOne(
... {
... "articleid": 100000,
... "content": "今天天气不错,挺风和日丽的",
... "userid": 1001,
... "nickname": "张三"
... })
{
acknowledged: true,
insertedId: ObjectId('689955dc9c524c1c26b5f89a')
}
也就是说有一个comment集合,没有创建会隐式创建。可以通过 db.comment.find()
来进行查看。
2.多个文档的插入:
使用insertMany()的方式插入,比如有一个评论表,是一个多个的插入。语法如下:
test> db.comment.insertMany([
... {"articleid": 100000,"content": "今天天气不错,挺风和日丽的"},
... {"articleid": 100001,"content": "吾道一以贯之"}])
{
acknowledged: true,
insertedIds: {
'0': ObjectId('689957fe9c524c1c26b5f89c'),
'1': ObjectId('689957fe9c524c1c26b5f89d')
}
}
save()已经弃用
自 MongoDB 4.2 版本起,save() 方法被标记为弃用,save() 的行为(根据 _id 存在与否决定插入或更新)可以通过 insertOne() 或 replaceOne() 更清晰地实现。直接使用 insertOne 或批量操作方法(如 insertMany)能更好地控制写入策略.
由于mongodb并没有事务的概念,如果批量多个文档插入,有一条失败了数据并不会回滚,此时可以通过try catch来得知具体的失败。比如:
test> try{
... db.comment.insertMany([
... {"_id":1,"articleid": 100000, "content": "今天天气不错,挺风和日丽的"},
... {"_id":2,"articleid": 100001, "content": "吾道一以贯之"}
... ]);
... }catch(e){
... print(e)
... }
{ acknowledged: true, insertedIds: { '0': 1, '1': 2 } }
test> db.comment.find()
[
{ _id: 1, articleid: 100000, content: '今天天气不错,挺风和日丽的' },
{ _id: 2, articleid: 100001, content: '吾道一以贯之' }
]
test> try{
... db.comment.insertMany([
... {"_id":1,"articleid": 100000, "content": "今天天气不错,挺风和日丽的"},
... {"_id":2,"articleid": 100001, "content": "吾道一以贯之"}
... ]);
... }catch(e){
... print(e)
... }
MongoBulkWriteError: E11000 duplicate key error collection: test.comment index: _id_ dup key: { _id: 1 }
at OrderedBulkOperation.handleWriteError (eval at module.exports (node:lib-boxednode/mongosh:103:20), <anonymous>:3:3182162)
at eval (eval at module.exports (node:lib-boxednode/mongosh:103:20), <anonymous>:3:3176690)
at async topology (eval at module.exports (node:lib-boxednode/mongosh:103:20), <anonymous>:3:3476854) {
errorLabelSet: Set(0) {},
errorResponse: {
message: 'E11000 duplicate key error collection: test.comment index: _id_ dup key: { _id: 1 }',
code: 11000,
writeErrors: [
WriteError {
err: {
index: 0,
code: 11000,
errmsg: 'E11000 duplicate key error collection: test.comment index: _id_ dup key: { _id: 1 }',
errInfo: undefined,
op: { _id: 1, articleid: 100000, content: '今天天气不错,挺风和日丽的' }
}
}
]
},
code: 11000,
writeErrors: [
WriteError {
err: {
index: 0,
code: 11000,
errmsg: 'E11000 duplicate key error collection: test.comment index: _id_ dup key: { _id: 1 }',
errInfo: undefined,
op: { _id: 1, articleid: 100000, content: '今天天气不错,挺风和日丽的' }
}
}
],
result: BulkWriteResult {
insertedCount: 0,
matchedCount: 0,
modifiedCount: 0,
deletedCount: 0,
upsertedCount: 0,
upsertedIds: {},
insertedIds: {}
}
}
文档的查询
1.查询所有,如果我们要查询集中的所有文档,我们可以使用db.xxx.find().比如:
test> db.comment.find()
[
{
_id: ObjectId('689956759c524c1c26b5f89b'),
articleid: 100000,
content: '今天天气不错,挺风和日丽的'
},
{
_id: ObjectId('689957fe9c524c1c26b5f89c'),
articleid: 100000,
content: '今天天气不错,挺风和日丽的'
},
{
_id: ObjectId('689957fe9c524c1c26b5f89d'),
articleid: 100001,
content: '吾道一以贯之'
}
]
这里可以发现每条文档都会有一个叫做_id的字段,这个相当于我们mysql中表的主键,当你在插入文档的时候没有指定这个字段,MongoDB会自动创建,类型就是ObjectID类型。 如果我要查询具体的信息呢?比如articleid为100000的信息。我们就可以这样做 db.xxx.find({条件}):
test> db.comment.find({articleid:100000})
[
{
_id: ObjectId('689956759c524c1c26b5f89b'),
articleid: 100000,
content: '今天天气不错,挺风和日丽的'
},
{
_id: ObjectId('689957fe9c524c1c26b5f89c'),
articleid: 100000,
content: '今天天气不错,挺风和日丽的'
}
]
但如果我要查询一条呢?可以使用findOne,比如db.comment.findOne({articleid:100000})
2.投影查询,也就是类似mysql的select 后面的具体字段,比如我只是想查询content,就是这样:
test> db.comment.find({articleid:100000},{articleid:1})
[ { _id: ObjectId('68996af49c524c1c26b5f89e'), articleid: 100000 } ]
test> db.comment.find({articleid:100000},{articleid:1,_id:0})
[ { articleid: 100000 } ]
在find的后面再加一个类型,把需要显示的字段置为1,不需要的字段置为0即可。
文档的更新
更新文档的语法是:db.xxx.updateOne(query,update,options)
要求更新操作必须使用原子操作符(如 $set、$inc 等),直接替换整个文档需改用 replaceOne()。
test> db.comment.updateOne({articleid:100000},{$set:{"content":"今天天气很差,雷声滚滚"}})
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
test> db.comment.find()
[
{ _id: 1, articleid: 100000, content: '今天天气很差,雷声滚滚' },
{ _id: 2, articleid: 100001, content: '吾道一以贯之' }
]
删除
删除文档的语法,和更新类似,也是db.xxx.deleteOne(条件) 或者是 db.xxx.deleteMany(条件)
比如:
test> db.comment.find()
[
{ _id: 1, articleid: 100000, content: '今天天气很差,雷声滚滚1' },
{ _id: 2, articleid: 100001, content: '吾道一以贯之' }
]
test> db.comment.deleteOne({"articleid":100000})
{ acknowledged: true, deletedCount: 1 }
test> db.comment.find()
[ { _id: 2, articleid: 100001, content: '吾道一以贯之' } ]
那么删库跑路呢?就是db.collectionName.deleteMany({})
[ { _id: 2, articleid: 100001, content: '吾道一以贯之' } ]
test> db.comment.deleteMany({})
{ acknowledged: true, deletedCount: 1 }
分页查询
统计查询使用countDocuments()方法来的,语法如下:db.xxx.countDocuments()
,当然里面那也是可以带条件的。
test> db.comment.countDocuments()
7
test> db.comment.countDocuments({"articleid":100006})
1
如果想返回指定条数的记录,可以在find()查询之后调用limit来返回结果,默认为20,比如:
test> db.comment.find().limit(2)
[
{
_id: ObjectId('68999a199c524c1c26b5f8a0'),
articleid: 100000,
content: '今天天气不错,挺风和日丽的'
},
{
_id: ObjectId('68999a199c524c1c26b5f8a1'),
articleid: 100001,
content: '吾道一以贯之'
}
]
那这样如何做分页呢?比如每页显示两条,我要查第二页怎么办呢?那就要跳过,就是skip了。比如:
test> db.comment.find().limit(2).skip(2)
[
{
_id: ObjectId('68999a199c524c1c26b5f8a2'),
articleid: 100002,
content: '吾道二以贯之'
},
{
_id: ObjectId('68999a199c524c1c26b5f8a3'),
articleid: 100003,
content: '吾道三以贯之'
}
]
排序查询
sort方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1来制定排序的方式,其中1为升序,-1为降序排列。
语法如下所示:db.xxx.find().sort(排序方式)
test> db.comment.find().sort({"articleid":-1})
[
{
_id: ObjectId('68999a199c524c1c26b5f8a6'),
articleid: 100006,
content: '吾道六以贯之'
},
{
_id: ObjectId('68999a199c524c1c26b5f8a5'),
articleid: 100005,
content: '吾道五以贯之'
},
{
_id: ObjectId('68999a199c524c1c26b5f8a4'),
articleid: 100004,
content: '吾道四以贯之'
}
]
比较查询
操作符 | 说明 | 示例 | 类似 SQL |
---|---|---|---|
$eq |
等于指定值 | db.users.find({ age: { $eq: 25 } }) |
WHERE age = 25 |
$ne |
不等于指定值 | db.users.find({ age: { $ne: 25 } }) |
WHERE age != 25 |
$gt |
大于指定值 | db.users.find({ age: { $gt: 30 } }) |
WHERE age > 30 |
$gte |
大于或等于指定值 | db.users.find({ age: { $gte: 30 } }) |
WHERE age >= 30 |
$lt |
小于指定值 | db.users.find({ age: { $lt: 20 } }) |
WHERE age < 20 |
$lte |
小于或等于指定值 | db.users.find({ age: { $lte: 20 } }) |
WHERE age <= 20 |
$in |
匹配数组中任意值 | db.users.find({ age: { $in: [25,30] } }) |
WHERE age IN (25,30) |
$nin |
不匹配数组中任意值 | db.users.find({ age: { $nin: [25,30] } }) |
WHERE age NOT IN (25,30) |
test> db.comment.find({articleid:{$gt:100003}})
[
{
_id: ObjectId('68999a199c524c1c26b5f8a4'),
articleid: 100004,
content: '吾道四以贯之'
},
{
_id: ObjectId('68999a199c524c1c26b5f8a5'),
articleid: 100005,
content: '吾道五以贯之'
},
{
_id: ObjectId('68999a199c524c1c26b5f8a6'),
articleid: 100006,
content: '吾道六以贯之'
}
]
条件查询
如果需要查询同事满足两个以上条件,需要使用$and
操作符将条件进行关联,相当于mysql的and。格式就是$and:[{},{}]
比如:我要查询articleid大于等于100001且小于100003的。
test> db.comment.find({$and:[{articleid:{$gte:100001}},{articleid:{$lt:100003}}]})
[
{
_id: ObjectId('68999a199c524c1c26b5f8a1'),
articleid: 100001,
content: '吾道一以贯之'
},
{
_id: ObjectId('68999a199c524c1c26b5f8a2'),
articleid: 100002,
content: '吾道二以贯之'
}
]
当然也有or的,比如我要查询articleid等于100001或者等于100003的
test> db.comment.find({$or:[{articleid:{$eq:100001}},{articleid:100003}]})
[
{
_id: ObjectId('68999a199c524c1c26b5f8a1'),
articleid: 100001,
content: '吾道一以贯之'
},
{
_id: ObjectId('68999a199c524c1c26b5f8a3'),
articleid: 100003,
content: '吾道三以贯之'
}
]
eq可以不用写。
索引-Index
索引支持在MongoDB中高效的执行查询,如果没有索引,MongoDB必须执行全集合扫描,即扫描集合中的每个文档,以选择查询语句匹配的文档,这种扫描全集合的查询效率是非常低的,特别是在处理大量的数据时,查询可以花费几十秒甚至几分钟,这对网站的性能是非常致命的。
如果查询存在适当的索引,MongoDB可以使用该索引限制必须检查的文档数。
索引是特殊的数据结构,它以易于遍历的形式存储集合数据集的一小部分,索引存储特定字段或一组字段的值,按字段值排序,索引项的排序支持有效的相等匹配和基于范围查询操作,此外MongoDB还可以索引中的排序返回排序结果。 MongoDB索引使用B数据结构,确切的说是B数,Mysql是B+数
查看索引
命令很简单,就是db.xxx.getIndexes()
返回一个集合中的所有索引的数组,如下:
test> db.comment.getIndexes()
[ { v: 2, key: { _id: 1 }, name: '_id_' } ]
可以看出他是一个数组,这个数组有包含了多个索引,可以看出这个_id这个索引并不是我们创建的,类似于mysql一样,会隐式的创建一个索引,也就是主键,会默认创建一个唯一索引。 我们看下这四个字段:
v:代表是一个版本号,不用关心。
key:表示哪个字段加的索引,后面这个1代表是升序的方式
name:就表示索引的名称,一般是由_来表示。
ns:表示当前的命名空间,你这个索引是存放在哪个索引下面。
索引的查询是比较简单的。
创建索引
单字段索引
创建索引的语法就是db.collection.createIndex({ <field>: <order> }, { options })
**<field>**: 要创建索引的字段名。
**<order>**: 排序方式,1 为升序,-1 为降序
**options**: 可选参数,控制索引行为(如唯一性、后台创建等)
比如要给articleid建立索引
test> db.comment.createIndex({articleid:1})
articleid_1
test> db.comment.createIndex({content:-1})
content_-1
test> db.comment.getIndexes()
[
{ v: 2, key: { _id: 1 }, name: '_id_' },
{ v: 2, key: { articleid: 1 }, name: 'articleid_1' },
{ v: 2, key: { content: -1 }, name: 'content_-1' }
]
建立了两个索引,就是articleid是升序的。
多字段索引-复合索引
对于articleid和content同时创建复合索引就是
test> db.comment.createIndex({articleid:1,content:-1})
articleid_1_content_-1
test> db.comment.getIndexes()
[
{ v: 2, key: { _id: 1 }, name: '_id_' },
{ v: 2, key: { articleid: 1 }, name: 'articleid_1' },
{ v: 2, key: { content: -1 }, name: 'content_-1' },
{
v: 2,
key: { articleid: 1, content: -1 },
name: 'articleid_1_content_-1'
}
]
索引的移除
可以移除指定的索引,也可以移除所有索引。
移除指定索引,语法是db.xxx.dropIndex(indexname)
如:
可以通过创建时候的规则去删除,也可以通过名称是去删除。
test> db.comment.dropIndex({articleid:1})
{ nIndexesWas: 4, ok: 1 }
test> db.comment.dropIndex("articleid_1_content_-1")
{ nIndexesWas: 3, ok: 1 }
test> db.comment.getIndexes()
[
{ v: 2, key: { _id: 1 }, name: '_id_' },
{ v: 2, key: { content: -1 }, name: 'content_-1' }
]
是不是有一个想法,如果说根据创建时候的规则去删除,那创建时候的规则不一样呢?比如content创建时候的规则是降序-1,那我删除的时候用升序会怎么样呢?答案是报错,找不到这个索引。
test> db.comment.dropIndex({content:1})
MongoServerError[IndexNotFound]: can't find index with key: { content: 1 }
test> db.comment.dropIndex({content:-1})
{ nIndexesWas: 2, ok: 1 }
删除所有的索引,那就比较简单了,直接是db.xxx.dropIndexes()
也不用指定什么删除了。比如:
test> db.comment.getIndexes()
[
{ v: 2, key: { _id: 1 }, name: '_id_' },
{ v: 2, key: { articleid: 1 }, name: 'articleid_1' },
{ v: 2, key: { content: -1 }, name: 'content_-1' },
{
v: 2,
key: { articleid: 1, content: -1 },
name: 'articleid_1_content_-1'
}
]
test> db.comment.dropIndexes()
{
nIndexesWas: 4,
msg: 'non-_id indexes dropped for collection',
ok: 1
}
test> db.comment.getIndexes()
[ { v: 2, key: { _id: 1 }, name: '_id_' } ]
但是主键并没有被干掉,这个就不要想了,主键索引怎么可能会被干掉,mysql也是一样的。
执行计划
分析查询性能,通常使用执行计划来查看情况,比如查询耗时、是否基于索引查询等等,mysql也有次功能,就是explain这个,那么在mongodb里面是如何查看的呢?命令就是db.xxx.find(query,options).explain(options)
比如查询具体的某一个条件:
test> db.comment.find({articleid:100006}).explain()
{
queryPlanner: {
plannerVersion: 1,
namespace: 'test.comment',
indexFilterSet: false,
parsedQuery: { articleid: { '$eq': 100006 } },
queryHash: 'A6557C7C',
planCacheKey: 'A6557C7C',
winningPlan: {
stage: 'COLLSCAN',
filter: { articleid: { '$eq': 100006 } },
direction: 'forward'
},
rejectedPlans: []
},
serverInfo: {
host: 'VM-24-6-centos',
port: 27017,
version: '4.4.29',
gitVersion: 'f4dda329a99811c707eb06d05ad023599f9be263'
},
ok: 1
}
我们要关心的就是winningPlan这个,这个:
- stage是'COLLSCAN'集合扫描,也就是并没有用到索引,全局去扫描的。因为我们刚刚删掉全部所以了。
- rejectedPlans为空,表示未尝试其他执行计划,说明集合上可能没有针对articleid的索引。
- indexFilterSet为false,表示未启用所以过滤器。
当我们创建索引之后就会变成:
test> db.comment.createIndex({articleid:1})
articleid_1
test> db.comment.find({articleid:100006}).explain()
{
queryPlanner: {
plannerVersion: 1,
namespace: 'test.comment',
indexFilterSet: false,
parsedQuery: { articleid: { '$eq': 100006 } },
queryHash: 'A6557C7C',
planCacheKey: '17FE47AA',
winningPlan: {
stage: 'FETCH',
inputStage: {
stage: 'IXSCAN',
keyPattern: { articleid: 1 },
indexName: 'articleid_1',
isMultiKey: false,
multiKeyPaths: { articleid: [] },
isUnique: false,
isSparse: false,
isPartial: false,
indexVersion: 2,
direction: 'forward',
indexBounds: { articleid: [ '[100006, 100006]' ] }
}
},
rejectedPlans: []
},
serverInfo: {
host: 'VM-24-6-centos',
port: 27017,
version: '4.4.29',
gitVersion: 'f4dda329a99811c707eb06d05ad023599f9be263'
},
ok: 1
}
- stage变为了FETCH,抓取,表示通过索引扫描并定位数据了。
- 此时的rejectedPlans还是空,但是得分情况,如果说是stage为COLLSCAN全表扫描为空,则表明没有找到合适的索引,性能很差,但如果是IXSCAN索引扫描为空,则表示当前索引是唯一可行的方案,此时可认为是高效的。