MongoDB是一个基于分布式文件存储的数据库,由C++编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB将数据存储为一个文档,数据结构由键值对组成,MongoDB文档类似于JSON对象,字段值可以包含其他文档,数组及文档数组。

MongoDB服务端可运行在Linux、Windows或macOS X平台,支持32位和64位应用,默认端口为27017。

安装

Windows

进入MongoDB官网下载预编译二进制包(下载地址:https://www.mongodb.com/download-center/community),打开之后选择Version后,点击Download下载即可。

msi安装向导

  1. 点击下载好的mongodb-windows-x86_64-6.0.5-signed.msi,安装类型需要选择Custom

  2. 修改安装目录为:D:\usr\local\mongodb

  3. 服务配置保持默认值;

  4. 需要将Install MongoDB Compass的勾去掉,否则安装过程中会下载Install MongoDB Compass并安装,比较耗费时间。Install MongoDB Compass是一个图形界面管理工具,可以使用其他GUI管理工具,如:Navicat for MongoDB

    之后一直Next直到安装完成。

    安装完成后,浏览器访问:http://localhost:27017/,如果显示:It looks like you are trying to access MongoDB over HTTP on the native driver port.说明MongoDB已经安装成功了。

zip手动安装

下载地址:https://fastdl.mongodb.org/windows/mongodb-windows-x86_64-6.0.10.zip

  1. 解压mongodb-windows-x86_64-6.0.10.zip文件到:D:\usr\local\mongodb
  2. 切换到D:\usr\local\mongodb目录,创建datalog目录,分别用于存储数据库和日志文件;
  3. 切换到D:\usr\local\mongodb目录,创建配置文件mongod.cfg,内容参考:

    storage:
      dbPath: D:\usr\local\mongodb\data
    
    systemLog:
      destination: file
      logAppend: true
      path:  D:\usr\local\mongodb\log\mongod.log
    
    net:
      port: 27017
      bindIp: 127.0.0.1
  4. 注册MongoDB服务

    C:\Users\Administrator> D:\usr\local\mongodb\bin\mongod --config "D:\usr\local\mongodb\mongod.cfg" --install --serviceName "MongoDB"
  5. 启动MongoDB服务

    C:\Users\Administrator> net start mongodb
  6. 卸载MongoDB服务

    C:\Users\Administrator> D:\usr\local\mongodb\bin\mongod --remove

Linux

5.0.9

[root@ecs src]# wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-5.0.9.tgz
[root@ecs src]# tar -zxvf mongodb-linux-x86_64-rhel70-5.0.9.tgz -C /usr/local
[root@ecs src]# mv /usr/local/mongodb-linux-x86_64-rhel70-5.0.9 /usr/local/mongodb
[root@ecs src]# mkdir /usr/local/mongodb/{data,log}
[root@ecs src]# cat > /etc/mongodb.cnf <<EOF
dbpath=/usr/local/mongodb/data
logpath=/usr/local/mongodb/log/mongodb.log
port=27017
fork=true
journal=false
bind_ip=0.0.0.0
auth=true
EOF
[root@ecs src]# echo "export PATH=\$PATH:/usr/local/mongodb/bin/" >> /etc/profile
[root@ecs src]# source /etc/profile
[root@ecs src]# mongod -f /etc/mongodb.cnf
about to fork child process, waiting until server is ready for connections.
forked process: 20927
child process started successfully, parent exiting
  • fork=true:后台运行
  • auth=true:开启授权登录模式

重启

> use admin
> db.shutdownServer()
server should be down...
> exit
[root@ecs ~]# mongod --config /etc/mongodb.cnf

7.0.11

[root@ecs src]# wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.0.11.tgz
[root@ecs src]# tar -zxvf mongodb-linux-x86_64-rhel70-7.0.11.tgz
[root@ecs src]# mv mongodb-linux-x86_64-rhel70-7.0.11 /usr/local/mongodb
[root@ecs src]# mkdir /usr/local/mongodb/{data,log}
[root@ecs src]# cat > /etc/mongodb.conf <<EOF
storage:
  dbPath: /usr/local/mongodb/data

systemLog:
  destination: file
  logAppend: true
  path:  /usr/local/mongodb/log/mongod.log

net:
  port: 27017
  bindIp: 0.0.0.0


processManagement:
  fork: true
  pidFilePath: /var/run/mongod.pid
  timeZoneInfo: /usr/share/zoneinfo

security:
  authorization: enabled
EOF
[root@ecs src]# echo "export PATH=\$PATH:/usr/local/mongodb/bin/" >> /etc/profile
[root@ecs src]# source /etc/profile
[root@ecs src]# mongod -f /etc/mongodb.conf
[root@ecs src]# ps -aux | grep 'mongod' | grep -v 'grep'
root     26335  0.8  1.3 2699260 105684 ?      Sl   03:11   0:04 mongod -f /etc/mongodb.conf
  • fork: true:以守护程序的方式启用(后台运行)
  • bindIp: 0.0.0.0:允许外网访问
  • authorization: enabled:开启授权模式

Docker

  1. 创建容器;

    7.0.11

    [root@ecs ~]# docker run --name mongo \
    --restart=always \
    -p 27017:27017 \
    -v /data/mongo/data:/data/db \
    -v /data/mongo/log:/data/log \
    --privileged=true \
    -d registry.cn-shanghai.aliyuncs.com/maisi/mongo:7.0.11 --auth

    设置管理员账号和密码:

    [root@ecs ~]# docker run --name mongo \
    --restart=always \
    -p 27017:27017 \
    -v /data/mongo/data:/data/db \
    -v /data/mongo/log:/data/log \
    -e MONGO_INITDB_ROOT_USERNAME=admin \
    -e MONGO_INITDB_ROOT_PASSWORD=ay2ikfyYonmKvFkS \
    --privileged=true \
    -d registry.cn-shanghai.aliyuncs.com/maisi/mongo:7.0.11

    4.2.8

    [root@ecs ~]# docker run --name mongo \
    --restart=always \
    -p 27017:27017 \
    -v /data/mongo/data:/data/db \
    -v /data/mongo/log:/data/log \
    -e MONGO_INITDB_ROOT_USERNAME=admin \
    -e MONGO_INITDB_ROOT_PASSWORD=ay2ikfyYonmKvFkS \
    --privileged=true \
    -d registry.cn-shanghai.aliyuncs.com/maisi/mongo:4.2.8
    • --auth要求必须验证密码才能进行基于角色的访问控制,没有密码虽然可以登录shell,但是无法执行实质性的数据操作;
    • 即使没有添加--auth,只要设置了MONGO_INITDB_ROOT_USERNAMEMONGO_INITDB_ROOT_PASSWORD环境变量,则自动追加--auth
    • 即使设置了--auth,由于MongoDB有本地例外(Localhost Exception)机制,即数据库中没有创建任何超级用户时,为了防止锁死,仍然允许localhost或127.0.0.1发起的连接拥有最高权限。添加了超级用户后,本地例外机制失效。
  2. 操作MongoDB

    [root@ecs ~]# docker exec -it mongo /bin/bash
    • 进入mongo shell

      root@38857e5751d8:/# mongosh
      Current Mongosh Log ID: 6a6225fea56d2a74a7a26a12
      Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.6
      Using MongoDB:          7.0.11
      Using Mongosh:          2.2.6
      
      For mongosh info see: https://docs.mongodb.com/mongodb-shell/
      
      
      To help improve our products, anonymous usage data is collected and sent to MongoDB periodically (https://www.mongodb.com/legal/privacy-policy).
      You can opt-out by running the disableTelemetry() command.
      
      test>
    • 登录admin数据库

      [root@ecs ~]# docker exec -it mongo mongosh admin
      Current Mongosh Log ID: 6a6226363f9e10a413a26a12
      Connecting to:          mongodb://127.0.0.1:27017/admin?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.6
      Using MongoDB:          7.0.11
      Using Mongosh:          2.2.6
      
      For mongosh info see: https://docs.mongodb.com/mongodb-shell/
      
      admin> 
  3. 配置外网访问

    1. 进入mongodb容器

      [root@ecs ~]# docker exec -it mongo /bin/bash
    2. 容器内修改配置文件

      root@38857e5751d8:/# cp /etc/mongod.conf.orig /etc/mongod.conf
      root@38857e5751d8:/# sed -i 's/bindIp: 127.0.0.1/bindIp: 0.0.0.0/g' /etc/mongod.conf
    3. 退出容器并重启mongodb容器

      root@38857e5751d8:/# exit
      [root@ecs ~]# docker restart mongo

客户端操作

安装Navicat for MongoDB,创建数据库连接:

如果MongoDB设置了以授权方式登录,验证类型需要选择Password,验证数据库选择admin,输入用户名和密码后,可以点击测试连接测试一下数据库是否连接成功。

点击确定即可创建数据库连接了。

Navicat for MongoDB激活方式:

  1. 打开注册机程序,Patch勾选BackupHost,选择Navicat v 15。License选择Enterprise,Product选择MongoDB,Language选择Simplified Chinese

    点击Patch,出现以下提示表示navicat.exe破解成功,此时还没有许可证,接着往下操作;

  2. Keygen / Offline Activation,点击Generate生成序列号;

  3. 重新打开Navicat for MongoDB,在试用提醒对话框中选择“注册”;

  4. 输入第2步生成的序列号,点击“激活”;

  5. 选择“手动激活”,弹出“手动激活”对话框,复制请求码;

  6. 切换到Navicat Products - Patch/Keygen,将复制的请求码粘贴到Request Code输入框中,点击Activation Code下面的Generate生成激活码;

  7. 最后再将激活码回填到Navicat for MongoDB的激活窗口中,点击“激活”;

    出现永久许可证界面,表明已经激活成功了。

命令行操作

数据库

  • 进入mongo shell

    MongoDB 6之前版本:

    [root@ecs ~]# mongo
    MongoDB shell version v5.0.9
    connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
    Implicit session: session { "id" : UUID("dc9cb95a-f071-49de-bd6f-0dacae8e3b00") }
    MongoDB server version: 5.0.9
    ================
    Warning: the "mongo" shell has been superseded by "mongosh",
    which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in
    an upcoming release.
    For installation instructions, see
    https://docs.mongodb.com/mongodb-shell/install/
    ================
    >

    MongoDB自6.0开始不再默认安装shell工具,需要额外安装mongosh,下载地址:https://www.mongodb.com/try/download/shell

    [root@ecs ~]# mongosh
  • 连接远程数据库

    [root@ecs ~]# mongosh -u root -p root --port 27017 --host 192.168.1.215
  • 获取mongdb版本

    admin> db.version()
    7.0.11
    admin> db.system.version.find()
    [
      { _id: 'featureCompatibilityVersion', version: '7.0' },
      { _id: 'authSchema', currentVersion: 5 }
    ]
    admin> 
  • 授权登录

    方法1

    admin> db.auth('admin', 'ay2ikfyYonmKvFkS')
    { ok: 1 }
    admin> 

    方法2

    [root@ecs ~]# mongosh admin -u admin -p ay2ikfyYonmKvFkS
    Current Mongosh Log ID: 6a6237969b309148aaa26a12
    Connecting to:          mongodb://<credentials>@127.0.0.1:27017/admin?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.6
    Using MongoDB:          7.0.11
    Using Mongosh:          2.2.6
    
    For mongosh info see: https://docs.mongodb.com/mongodb-shell/
    
    admin> 

    方法3

    [root@ecs ~]# mongosh mongodb://admin:'ay2ikfyYonmKvFkS'@127.0.0.1:27017
    [root@ecs ~]# mongosh mongodb://admin:'ay2ikfyYonmKvFkS'@127.0.0.1:27017/maisi_db --authenticationDatabase admin

    当直接登录数据库时,默认会到登录数据库中获取用户名和密码,而超级用户是存储在admin库中的,因此会因找不到用户而报错。可以使用--authenticationDatabase指定授权数据库。

    [root@ecs ~]# mongosh maisi_db -u admin -p ay2ikfyYonmKvFkS --authenticationDatabase admin
  • 查看所有数据库

    admin> show dbs
    admin   100.00 KiB
    config   12.00 KiB
    local    72.00 KiB
  • 查看当前库下所有表

    admin> show tables
    system.users
    system.version
  • 切换test数据库

    admin> use test
    switched to db test
    test> 
  • 创建数据库(不存在则创建,存在则切换数据库)

    admin> use maisi_db
    switched to db maisi_db
    maisi_db> 
  • 删除数据库

    maisi_db> db.dropDatabase()
    { ok: 1, dropped: 'maisi_db' }
    maisi_db> 
  • 获取当前数据库名称

    maisi_db> db
    maisi_db
    maisi_db> db.getName()
    maisi_db
  • 获取数据库信息

    maisi_db> db.stats()
    {
      db: 'maisi_db',
      collections: Long('0'),
      views: Long('0'),
      objects: Long('0'),
      avgObjSize: 0,
      dataSize: 0,
      storageSize: 0,
      indexes: Long('0'),
      indexSize: 0,
      totalSize: 0,
      scaleFactor: Long('1'),
      fsUsedSize: 0,
      fsTotalSize: 0,
      ok: 1
    }
  • 备份数据库

    MongoDB导入导出工具从4.4版开始不再随数据库自动安装,而需手动安装MongoDB Database Tools,下载地址:https://www.mongodb.com/try/download/database-tools

    [root@ecs ~]# tar -zxvf mongodb-database-tools-rhel70-x86_64-100.8.0.tgz
    [root@ecs ~]# mv -uf mongodb-database-tools-rhel70-x86_64-100.8.0/bin/* /usr/local/mongodb/bin
    [root@ecs ~]# echo "export PATH=\$PATH:/usr/local/mongodb/bin" >> /etc/profile
    [root@ecs ~]# source /etc/profile
    [root@ecs ~]# mkdir -p /data/backup
    [root@ecs ~]# mongodb://root:'root'@127.0.0.1:27017/maisi_db --authenticationDatabase admin
    maisi_db> db.fsyncLock()

    另起一个终端:

    [root@ecs ~]# mongodump -u root --password 'root' --authenticationDatabase admin -h 127.0.0.1 -d maisi_db -o /data/backup

    或者:

    [root@ecs ~]# mongodump --uri=mongodb://root:root@127.0.0.1:27017/maisi_db?authSource=admin -o /data/backup

    等待备份完成,重新返回Mongo Shell,继续执行:

    maisi_db> db.fsyncUnlock()
  • 还原数据库

    maisi_db> db.dropDatabase()
    [root@ecs ~]# mongorestore -h 127.0.0.1:27017 --authenticationDatabase=admin -u root -p 'root' -d maisi_db /data/backup/maisi_db

    或者:

    [root@ecs ~]# mongorestore --drop -h 127.0.0.1:27017 --authenticationDatabase=admin -u root -p 'root' -d maisi_db /data/backup/maisi_db

    注:还原数据库之前,必须先删除旧库或者添加--drop,否则在恢复数据库的时候,集合中遇到相同的_id会报错。

用户

  • 创建超级权限root角色用户

    admin> db.createUser({user: 'root', pwd: 'root', roles: [{role: 'root', db: 'admin'}]})
    { ok: 1 }

    创建root级别的用户和管理员级别的用户都只能在admin数据库里面执行,创建指定数据库的所有者需要通过use切换数据库。另外,需要注意,系统级别的函数,比如:db.system.users.find()(查看系统所有用户),只能在admin数据库下执行,创建用户和执行其他系统函数的时候需要先使用db.auth鉴权

  • 添加最高级权限

    admin> db.grantRolesToUser("root", [{ role: "__system", db: "admin"}])
  • 创建库用户(必须先切换到对应库)

    admin> db.auth('root', 'root')
    admin> use maisi_db
    maisi_db> db.createUser({user: 'maisi', pwd: 'kJTzjgSnIKqbZ4UD', roles: [{role: 'dbOwner', db: 'maisi_db'}]})
    { ok: 1 }
  • 删除库用户

    maisi_db> db.dropUser('maisi')
    { ok: 1 }

    或者切换到admin库下操作db.system.users(必须切换到admin库,否则没有权限):

    admin> db.system.users.deleteOne({user: 'maisi'})
    { acknowledged: true, deletedCount: 1 }
  • 修改库用户密码

    maisi_db> db.updateUser('maisi', {pwd: 'sCUKbQgHwI1VdUIw'})
    { ok: 1 }

    或者:

    maisi_db> db.changeUserPassword("maisi", "sCUKbQgHwI1VdUIw")
    { ok: 1 }
  • 更改用户角色

    maisi_db> db.updateUser('maisi', {roles: [{ role: "dbOwner", db: "maisi_db" }]})
    { ok: 1 }
  • 查询所有用户(必须切换到admin)

    admin> db.system.users.find()
  • 查看当前数据库用户权限

    maisi_db> show users
    [
      {
        _id: 'maisi_db.maisi',
        userId: UUID('8737b649-ce51-4de3-8dac-54296969f66e'),
        user: 'maisi',
        db: 'maisi_db',
        roles: [ { role: 'dbOwner', db: 'maisi_db' } ],
        mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
      }
    ]
    
    maisi_db> db.getUsers()
    {
      users: [
        {
          _id: 'maisi_db.maisi',
          userId: UUID('8737b649-ce51-4de3-8dac-54296969f66e'),
          user: 'maisi',
          db: 'maisi_db',
          roles: [ { role: 'dbOwner', db: 'maisi_db' } ],
          mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
        }
      ],
      ok: 1
    }
    maisi_db> db.getUser('maisi')
    {
      _id: 'maisi_db.maisi',
      userId: UUID('8737b649-ce51-4de3-8dac-54296969f66e'),
      user: 'maisi',
      db: 'maisi_db',
      roles: [ { role: 'dbOwner', db: 'maisi_db' } ],
      mechanisms: [ 'SCRAM-SHA-1', 'SCRAM-SHA-256' ]
    }
  • 新增角色

    maisi_db> db.grantRolesToUser("maisi", [{role: "userAdmin", db: "maisi_db"}])
    { ok: 1 }
  • 删除角色

    maisi_db> db.revokeRolesFromUser("maisi", [{role: "userAdmin", db: "maisi_db"}])
    { ok: 1 }

集合

  • 查看所有集合

    maisi_db> show tables
    app_user
    maisi_db> show collections
    app_user
  • 写入文档

    mongo

    > db.app_user.insert({username: 'alice'})
    WriteResult({ "nInserted" : 1 })

    mongosh

    maisi_db> db.app_user.insertOne({username: 'bob', skill: ['电脑', '音乐']})
    {
      acknowledged: true,
      insertedIds: { '0': ObjectId('6a62dc4b8378d289b0a26a14') }
    }
  • 查询文档

    • 普通查询

      mongo

      > db.app_user.find()
      { "_id" : ObjectId("6a63707319c415e8b0f5d4de"), "username" : "bob", "skill" : [ "电脑", "音乐" ] }
      { "_id" : ObjectId("6a63708c19c415e8b0f5d4df"), "username" : "alice" }

      mongosh

      maisi_db> db.app_user.find()
      [
        { _id: ObjectId('6a62d6368378d289b0a26a13'), username: 'alice' },
        {
          _id: ObjectId('6a62dc4b8378d289b0a26a14'),
          username: 'bob',
          skill: [ '电脑', '音乐' ]
        }
      ]
    • 隐藏字段

      maisi_db> db.app_user.find({}, {username: 1, _id: 0})
      [ { username: 'alice' }, { username: 'bob' } ]
    • 限制数组字段元素个数

      maisi_db> db.app_user.find({}, {"skill": {$slice: [0,1]}})
      [
        { _id: ObjectId('6a62d6368378d289b0a26a13'), username: 'alice' },
        {
          _id: ObjectId('6a62dc4b8378d289b0a26a14'),
          username: 'bob',
          skill: [ '电脑' ]
        }
      ]

      $slice语法:

      解释
      $slice(n)返回数组前n个
      $slice(-n)返回数组后n个
      $slice(m, n)从数组第m+1个开始,返回前n个
      $slice(-m, n)从数组倒数第m个开始,返回前n个
    • 条件查询

      符号说明
      $gt大于
      $gte大于或等于
      $lt小于
      $lte小于或等于
      $ne不等于
      $in在范围内
      $nin不在范围内
      maisi_db> db.app_user.find({username: 'alice'})
      maisi_db> db.app_user.find({age: {$gt: 20}})
      maisi_db> db.app_user.find({age: {$gt: 20, $lt: 50}})
      maisi_db> db.app_user.find({username: {$ne: 'alice'}})
      maisi_db> db.app_user.find({username: {$in: ['alice', 'bob']}})
    • 去重查询

      maisi_db> db.app_user.distinct('username')
      [ "alice", "bob" ]
      maisi_db> db.app_user.distinct('username', {age: {$gt: 20}})
      [ "alice" ]
    • 正则查询

      maisi_db> db.app_user.find({username:/^ali/})
      maisi_db> db.app_user.find({username: {$regex: '^ali.*'}})
    • 字段存在查询

      maisi_db> db.app_user.find({age: {$exists: true}})

      注:$exists只判断字段是否存在,不判断字段的值。

    • 字段类型查询

      maisi_db> db.app_user.find({username: {$type: 'string'}})
    • 模查询

      maisi_db> db.app_user.find({age: {$mod: [10, 2]}})
    • 全文检索

      maisi_db> db.app_user.createIndex({skill: 'text', hobby: 'text'})
      {
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 2,
        "numIndexesAfter" : 3,
        "ok" : 1
      }
      maisi_db> db.app_user.find({$text: {$search: "电脑 游戏"}})
      maisi_db> db.app_user.find({$text: {$search: "-电脑 游戏"}})
      
      maisi_db> db.app_user.updateOne({username: 'alice'}, {$set: {hobby: 'watch TV,play game'}})
      maisi_db> db.app_user.find({$text: {$search: "\"play game\""}})
      
      maisi_db> db.app_user.find({$text: {$search: "电脑"}}, {score: {$meta: "textScore"}}).sort({score: {$meta: "textScore"}})

      注:使用全文检索必须给文档创建text索引,一个文档只能有一个text索引,可以包含多个字段。

      • 使用空格分隔搜索字符串,分割后的多个关键字执行逻辑或;
      • 排除某个关键字,在关键字前加--电脑表示排除文本索引中包含电脑的文档;
      • 短语查询通过双引号组合,双引号需要转义;
      • {$meta: "textScore"}计算并获取文档与检索关键字的相关性匹配得分。
    • 高级查询

      maisi_db> db.app_user.find({"$where": "this.username=='alice'"})
    • 分页

      maisi_db> db.app_user.find().limit(2).skip(0)
    • 排序(1升序,-1降序)

      maisi_db> db.app_user.find().sort({username: 1})
      maisi_db> db.app_user.find().sort({username: -1})
    • 逻辑或

      maisi_db> db.app_user.find({$or: [{username: 'alice'}, {age: {$lt: 30}}]})
  • 更新文档

    maisi_db> db.app_user.updateOne({"username": "bob"}, {$set: {"username": "alice"}})
    {
      acknowledged: true,
      insertedId: null,
      matchedCount: 1,
      modifiedCount: 1,
      upsertedCount: 0
    }

    或者:

    maisi_db> user = db.app_user.findOne({username: 'alice'})
    { _id: ObjectId('6a62d6368378d289b0a26a13'), username: 'alice' }
    maisi_db> user.username = 'bob'
    bob
    maisi_db> db.app_user.replaceOne({_id: user._id}, user, {upsert: true})
    {
      acknowledged: true,
      insertedId: null,
      matchedCount: 1,
      modifiedCount: 1,
      upsertedCount: 0
    }

    upsert: true:当找不到文档时,插入新文档。

    注:mongosh已经废弃了save()方法,save()仅适用于mongo。

    > user = db.app_user.findOne({username: 'alice'})
    > user.username = 'bob'
    > db.app_user.save(user)
  • 删除文档

    > db.app_user.remove({"username": "alice"}, {justOne: true, writeConcern: {w: 0}})
    WriteResult({ })

    justOne:是否只删除一个文档,默认为false,删除符合条件的所有文档,true则只删除一个文档。

    maisi_db> db.app_user.deleteMany({"username": "alice"});
    { acknowledged: true, deletedCount: 1 }
  • 调整文档结构

    • 新增字段

      > db.app_user.update({}, {$set: {age: ''}}, {multi: true})
      WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })

      multi: true:默认为false,只更新匹配的第一个文档,true则更新匹配的所有文档。

      maisi_db> db.app_user.updateMany({}, {$set: {age: ''}})
      {
        acknowledged: true,
        insertedId: null,
        matchedCount: 1,
        modifiedCount: 1,
        upsertedCount: 0
      }
    • 删除字段

      > db.app_user.update({}, {$unset: {age: ''}}, {multi: true})
      WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })

索引

  • 获取索引

    maisi_db> db.app_user.getIndexes()
    [ { v: 2, key: { _id: 1 }, name: '_id_' } ]
  • 创建索引

    • 排序索引(1升序,-1降序)

      maisi_db> db.app_user.ensureIndex({username: 1})
      [ 'username_1' ]

      或者:

      maisi_db> db.app_user.createIndex({username: 1})
      username_1
    • 复合索引

      maisi_db> db.app_user.ensureIndex({username: 1, age: 1})
      [ 'username_1_age_1' ]
    • 唯一索引

      maisi_db> db.app_user.ensureIndex({username: 1}, {unique: true})
      [ 'username_1' ]
    • 文本索引

      maisi_db> db.app_user.createIndex({skill: 'text'})
      skill_text
  • 删除索引

    • 排序索引

      maisi_db> db.app_user.dropIndex({username: 1})
      { nIndexesWas: 2, ok: 1 }
    • 复合索引

      maisi_db> db.app_user.dropIndex('username_1_age_1')
      { nIndexesWas: 2, ok: 1 }

聚合

db.collection.aggregate()是基于数据处理的聚合管道,每个文档通过管道进行处理后输出结果。

基本语法:db.collection.aggregate(pipeline, options)

  • pipeline:一系列阶段,管道由多个阶段(stage)组成。阶段包括:count、match(筛选)、project(映射)、group(分组)、unwind、sort(排序)、limit(限制)、skip(跳过)、sortByCount、lookup、out和addFields等;
  • options:可选,仅pipeline为数组时才能指定options。

$count

返回管道中的文档数。

maisi_db> db.app_user.aggregate([{$count: "total"}])
[ { total: 2 } ]
maisi_db> db.app_user.aggregate([{$match: {skill: {$regex: '电脑|游戏'}}}, {$count: 'total'}])
[ { total: 1 } ]
  • $match阶段skill包含“电脑”或“游戏”的文档传入下个阶段;
  • $count阶段返回管道中剩余文档的计数,并将该值分配给名为total的字段。

等效$group + $project:

maisi_db> db.app_user.aggregate([{$group: {_id: null, total: { $sum: 1 }}}, {$project: {_id: 0, total: 1}}])
[ { total: 2 } ]

$group

按指定的表达式对文档进行分组,并将分组后的文档输出到下一个阶段。

{$group: {_id: [expression], field: {accumulator: expression, ...}, ...}}

  • _id必填,可以赋值null为整个输入文档计算累计值;
  • 剩余计算字段可选,使用accumulator运算符进行计算;
  • _id和accumulator表达式可以接受任意有效表达式。

accumulator

操作符描述
$avg计算均值
$first返回每组第一个文档,组内按照排序规则(未指定则按照存储顺序)排序
$last返回每组最后一个文档
$max返回每组最大值
$min返回每组最小值
$push将表达式值添加到数组
$addToSet将表达式值添加到集合(无重复值,无序)
$sum计算求和
$stDevPop返回输入值总体标准偏差(population standard deviation)
$stDevSamp返回输入值样本标准偏差(the sample standard deviation)
  • 按年、月、日对订单进行分组,并计算每组总价、平均商品数和订单数

    maisi_db> db.app_order.aggregate([{
      $group: {
        _id: {
          year: {$year: "$order_time"},
          month: {$month: "$order_time"},
          day: {$dayOfMonth: "$order_time"}
        },
        total_price: {$sum: {$multiply: ["$price", "$quantity"]}},
        avg_quantity: {$avg: "$quantity"},
        total: {$sum: 1}
      }
    }])
    [
      {
        _id: { year: 2026, month: 5, day: 15 },
        total_price: 2241.6,
        avg_quantity: 4,
        total: 1
      },
      {
        _id: { year: 2026, month: 6, day: 30 },
        total_price: 8960,
        avg_quantity: 8,
        total: 1
      }
    ]
  • 计算所有订单的总价、平均商品数和订单数

    maisi_db> db.app_order.aggregate([{
      $group: {
        _id: null,
        total_price: {$sum: {$multiply: ["$price", "$quantity"]}},
        avg_quantity: {$avg: "$quantity"},
        total: {$sum: 1}
      }
    }])
    [
      { _id: null, total_price: 144997.95, avg_quantity: 4.35, total: 40 }
    ]
  • 获取所有订单日期

    maisi_db> db.app_order.aggregate([{
      $group: {
        _id: {
          $dateToString: {
            format: "%Y-%m-%d",
            date: "$order_time",
            timezone: "+08:00"
          }
        }
      }
    }])
    [
      { _id: '2026-05-11' }, { _id: '2026-07-08' }
    ]
  • 按日期分组统计订单总数并返回订单总数大于10的分组

    mais_db> db.app_order.aggregate([{
      $group: {
        _id: {
          order_date: {
            year: { $year: "$order_time" },
            month: { $month: "$order_time" },
            day: { $dayOfMonth: "$order_time" }
          }
        },
        total_quantity: { $sum: "$quantity" }
      }
    }, {
      $match: {
        total_quantity: { $gt: 10 }
      }
    }])
    [
      {
        _id: { order_date: { year: 2026, month: 6, day: 18 } },
        total_quantity: 12
      }
    ]
  • 将6月份的订单按日期分组统计分组数

    maisi_db> db.app_order.aggregate([{
      $match: {
        order_time: {
          $gte: ISODate("2026-05-31T16:00:00Z"),
          $lt: ISODate("2026-06-30T16:00:00Z")
        }
      }
    }, {
      $group: {
        _id: {
          order_date: {
            year: { $year: "$order_time" },
            month: { $month: "$order_time" },
            day: { $dayOfMonth: "$order_time" }
          }
        }
      }
    }, {
      $group: {
        _id: null,
        count: { $sum: 1 }
      }
    }])
    [ { _id: null, count: 15 } ]
  • 按用户性别分组,并在每组显示用户名列表和用户列表

    maisi_db> db.app_user.aggregate([{
      $group: {
        _id: "$gender",
        usernames: {
          $push: "$username"
        },
        users: {
          $push: "$$ROOT"
        }
      }
    }])
    [
      {
        _id: 2,
        usernames: [ 'Cindy', 'Grace', 'Helen' ],
        users: [
          {
            _id: ObjectId('6a63d5476077589d45abd7fd'),
            username: 'Cindy',
            gender: 2,
            age: 13
          },
          {
            _id: ObjectId('6a63d5476077589d45abd801'),
            username: 'Grace',
            gender: 2,
            age: 9
          },
          {
            _id: ObjectId('6a63d5476077589d45abd802'),
            username: 'Helen',
            gender: 2,
            age: 14
          }
        ]
      },
      {
        _id: 1,
        usernames: [ 'Alice', 'Bob', 'Dale', 'Eric', 'Frank' ],
        users: [
          {
            _id: ObjectId('6a63d5476077589d45abd7fb'),
            username: 'Alice',
            gender: 1,
            age: 16
          },
          {
            _id: ObjectId('6a63d5476077589d45abd7fc'),
            username: 'Bob',
            gender: 1,
            age: 11
          },
          {
            _id: ObjectId('6a63d5476077589d45abd7fe'),
            username: 'Dale',
            gender: 1,
            age: 10
          },
          {
            _id: ObjectId('6a63d5476077589d45abd7ff'),
            username: 'Eric',
            gender: 1,
            age: 8
          },
          {
            _id: ObjectId('6a63d5476077589d45abd800'),
            username: 'Frank',
            gender: 1,
            age: 18
          }
        ]
      }
    ]
  • 按用户性别分组,获取每组最大年龄和最小年龄

    maisi_db> db.app_user.aggregate([{
      $group: {
        _id: "$gender",
        min_age: { $min: "$age" },
        max_age: { $max: "$age" }
      }
    }])
    [
      { _id: 1, min_age: 8, max_age: 18 },
      { _id: 2, min_age: 9, max_age: 14 }
    ]
  • 按用户性别分组,或每组第一个和最后一个用户

    maisi_db> db.app_user.aggregate([{
      $group: {
        _id: "$gender",
        first: { $first: "$username" },
        last: { $last: "$username" }
      }
    }])
    [
      { _id: 1, first: 'Alice', last: 'Frank' },
      { _id: 2, first: 'Cindy', last: 'Helen' }
    ]

$match

过滤文档,仅将符合条件的文档传递到新一个阶段。

$match查询语法与find()查询语法相同。

  • 简单匹配查询

    maisi_db> db.app_user.aggregate([{
      $match: {
        username: "Alice"
      }
    }])
    [
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        username: 'Alice',
        gender: 1,
        age: 16
      }
    ]
  • 将年龄10-15的用户按性别统计人数

    maisi_db> db.app_user.aggregate([{
      $match: {
        age: { $gte: 10, $lte: 15 }
      }
    }, {
      $group: {
        _id: "$gender",
        count: { $sum: 1 }
      }
    }])
    [ { _id: 1, count: 2 }, { _id: 2, count: 2 } ]

$unwind

将文档数组拆分成单独的文档。

示例文档:

[
  {
    _id: ObjectId('6a63d5476077589d45abd7fb'),
    username: 'Alice',
    gender: 1,
    age: 16,
    skill: [ '电脑', '游戏' ]
  }
]
  • 使用$unwind将skill数组中每个元素单独输出文档

    maisi_db> db.app_user.aggregate([{
      $match: { username: "Alice" }
    }, {
      $unwind: "$skill"
    }])
    [
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        username: 'Alice',
        gender: 1,
        age: 16,
        skill: '电脑'
      },
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        username: 'Alice',
        gender: 1,
        age: 16,
        skill: '游戏'
      }
    ]
  • 显示数组元素的索引

    maisi_db> db.app_user.aggregate([{
      $match: { username: "Alice" }
    }, {
      $unwind: {
        path: "$skill",
        includeArrayIndex: "index"
      }
    }])
    [
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        username: 'Alice',
        gender: 1,
        age: 16,
        skill: '电脑',
        index: Long('0')
      },
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        username: 'Alice',
        gender: 1,
        age: 16,
        skill: '游戏',
        index: Long('1')
      }
    ]

    默认情况下只输出skill有值的文档,如果需要输出不存在skill字段、skill值为null或空数组的文档,可以设置preserveNullAndEmptyArrays为true。

    maisi_db> db.app_user.aggregate([{
      $unwind: {
        path: "$skill",
        preserveNullAndEmptyArrays: true
      }
    }])
    [
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        username: 'Alice',
        gender: 1,
        age: 16,
        skill: '电脑',
        index: Long('0')
      },
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        username: 'Alice',
        gender: 1,
        age: 16,
        skill: '游戏',
        index: Long('1')
      },
      {
        _id: ObjectId('6a63d5476077589d45abd7fc'),
        username: 'Bob',
        gender: 1,
        age: 11
      }
    ]

$project

选择字段、重命名字段和派生字段。

  • 输出文档只包含username

    maisi_db> db.app_user.aggregate([{
      $project: {
        username: 1
      }
    }])
    [
      { _id: ObjectId('6a63d5476077589d45abd7fb'), username: 'Alice' },
      { _id: ObjectId('6a63d5476077589d45abd7fc'), username: 'Bob' },
      { _id: ObjectId('6a63d5476077589d45abd7fd'), username: 'Cindy' },
      { _id: ObjectId('6a63d5476077589d45abd7fe'), username: 'Dale' },
      { _id: ObjectId('6a63d5476077589d45abd7ff'), username: 'Eric' },
      { _id: ObjectId('6a63d5476077589d45abd800'), username: 'Frank' },
      { _id: ObjectId('6a63d5476077589d45abd801'), username: 'Grace' },
      { _id: ObjectId('6a63d5476077589d45abd802'), username: 'Helen' }
    ]

    默认会包含_id字段,设置_id为0以排除_id字段:

    maisi_db> db.app_user.aggregate([{
      $project: {
        _id: 0,
        username: 1
      }
    }])
    [
      { username: 'Alice' },
      { username: 'Bob' },
      { username: 'Cindy' },
      { username: 'Dale' },
      { username: 'Eric' },
      { username: 'Frank' },
      { username: 'Grace' },
      { username: 'Helen' }
    ]
  • 输出排除age字段

    maisi_db> db.app_user.aggregate([{
      $project: {
        age: 0
      }
    }])
    [
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        username: 'Alice',
        gender: 1,
        skill: [ '电脑', '游戏' ]
      },
      {
        _id: ObjectId('6a63d5476077589d45abd7fc'),
        username: 'Bob',
        gender: 1
      }
    ]
  • 排除嵌套文档字段

    maisi_db> db.app_user.aggregate([{
      $project: {
        "region.province": 0
      }
    }])
    [
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        username: 'Alice',
        gender: 1,
        age: 16,
        skill: [ '电脑', '游戏' ],
        region: { city: '南京市' }
      }
    ]
    maisi_db> db.app_user.aggregate([{
      $project: {
        region: {
          province: 0
        }
      }
    }])

    3.6+可以使用REMOVE进行条件移除

    maisi_db> db.app_user.aggregate([{
      $project: {
        "region.city": {
          $cond: {
            if: { $eq: ["南京市", "$region.city"] },
            then: "$$REMOVE",
            else: "$$ROOT"
          }
        }
      }
    }])
  • 返回计算后字段

    maisi_db> db.app_user.aggregate([{
      $project: {
        province: { $substr: [ "$id_card_no", 0, 2] },
        city: { $substr: [ "$id_card_no", 2, 2] },
        district: { $substr: [ "$id_card_no", 4, 2] },
        birthday: { $substr: [ "$id_card_no", 6, 8] },
      }
    }])
    [
      {
        _id: ObjectId('6a63d5476077589d45abd7fb'),
        province: '13',
        city: '23',
        district: '30',
        birthday: '19800101'
      }
    ]

$limit

限制传递到下一个阶段的文档数

maisi_db> db.app_user.aggregate([{
  $limit: 2
}])
[
  {
    _id: ObjectId('6a63d5476077589d45abd7fb'),
    username: 'Alice',
    gender: 1,
    age: 16,
    skill: [ '电脑', '游戏' ],
    region: { province: '江苏省', city: '南京市' },
    id_card_no: '132330198001011211'
  },
  {
    _id: ObjectId('6a63d5476077589d45abd7fc'),
    username: 'Bob',
    gender: 1,
    age: 11
  }
]

$skip

跳过指定数量的文档,将剩余文档传递给下一个阶段

maisi_db> db.app_user.aggregate([{
  $skip: 1
}, {
  $limit: 2
}])
[
  {
    _id: ObjectId('6a63d5476077589d45abd7fc'),
    username: 'Bob',
    gender: 1,
    age: 11
  },
  {
    _id: ObjectId('6a63d5476077589d45abd7fd'),
    username: 'Cindy',
    gender: 2,
    age: 13
  }
]

$sort

对输入文档进行排序(1升序,-1降序),将排序后的结果传递个下一个阶段

maisi_db> db.app_user.aggregate([{
  $sort: { age: 1 }
}, {
  $skip: 1
}, {
  $limit: 2
}])
[
  {
    _id: ObjectId('6a63d5476077589d45abd801'),
    username: 'Grace',
    gender: 2,
    age: 9
  },
  {
    _id: ObjectId('6a63d5476077589d45abd7fe'),
    username: 'Dale',
    gender: 1,
    age: 10
  }
]

$sortByCount

根据表达式的值对文档进行分组,统计各组文档数量,根据文档数量降序排列,输出_id和count值。

maisi_db> db.app_user.aggregate([{
  $sortByCount: "$gender"
}])
[ { _id: 1, count: 5 }, { _id: 2, count: 3 } ]

$lookup

maisi_db> db.app_user.aggregate([{
  $lookup: {
    from: "app_profile",
    localField: "_id",
    foreignField: "user_id",
    as: "profile"
  }
}])
[
  {
    _id: ObjectId('6a63d5476077589d45abd7fb'),
    username: 'Alice',
    gender: 1,
    age: 16,
    skill: [ '电脑', '游戏' ],
    region: { province: '江苏省', city: '南京市' },
    id_card_no: '132330198001011211',
    profile: [
      {
        _id: ObjectId('6a63fd462ba0b4fd98b28930'),
        user_id: ObjectId('6a63d5476077589d45abd7fb'),
        avatar: 'https://i0.hdslb.com/bfs/face/94f8acdde8d111f6d9cc6fe9d450a7c90cd337b8.jpg'
      }
    ]
  },
  {
    _id: ObjectId('6a63d5476077589d45abd7fc'),
    username: 'Bob',
    gender: 1,
    age: 11,
    profile: []
  }
]
  • localField:主表(app_user)中的字段;
  • foreignField:关联表(app_profile)中的字段。

安全

角色

角色类型角色描述
数据库一般角色
Database User Roles
read可以读取所有非系统集合和部分系统集合(system.indexes,system.js和system.namespaces)。
允许调用:collStats、dbHash、dbStats、find、killCursors、listIndexes和listCollections。
readWrite可以读取所有集合,写入所有非系统集合和写入部分系统集合(system.js)。
允许调用:collStats、convertToCapped、createCollection、dbHash、dbStats、dropCollection、createIndex、dropIndex、find、emptycapped、insert、killCurrsors、listIndexes、listCollections、remove、renameCollectionSameDB和update。
数据库管理角色
Database Administration Roles
dbAdmin可以在当前数据库上执行管理函数或部分系统集合上执行部分函数。
允许在system.indexes, system.namespaces, system.profile调用:collStats、dbHash、dbStats、find、killCursors、listIndexes、listCollections。
system.profile还允许调用:dropCollection和createCollection。
在非系统集合允许调用:collMod、collStats、compact、convertToCapped、createCollection、createIndex、dbStats、dropCollection、dropDatabase、dropIndex、enableProfiler、indexStats、reIndex、renameCollectionSameDB、repairDatabase、storageDetails和validate。
userAdmin提供在当前数据库上创建和修改用户和角色的能力。拥有该角色的用户可以给其他用户授予任何权限(包括userAdmin角色)。
允许调用:changeCustomData、changePassword、createRole、createUser、dropRole、dropUser、grantRole、revokeRole、viewRole和viewUser。
dbOwner提供对数据库执行任何管理操作的能力。此角色集合了readWrite,dbAdmin和userAdmin角色授予的权限。
全数据库角色
All-Database Roles
readAnyDatabase在集群的所有数据库上(除local和config数据库)提供与read角色相同的权限,同时提供listDatabases命令权限。
readWriteAnyDatabase在集群的所有数据库上提供与readWrite角色相同的权限,同时提供listDatabases命令权限。
userAdminAnyDatabase在集群中的所有数据库上提供和userAdmin角色相同的权限,此外还提供authSchemaUpgrade、invalidateUserCache和listDatabases权限。此角色还可以在admin数据库的system.users和system.roles执行:collStats、dbHash、dbStats、find、killCursors、planCacheRead、createIndex和dropIndex。
dbAdminAnyDatabase在集群的所有数据库上提供与dbAdmin角色相同的权限,同时提供listDatabases命令权限。
集群管理角色
Cluster Administration Roles
clusterManager提供对群集的管理和监视操作。具有此角色的用户可以访问local和config数据库,分别用于分片和复制。
允许调用:addShard、applicationMessage、cleanupOrphaned、flushRouterConfig、listShards、removeShard、replSetConfigure、replSetGetStatus、replSetStateChange、resync、enableSharding、moveChunk、splitChunk和splitVector。
config数据库还允许调用:insert、remove、update、collStats、dbHash、find和killCursors。
clusterMonitor为监控工具(如MongoDB Cloud Manager或Ops Manager监控代理)提供只读访问权限。
允许调用:connPollStats、cursorInfo、getCmdLineOpts、getLog、getParameter、getShardMap、hostInfo、inprog、listDatabases、lisstShards、netstat、replSetGetStatus、serverStatus、shardingState、top、collStats、dbStats和getShardVersion。
hostManager提供监视和管理服务器的能力。
允许调用:applicationMessage、closeAllDatabases、connPoolSync、cpuProfiler、diagLogging、flushRouterConfig、fsync、invalidateUserCache、killop、logRotate、resync、setParameter、showdown、touch和unlock。
clusterAdmin提供群集管理的最高权限。此角色集合了clusterManager、clusterMonitor和hostManager,还拥有dropDatabase操作权限。
备份和恢复
Backup and Restoration Roles
backup提供备份数据所需的权限。此角色提供足够的权限来使用MongoDB Cloud Manager备份代理,Ops Manager备份代理或使用mongodump
restore提供足够的权限来使用mongorestore
超级角色
Superuser Roles
root提供对所有资源的访问权限。集合了readWriteAnyDatabase、dbAdminAnyDatabase、userAdminAnyDatabase、clusterAdmin、backup和restore。
内部角色__system该角色允许用户对所有数据库拥有所有操作权限,不要将该角色授予应用程序用户或其它管理员用户。

查看某个角色在指定数据库上所拥有的详细权限:

admin> db.runCommand({rolesInfo: {role: "dbAdmin", db: "maisi_db"}, showPrivileges: true})

Write Concern

MongoDB Write Concern(MongoDB写入安全机制)是一种客户端设置,用于控制写入安全的级别。Write Concern描述了MongoDB写入到mongod单实例、副本集以及分片集群时何时应答给客户端。默认情况下,mongoDB文档增删改都会一直等待数据库响应(确认写入是否成功),然后才会继续执行。

MongoDB应答机制就是对于当前数据库的写入成功与否告知客户端(db.getLastError())。mongoDB Client发出写入(或更新)请求后,mongoDB Server端开始写入,然后通知客户端是否写入成功。应答机制主要分两种:

  1. 应答式写入,缺省情形,安全写入,适用于数据强一致性场景;
  2. 非应答式写入,非安全写入,适用于数据弱一致性场景。

MongoDB通过Write Concern,客户端调用db.getLastError()方法来获取服务端的返回信息,如果捕获到错误,则可以尝试再次写入或记录到特定日志等。

Write Concern的用法:{ w: <value>, j: <boolean>, wtimeout: <number> }

  • w用以指定写入操作需要确认的节点数量:

    • w: 0(Unacknowledged),非应答式写入,客户端不等待服务器的任何确认。

      20161024172337968.png

    • w: 1(Acknowledged,默认值),应答式写入,要求主节点确认写入。

      20161024172424766.png

      对于使用副本集的场景,默认情况下仅仅从主节点进行应答,建议修改缺省的应答情形为特定数量或者majority来保证数据的可靠。

      20161024173434123.png

    • w: n(大于1的整数),要求主节点以及至少n-1个从节点确认写入。
    • w: "majority",要求写入操作被副本集中的大多节点确认。
    • w: "自定义",要求写入操作被副本集中指定TAG标记的节点确认。
  • j要求确认写入journal日志后应答客户端。

    20161024172513517.png

    注:开启j选项前,必须开启mongodb的日志功能。

  • wtimeout指定时间限制,防止写操作被阻塞导致无法应答给客户端,单位为ms,当w大于1时自动生效。

    仅适用于集群环境,当某个节点写入超出wtimeout后,mongod返回错误。

小结:

  • write concern用于控制写入安全的级别,可以分为应答式写入和非应答式写入;
  • write concern是一个性能和数据强一致性的权衡,应根据业务场景进行设定;
  • 对于强一致性场景,建议w>1或者等于majority,同时j为true,否则w=0;
  • 在副本集的情形下,建议通过配置文件来修改w和wtimeout,以避免由于某个节点挂起导致无法应答。

常见问题

  • Authorization failed

    MongoDB自3.0.3版本之后加入了SCRAM-SHA-1校验方式,之前默认使用MONGODB-CR验证方式。部分客户端端软件在使用用户名和密码连接MongoDB时,默认采用MONGODB-CR校验方式,从而导致授权失败了。

    查看验证方式:

    admin> db.system.version.find()
    { "_id" : "featureCompatibilityVersion", "version" : "4.2" }
    { "_id" : "authSchema", "currentVersion" : 5 }

    authSchema版本为5,即SCRAM-SHA-1校验方式,将authSchema改为3,切换为MONGODB-CR校验方式。

    解决方法(二选一):

    • 升级客户端,使用SCRAM-SHA-1校验方式;
    • 使用低版本MongoDB。

标签: none

添加新评论