Office KMS激活

如果当前Office为零售版,先操作1-3步将零售版转为VOL版。

  1. 重置试用期激活状态,重置为30天试用期(允许重置3次)

    C:\Users\Administrator>cd %ProgramFiles%\Microsoft Office\Office16
    C:\Program Files\Microsoft Office\Office16>cscript ospp.vbs /rearm
  2. 安装KMS许可证

    C:\Program Files\Microsoft Office\Office16>cscript ospp.vbs /inslic:"..\root\Licenses16\ProPlusVL_KMS_Client-ppd.xrm-ms"
    C:\Program Files\Microsoft Office\Office16>cscript ospp.vbs /inslic:"..\root\Licenses16\ProPlusVL_KMS_Client-ul.xrm-ms"
    C:\Program Files\Microsoft Office\Office16>cscript ospp.vbs /inslic:"..\root\Licenses16\ProPlusVL_KMS_Client-ul-oob.xrm-ms"
  3. 安装KMS密钥

    C:\Program Files\Microsoft Office\Office16>cscript ospp.vbs /inpkey:XQNVK-8JYDB-WJ9W3-YJ8YR-WFG99

    卸载密钥

    C:\Program Files\Microsoft Office\Office16>cscript ospp.vbs /dstatus
    C:\Program Files\Microsoft Office\Office16>cscript ospp.vbs /unpkey:WFG99
  4. 在线激活

    C:\Program Files\Microsoft Office\Office16>cscript /nologo ospp.vbs /sethst:kms.wiseidc.com
    C:\Program Files\Microsoft Office\Office16>cscript /nologo ospp.vbs /act

Windows KMS激活

如果当前操作系统为零售版,先操作1-3步将零售版转为VOL版。

  1. 清除激活状态

    C:\Users\Administrator>cd /D "%SystemRoot%\System32"
    C:\Windows\System32>slmgr /upk
  2. 重置激活

    C:\Windows\System32>cscript slmgr.vbs /rearm
  3. 安装KMS密钥

    C:\Windows\System32>cscript slmgr.vbs /ipk W269N-WFGWX-YVC9B-4J6C9-T83GX
  4. 在线激活

    C:\Windows\System32>cscript slmgr.vbs /skms kms.wiseidc.com
    C:\Windows\System32>cscript slmgr.vbs /ato
  5. 查看过期日期

    C:\Windows\System32>cscript slmgr.vbs /xpr
    Microsoft (R) Windows Script Host Version 5.812
    版权所有(C) Microsoft Corporation。保留所有权利。
    
    Windows(R), EnterpriseS edition:
        批量激活将于 2026/12/25 11:38:08 过期
  6. 查看激活信息

    C:\Windows\System32>cscript slmgr.vbs /dlv
    Microsoft (R) Windows Script Host Version 5.812
    版权所有(C) Microsoft Corporation。保留所有权利。
    
    软件授权服务版本: 10.0.17763.9020
    
    名称: Windows(R), EnterpriseS edition
    描述: Windows(R) Operating System, VOLUME_KMSCLIENT channel
    激活 ID: 32d2fab3-e4a8-42c2-923b-4bf4fd13e6ee
    应用程序 ID: 55c92734-d682-4d71-983e-d6ec3f16059f
    扩展 PID: 03612-04250-000-000002-03-2052-17763.0000-0212026
    产品密钥通道: Volume:GVLK
    安装 ID: 286926084471821607432456549385465164969976541201630300527855602
    部分产品密钥: J462D
    许可证状态: 已授权
    批量激活过期: 217072 分钟(151 天)
    剩余 Windows 重置计数: 1001
    剩余 SKU 重置计数: 1001
    信任时间: 2026/7/27 17:46:06
    已配置的激活类型: 全部
    
    最新激活信息:
    密钥管理服务客户端信息
            客户端计算机 ID (CMID):7071b918-1339-4bfa-a42f-d85e98a44057
            已注册的 KMS 计算机名称: kms.o88.co:1688
            KMS 计算机 IP 地址: 47.101.38.23
            KMS 计算机扩展的 PID: 05426-00206-562-489683-03-1043-9200.0000-2732025
            激活时间间隔: 120 分钟
            续订间隔: 10080 分钟
            已启用 KMS 主机缓存

GVLK(Generic Volume License Key)

Windows VOL/LTSC

桌面操作系统KMS密钥
Windows 7 ProfessionalFJ82H-XT6CR-J8D7P-XQJJ2-GPDD4
Windows 7 Professional NMRPKT-YTG23-K7D7T-X2JMM-QY7MG
Windows 7 Professional EW82YF-2Q76Y-63HXB-FGJG9-GF7QX
Windows 7 Enterprise33PXH-7Y6KF-2VJC9-XBBR8-HVTHH
Windows 7 Enterprise NYDRBP-3D83W-TY26F-D46B2-XCKRJ
Windows 7 Enterprise EC29WB-22CC8-VJ326-GHFJW-H9DH4
Windows 11 Professional
Windows 10 Professional
W269N-WFGWX-YVC9B-4J6C9-T83GX
Windows 11 Professional N
Windows 10 Professional N
MH37W-N47XK-V7XM9-C7227-GCQG9
Windows 11 Professional Workstation
Windows 10 Professional Workstation
NRG8B-VKK3Q-CXVCJ-9G2XF-6Q84J
Windows 10 RTM Professional Workstation N
Windows 11 Professional Workstation N
Windows 10 Professional Workstation N
9FNHH-K3HBT-3W4TD-6383H-6XYWF
Windows 10 RTM Professional Education
Windows 11 Professional Education
Windows 10 Professional Education
6TP4R-GNPTD-KYYHQ-7B7DP-J447Y
Windows 10 RTM Professional Education N
Windows 11 Professional Education N
Windows 10 Professional Education N
YVWGF-BXNMC-HTQYQ-CPQ99-66QFC
Windows 10 RTM Education
Windows 11 Education
Windows 10 Education
NW6C2-QMPVW-D7KKK-3GKT6-VCFB2
Windows 10 RTM Education N
Windows 11 Education N
Windows 10 Education N
2WH4N-8QGBV-H22JP-CT43Q-MDWWJ
Windows 10 RTM Enterprise
Windows 11 Enterprise
Windows 10 Enterprise
NPPR9-FWDCX-D2C8J-H872K-2YT43
Windows 10 RTM Enterprise N
Windows 11 Enterprise N
Windows 10 Enterprise N
DPH2V-TTNVB-4X9Q3-TJR4H-KHJW4
Windows 10 RTM Enterprise G
Windows 11 Enterprise G
Windows 10 Enterprise G
YYVX9-NTFWV-6MDM3-9PT4T-4M68B
Windows 10 RTM Enterprise G N
Windows 11 Enterprise G N
Windows 10 Enterprise G N
44RPN-FTY23-9VTTB-MP9BX-T84FV
Windows 10 Enterprise S
Windows 11 Enterprise LTSC 2024
Windows 10 Enterprise LTSC 2021
Windows 10 Enterprise LTSC 2019
M7XTQ-FN8P6-TTKYV-9D4CC-J462D
Windows 10 Enterprise S N
Windows 11 Enterprise N LTSC 2024
Windows 10 Enterprise N LTSC 2021
Windows 10 Enterprise N LTSC 2019
92NFX-8DJQP-P6BBQ-THF9C-7CG2H
Windows 10 RTM IoT Enterprise S
Windows IoT Enterprise LTSC 2024
Windows IoT Enterprise LTSC 2021
KBN8V-HFGQ4-MGXVD-347P6-PDQGT
Windows 10 RTM Enterprise S
Windows 10 Enterprise LTSB 2016
DCPHK-NFMTC-H88MJ-PFHPY-QJ4BJ
Windows 10 RTM Enterprise S N
Windows 10 Enterprise N LTSB 2016
QFFDN-GRT3P-VKWWX-X7T3R-8B639
Windows 10 RTM Enterprise S
Windows 10 Enterprise LTSB 2015
WNMTR-4C88C-JK8YV-HQ7T2-76DF9
Windows 10 RTM Enterprise S N
Windows 10 Enterprise N LTSB 2015
2F77B-TNFGY-69QQF-B8YKP-D69TJ

Windows Server GVLK

服务器操作系统KMS密钥
Windows Server 2016 RTM ServerStandard
Windows Server 2016 Standard
WC2BQ-8NRM3-FDDYY-2BFGV-KHKQY
Windows Server 2019 RTM ServerStandard
Windows Server 2019 Standard
N69G4-B89J2-4G8F4-WWYCC-J464C
Windows Server 2021 RTM ServerStandard
Windows Server 2021 Standard
VDYBN-27WPP-V4HQT-9VMD4-VMK7H
Windows Server 2025 RTM ServerStandard
Windows Server 2025 Standard
TVRH6-WHNXV-R9WG3-9XRFY-MY832
Windows Server 2012 R2 RTM ServerStandard
Windows Server 2012 R2 Standard
D2N9P-3P6X9-2R39C-7RTCD-MDVJX
Windows Server 2012 RTM ServerStandard
Windows Server 2012 Standard
XC9B7-NBPP2-83J2H-RHMBY-92BT4
Windows Server 2008 R2 StandardYC6KT-GKW9T-YTKYR-T4X34-R7VHC
Windows Server 2008 R2 Enterprise489J6-VHDMP-X63PK-3K798-CPX3Y
WWindows Server 2008 StandardTM24T-X9RMF-VWXK6-X8JC9-BFGM2
Windows Server 2008 EnterpriseYQGMW-MPWTJ-34KDK-48M3W-X4Q6V

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。

更换yum源

使用阿里云yum源

x86_64 Base

[root@ecs ~]# curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

x86_64 epel

[root@ecs ~]# curl -o /etc/yum.repos.d/epel-7.repo https://mirrors.aliyun.com/repo/epel-7.repo

注:不推荐使用yum install -y epel-release。epel-release使用https://dl.fedoraproject.org/pub/的软件源,速度较慢。

armv7 Base

[root@ecs ~]# curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-altarch-7.repo

armv7 epel

[root@ecs ~]# cat > /etc/yum.repos.d/epel.repo << EOF
[epel]
name=Epel rebuild for armhfp
baseurl=http://armv7.dev.wiseidc.com/repodir/epel-pass-1/
enabled=1
gpgcheck=0
EOF

清空缓存并重新生成缓存

[root@ecs ~]# yum clean all && yum makecache

自建yum源

搭建yum服务器

[root@ecs ~]# yum install -y nginx
[root@ecs ~]# systemctl enable --now nginx
[root@ecs ~]# ln -s /data/sdb1/mirrors /usr/local/nginx/html/mirrors
[root@ecs ~]# ln -s /data/sdb1/mirrors/epel-armv7 /usr/local/nginx/html/repodir/epel-pass-1

应用yum源

[root@ecs ~]# curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
[root@ecs ~]# sed -i s#http://mirrors.aliyun.com#http://mirrors.wiseidc.com#g /etc/yum.repos.d/CentOS-Base.repo

NTFS操作

挂载分区

[root@ecs ~]# yum install -y ntfs-3g
[root@ecs ~]# mkdir -p /data/sdb1
[root@ecs ~]# mount -t ntfs-3g /dev/sdb1 /data/sdb1

注:挂载还可以使用ntfs-3g /dev/sdb1 /data/sdb1

格式化分区

[root@ecs ~]# yum install -y ntfsprogs
[root@ecs ~]# fdisk /dev/sdb
[root@ecs ~]# mkfs.ntfs /dev/sdb1 -f

注:如果硬盘尚未分区,先使用fdisk /dev/sdb进行分区。(n创建新分区,p创建主分区,由于只创建一个分区,因此起始扇区和结束扇区保持默认,w写入分区表)。

修复分区

有时挂载一个ntfs分区时会报错:

Metadata kept in Windows cache, refused to mount.
Falling back to read-only mount because the NTFS partition is in an unsafe state. Please resume and shutdown Windows fully (no hibernation or fast restarting.)

使用ntfsfix /dev/sdb1进行修复。

安装桌面环境

安装依赖

[root@ecs ~]# yum check-update
[root@ecs ~]# yum -y groupinstall "X Window System"

安装gnome桌面

[root@ecs ~]# yum -y install gnome-classic-session gnome-terminal nautilus-open-terminal liberation-mono-fonts

注:

  • gnome-classic-session:桌面会话
  • gnome-terminal:终端工具
  • nautilus-open-terminal:右键打开终端
  • liberation-mono-fonts:终端字体

或者直接安装GNOME Desktop:

[root@ecs ~]# yum groupinstall 'GNOME Desktop' -y

设置运行级别

[root@ecs ~]# unlink /etc/systemd/system/default.target
[root@ecs ~]# ln -sf /lib/systemd/system/graphical.target /etc/systemd/system/default.target
[root@ecs ~]# reboot

还原为多用户终端方式登录

[root@ecs ~]# unlink /etc/systemd/system/default.target
[root@ecs ~]# ln -sf /lib/systemd/system/multi-user.target /etc/systemd/system/default.target

终端使用文件管理器打开当前目录

[root@ecs ~]# nautilus .

安装RDP

  1. 关闭SELinux

    临时关闭

    [root@ecs ~]# setenforce 0
    [root@ecs ~]# getenforce

    系统会进入宽容模式(Permissive),此时SELinux仅记录违规行为,而不强制拦截操作,重启后恢复默认配置(Enforcing)。

    永久关闭

    [root@ecs ~]# sed -i "/^SELINUX=enforcing/cSELINUX=disabled" /etc/selinux/config
  2. 安装xrdp

    [root@ecs ~]# yum install -y xrdp
  3. 配置防火墙

    [root@ecs ~]# firewall-cmd --permanent --zone=public --add-port=3389/tcp
    [root@ecs ~]# firewall-cmd --reload
  4. 启动xrdp并设置自启动

    [root@ecs ~]# systemctl enable --now xrdp

中文支持

  1. 安装语言包

    [root@ecs ~]# yum -y install kde-l10n-Chinese
    [root@ecs ~]# yum -y install google-noto-sans-traditional-chinese-fonts google-noto-sans-simplified-chinese-fonts
    [root@ecs ~]# yum -y install chkfontpath-2.0.3-alt1.x86_64.rpm fonts-chinese-3.02-12.el5.noarch.rpm

    注:fonts-ISO8859-2-75dpi-1.0-17.1.noarch.rpm包含一套75dpi的中欧字体。

    或者:yum -y groupinstall fonts

  2. 查看是否支持中文

    [root@ecs ~]# locale -a | grep zh
  3. 切换系统语言

    [root@ecs ~]# sed -i "/^LANG=\"en_US.UTF-8\"/cLANG=\"zh_CN.UTF-8\"" /etc/locale.conf
    [root@ecs ~]# reboot

    或者:localectl set-locale LANG=zh_CN.UTF-8

安装常用软件

上传下载

[root@ecs ~]# yum install -y lrzsz
[root@ecs ~]# cd /usr/local/src/ && rz
[root@ecs ~]# sz output.jpg
  • rz上传文件,需要再服务器端先切换到上传目录。
  • sz下载文件,SecureCRT会自动下载到~/Documents(修改方式:在服务器连接上右击->Properties->Terminal->X/Y/Zmodem->Directories->修改Download目录即可)。

chrome

[root@ecs ~]# yum localinstall google-chrome-stable_current_x86_64.rpm
[root@ecs ~]# cp google-chrome.desktop ~/Desktop
[root@ecs ~]# chmod a+x ~/Desktop/google-chrome.desktop
[root@ecs ~]# sed -i '/Exec=/s/$/ --no-sandbox/g' ~/Desktop/google-chrome.desktop

Firefox

[root@ecs ~]# yum install -y firefox

向日葵远程

[root@ecs ~]# yum install -y libappindicator-gtk3 libXScrnSaver-devel
[root@ecs ~]# rpm -ivh /usr/local/src/SunloginClient_15.2.0.63064_x86_64.rpm

Visual Studio Code

[root@ecs ~]# cat > /etc/yum.repos.d/vscode.repo <<EOF
[code]
name=Visual Studio Code
baseurl=https://packages.microsoft.com/yumrepos/vscode
enabled=0
gpgcheck=1
gpgkey=https://packages.microsoft.com/keys/microsoft.asc
EOF
[root@ecs ~]# yum install -y xdg-utils
[root@ecs ~]# rpm -ivh code-1.74.3-1673284922.el7.x86_64.rpm
[root@ecs ~]# echo $'alias code=\'code --no-sandbox --user-data-dir="/root/.vscode"\'' >> ~/.bashrc
[root@ecs ~]# source ~/.bashrc
[root@ecs ~]# code

或者:sh -c 'echo -e "[code]\n" > /etc/yum.repos.d/vscode.repo'

Code::Blocks

安装软件

  1. 安装依赖

    [root@ecs ~]# yum install -y wxGTK xterm

    注:xterm也可以不安装,可以使用gnome-terminal。

    查找依赖:

    [root@ecs ~]# repoquery --nvr --whatprovides libwx_gtk2u*
    compat-wxGTK3-gtk2-gl-3.0.2-32.5.el7
    wxGTK-gl-2.8.12-20.el7
    wxGTK-media-2.8.12-20.el7
    compat-wxGTK3-gtk2-media-3.0.2-32.5.el7
    wxGTK-2.8.12-20.el7
    compat-wxGTK3-gtk2-3.0.2-32.5.el7
  2. 安装codeblocks

    [root@ecs ~]# rpm -ivh codeblocks-libs-20.03-1.el7.x86_64.rpm
    [root@ecs ~]# rpm -ivh codeblocks-20.03-1.el7.x86_64.rpm
  3. 安装c++编译器

    [root@ecs ~]# yum install -y gcc-c++

软件设置

  • 修改终端

    Code::blocks默认的运行终端为XTerm,当C++程序中输出中文时,XTerm显示为乱码,需要将运行终端修改为gnome-terminal。

    打开Code::blocks ,依次点击:Settings->Environment,打开Environment Settings对话框,切换到General settings

    Terminal to launch console programs:的值由xterm -T $TITLE -e改为:gnome-terminal -t $TITLE -x

VLC

  1. 安装rpm fusion软件源仓库

    [root@ecs ~]# yum install -y rpmfusion-free-release-7.noarch.rpm
  2. 安装vlc

    [root@ecs ~]# yum install -y vlc
  3. 修改运行用户

    默认情况下以root用户方式运行vlc会报错:

    VLC is not supposed to be run as root. Sorry.
    If you need to use real-time priorities and/or privileged TCP ports
    you can use vlc-wrapper (make sure it is Set-UID root and
    cannot be run by non-trusted users first).

    解决方法:

    [root@ecs ~]# sed -i 's/geteuid/getppid/' /usr/bin/vlc
  4. 设置声卡自启动

    [root@ecs ~]# gnome-session-properties

    打开Startup Applications Preferences

    点击“Add”,添加自启动程序:

    • Name:pulseaudio
    • Command:pulseaudio --start

安装搜狗输入法

  1. 卸载ibus

    Fcitx(小企鹅输入法)是在X Window中使用的输入法框架,Gnome自带了ibus输入法框架,为了防止与Fcitx冲突需要先卸载ibus。

    [root@ecs ~]# rpm -e --nodeps ibus
  2. 安装fcitx

    [root@ecs ~]# yum -y install fcitx fcitx-configtool
    [root@ecs ~]# yum -y install fcitx-pinyin fcitx-table-chinese
    • fcitx-pinyin:拼音输入法
    • fcitx-table-chinese:五笔输入法
  3. 删除输入源

    应用程序->系统设置->设置->Region & Language:

    除了“英语(美国)”,其它输入源都删除,以防止出现输入乱码。

  4. 设置自启动

    [root@ecs ~]# yum install -y gnome-tweak-tool

    应用程序->附件->优化->开机启动程序,添加Fcitx。

  5. 安装搜狗输入法

    [root@ecs ~]# yum install -y qtwebkit alien
    [root@ecs ~]# alien -r --scripts sogoupinyin_2.2.0.0108_amd64.deb
    [root@ecs ~]# rpm -ivh --force sogoupinyin-2.2.0.0108-2.x86_64.rpm
    [root@ecs ~]# cp /usr/lib/x86_64-linux-gnu/fcitx/fcitx-sogoupinyin.so /usr/lib64/fcitx/
    [root@ecs ~]# cp /usr/lib/x86_64-linux-gnu/fcitx/fcitx-punc-ng.so /usr/lib64/fcitx/
    [root@ecs ~]# cat >> ~/.bashrc << EOF
    export GTK_IM_MODULE=fcitx
    export QT_IM_MODULE=fcitx
    export XMODIFIERS=@im=fcitx
    EOF
    [root@ecs ~]# source ~/.bashrc
    [root@ecs ~]# reboot
    • fcitx-punc-ng.so:中文标点符号
  6. 添加输入法(必须在图形界面打开)

    [root@ecs ~]# fcitx-configtool

    取消勾选“Only Show Current Language”,输入“sogou”搜索搜狗输入法。

创建Dockerfile

FROM alpine:latest AS downloader

ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
ARG VERSION

ENV BUILDX_ARCH="${TARGETOS:-linux}_${TARGETARCH:-amd64}${TARGETVARIANT}"
ENV ND_DATAFOLDER=./data
ENV ND_MUSICFOLDER=./music
ENV ND_ADDRESS=0.0.0.0
ENV ND_PORT=4533

WORKDIR /

RUN echo "https://mirrors.tuna.tsinghua.edu.cn/alpine/latest-stable/main/" > /etc/apk/repositories && \
    echo "https://mirrors.tuna.tsinghua.edu.cn/alpine/latest-stable/community/" >> /etc/apk/repositories

RUN apk add --no-cache ffmpeg

RUN wget https://gh-proxy.org/https://github.com/navidrome/navidrome/releases/download/v${VERSION}/navidrome_${VERSION}_${BUILDX_ARCH} \
    && tar -zxvf navidrome_${VERSION}_${BUILDX_ARCH} -C ./ \
    && chmod +x /navidrome \
    && rm -f navidrome_${VERSION}_${BUILDX_ARCH}

RUN mkdir -p /data /music

VOLUME ["/data", "/music"]

EXPOSE 4533

CMD ["./navidrome"]

编译镜像并创建容器

[root@ecs ~]# docker build . -t navidrome --progress=plain \
--build-arg VERSION=0.61.2 \
--build-arg TARGETVARIANT=.tar.gz
[root@ecs ~]# docker run --name navidrome \
-p 4533:4533 \
--restart=unless-stopped \
-v /data/navidrome/data:/data \
-v /data/music:/music:ro \
--user $(id -u):$(id -g) \
-e ND_LOGLEVEL=info \
-d navidrome

也可使用官方镜像:deluan/navidrome:latest

防火墙开放端口

firewall-cmd --zone=public --add-port=4533/tcp --permanent
firewall-cmd --reload

管理地址:http://192.168.3.103:4533

软件维护

  • 查看数据库路径

    [root@ecs ~]# docker exec navidrome env | grep ND_DATAFOLDER
    ND_DATAFOLDER=./data
    [root@ecs ~]# docker exec navidrome find / -name navidrome.db 2>/dev/null
    /data/navidrome.db
    [root@ecs ~]# docker inspect navidrome --format='{{range .Mounts}}{{printf "%s -> %s\n" .Source .Destination}}{{end}}'
    /data/music -> /music
    /data/navidrome/data -> /data
    
    • 2>/dev/null:将标准错误重定向到/dev/null,>/dev/null 2>&1&>/dev/null:将标准输出和标准错误同时重定向到/dev/null;
      &1表示文件描述符1(标准输出stdout),2>&1表示将文件描述符2(标准错误stderr)重新指向文件描述符1。如果改成2>1则表示将标准错误指向当前目录的文件1。
    • --format='':使用Go语言模板语法来格式化输出;
    • {{range .Mounts}}{{end}}:遍历所有的挂载卷(即docker inspect navidrome数据中Mounts数组)。