# 配置

# 配置文件

  • 旧版 MongoDB 的配置文件采用 ini 格式,从 2.6 版开始推荐采用 YAML 格式,分为 storage、systemLog、net 等多个大类。

# 示例

mongod.conf 配置文件示例:

net:
  port: 27017
  bindIp: 0.0.0.0
  # bindIp: localhost,10.0.0.1,/tmp/mongod.sock   # 可以绑定多个 IP 供客户端访问

# processManagement:
#   fork: false               # 是否作为 daemon 进程运行
#   pidFilePath: /var/run/mongod.pid

security:
  authorization: enabled      # 是否对客户端启用 RBAC 认证,默认为 disabled
  # clusterAuthMode: keyFile  # 集群各节点之间的认证方式

storage:
  dbPath: /var/lib/mongo      # 存储数据的目录。默认在同一个目录下存放所有数据库的各个集合的数据文件 collection-xx.wt 和 index-xx.wt
  # directoryPerDB: false     # 是否在 dbPath 下为每个数据库分别创建一个目录来存放数据文件。在 MongoDB 首次运行之后,不方便修改该配置参数
  # journal:
  #   enabled: true           # 是否启用 WiredTiger 预写日志,从而在 mongo 异常重启时自动恢复数据文件,默认为 true
  # engine: wiredTiger        # 采用的存储引擎
  # wiredTiger:               # wiredTiger 存储引擎的配置
  #   engineConfig:
  #      cacheSizeGB: 2       # 内部缓存的最大体积,单位为 GB 。默认为 (RAM-1GB)*50% ,至少为 0.25GB
  #   collectionConfig:
  #     blockCompressor: snappy # 将集合数据存储到磁盘时的压缩算法

systemLog:                    # 服务器日志
  # verbosity: 0              # 日志级别,取值范围为 0 ~ 5 ,对应 INFO ~ DEBUG
  destination: file           # 日志的输出方向,可以为 file 或 syslog 。默认输出到 stdout
  path: /var/log/mongo/mongod.log
  logAppend: true             # mongo 重启后,是否将日志以追加方式写到之前的日志文件。默认为 false ,会备份之前你的日志文件,并创建新的日志文件

operationProfiling:           # 慢查询日志
  mode: slowOp                # 工作模式,默认为 off
  # slowOpThresholdMs: 100
  # slowOpSampleRate: 1       # 慢查询的采样率,取值范围为 0~1 。对于副本集的从节点,采样率总是 1

# 慢日志

  • 慢日志的几种模式:
    off       # 不记录
    slowOp    # 只记录耗时超过 slowOpThresholdMs 阈值的操作
    all       # 记录所有数据库操作
    
  • 慢日志会记录到两个位置:
    • 当前 db 的系统集合 system.profile 。该集合为 capped 类型,size 为 1024^2 。
    • 日志文件 mongod.log 。
  • 相关命令:
    db.system.profile.find().limit(10).sort({ts:-1}).pretty() // 查看最近的 10 个慢查询
    db.system.profile.find({millis:{$gt:10}}).pretty()        // 查看耗时超过 10 毫秒的慢查询
    db.system.profile.find({ts:{$gt: new ISODate('2022-01-01T00:00:00Z'), $lt: new ISODate('2022-01-02T00:00:00Z')}}).pretty()  // 查看指定时间范围的慢查询
    

# 存储引擎

  • In-Memory
    • 存储在内存中。
    • 该引擎仅企业版可用。
  • MMAPv1
    • MongoDB v3.2 之前的默认引擎。
  • WiredTiger
    • MongoDB v3.2 开始的默认引擎。
    • 与 MMAPv1 相比:
      • MMAPv1 基于 MMAP 技术读写磁盘文件,而 WiredTiger 以普通方式读写磁盘文件。
      • MMAPv1 在磁盘存储数据时不支持压缩,而 WiredTiger 默认会进行压缩,主要分为两种数据:
        • 文档数据通过 snappy 算法压缩之后,存储到磁盘文件 collection-xx.wt 中,从而大幅减少占用的磁盘空间。
        • 索引数据通过前缀压缩之后,存储到磁盘文件 index-xx.wt 中,从而小幅减少占用的磁盘空间。
          前缀压缩是指,在一个 page 中存储多条索引数据时,如果它们索引键的前缀相同,则只存储一份前缀。
      • MMAPv1 数据库锁的最小单位为 collection ,而 WiredTiger 最小锁单位为 document 。
    • 默认基于 B+ tree 存储数据并进行索引。
      • 也可改用 LSM Tree ,增加写入速度,降低读取速度。
    • 从 MongoDB v3.6 开始,WiredTiger 每隔一定时间会将内存中的写操作数据存储到磁盘(而不是每发生一次写操作就立即存储),建立一个快照,称为检查点(checkpoint)。
      • 在两个 checkpoint 之间,采用 journal 预写日志来临时记录数据库的所有写操作,相当于增量备份。
      • 如果 mongod 异常退出,重启时会自动回滚到上一个 checkpoint ,并使用预写日志来恢复数据。
      • 默认每隔 60s ,或累计写入 2G 预写日志时,就创建一个新的 checkpoint ,并删除旧的 checkpoint 、预写日志。
    • 删除集合中的文档时,这部分磁盘空间不会立即释放,而是留给该集合以后写入新文档。
      • 缺点:标记为 deleted 的磁盘空间如果一直未写入新文档,则造成了浪费。而且按块读取文件数据时,deleted 空间会跟正常文档混合在一起被读取,浪费内存空间。
      • 为了清理 deleted 空间,可以删除整个集合,这会删除磁盘中的数据文件 collection-xx.wt 和 index-xx.wt ,立即释放磁盘空间。
      • 为了清理 deleted 空间,可以将原集合 copy 到一个新集合,然后将原集合 drop ,最后将新集合 rename 为原集合。
      • 为了清理 deleted 空间,可以执行 db.runCommand({compact:'<collection>'})
        • 它的原理是将集合的数据拷贝一份,重新写入磁盘,然后重建索引,因此需要事先留出一倍空闲的磁盘空间。
        • 在 MongoDB v4.4 之前,执行 compact 命令时,会阻塞当前数据库的所有 CRUD 操作。从 MongoDB v4.4 开始,只会阻塞当前集合的 drop()、createIndex()、dropIndex() 操作。
        • 在副本集中,Secondary 节点并不会同步该命令,因此需要到每个节点执行该命令。
    • 对比 Mongo 与 ES 的磁盘清理策略:
      • Mongo WiredTiger 删除文档之后,这部分磁盘空间不会立即释放,但随时可用于写入新文档,因此磁盘文件是可变的,不能统计 deleted 文档数。
      • ES 删除文档之后,这部分磁盘空间不会立即释放,而是等到下一次合并磁盘文件时才清理。在合并之前,磁盘文件是不变的,因此可以统计 deleted 文档数。

# 访问控制

# 身份认证

  • MongoDB 默认没有启用身份认证,可按以下步骤启用:
    1. 进入 MongoDB 终端,创建一个管理员用户:
      use admin
      db.createUser(
        {
          user: 'root',
          pwd: '******',
          roles: [{role: 'root', db: 'admin'}]
        }
      )
      
    2. 退出 MongoDB 终端,执行 mongod --auth 重启 MongoDB 服务器,启用身份认证。
  • 如果已启用身份认证,但是不知道管理员账号,则可以先用 mongod ---noauth 重启服务器,再创建管理员账号。
  • 如果已启用身份认证,则启动客户端时,需要传入用户名、密码,并指定用于验证该用户的数据库。例如 mongo mongodb://127.0.0.1:27017?authSource=admin -u root -p ******
    • 默认采用当前连接的数据库作为验证数据库,检查该用户在该数据库中是否有效。如果不存在该用户名,或密码错误,则报错:Authentication failed
    • 可以先启动客户端,再进行密码认证:
      mongo 127.0.0.1:27017/admin
      db.auth('root', '******')
      

# 管理用户

  • MongoDB 支持启用基于角色的访问控制(RBAC)。
    • 在 admin 数据库的 system.users 集合中存储了所有用户的信息,包括用户名、密钥、所属数据库、分配的角色。
    • 每个用户登录时,除了输入密码,还需要指定所属的数据库进行身份认证。
    • root 用户登录时,只能指定 admin 数据库进行身份认证。
  • 普通数据库定义了以下角色:
    • read :可以读取数据库
    • readWrite :可以读写数据库
    • dbAdmin :可以管理数据库
    • userAdmin :可以管理用户
  • admin 数据库定义了以下角色:
    • readAnyDatabase
    • readWriteAnyDatabase
    • dbAdminAnyDatabase
    • userAdminAnyDatabase
    • clusterAdmin :可以管理副本集
    • root :超级管理员,拥有所有权限
  • 例:
    // 显示当前数据库的所有用户
    show users
    // 创建一个用户
    db.createUser({user: 'user1', pwd: '******', roles: [{role: 'readWrite', db: 'db1'}]})
    // 删除用户
    db.dropUser('user1')
    // 给用户分配权限
    db.grantRolesToUser('user1', [{role: 'readWrite', db: 'db1'}, [{role: 'read', db: 'db2'}])
    // 删除用户的权限
    db.revokeRolesFromUser('user1', [{role: 'readWrite', db: 'db1'}])