MongoDB-一文了解基本概念(快速上手)

miloyang
0 评论
/ /
162 阅读
/
27819 字
29 2025-04

具体在什么场景下需要使用到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索引扫描为空,则表示当前索引是唯一可行的方案,此时可认为是高效的。
人未眠
工作数十年
脚步未曾歇,学习未曾停
乍回首
路程虽丰富,知识未记录
   借此博客,与之共进步