参考文献

ObjectId

  • MongoDB中存储的每个文档都必须有一个_id键._id的值可以是任何类型,但其默认为ObjectId.在单个集合中每个文档的_id值都必须是唯一的,以确保集合中每个文档都可以被唯一标记.

  • ObjectId占用了12字节的存储空间,可以用24个十六进制数字组成的字符串来表示:每字节存储两个数字.

  • ObjectId的12字节是按照如下方式生成的:

    1
    2
    0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 
    时间戳 | 随机值 | 计数器(起始值随机)
  • ObjectId的前4字节是从Unix纪元开始以秒为单位的时间戳.

    • 时间戳与接下来的5字节组成,在秒级别的粒度上提供了唯一性
    • 因为时间戳在前,所以ObjectId将大致按照插入的顺序进行排列.
  • ObjectId中接下来的5字节是一个随机值,最后3字节是一个计数器,以一个随机数作为起始值,用来避免在不同机器上生成相互冲突的ObjectId

  • 因此,前9字节保证了ObjectId在1秒内跨机器和进程的唯一性.最后3字节只是递增计数器,负责保证单个进程中1秒内的唯一性.这允许在1秒内为每个进程生成多达2563256^3(16777216)个唯一的ObjectId

修饰符

$set修饰符

  • 用来设置一个字段的值.如果这个字段不存在,则创建该字段.

数组运算符

添加元素$push
  • 如果数组已存在,$push就会将元素添加到数组末尾;如果数组不存在,则会创建一个新的数组.

  • 还可以配合$each一次性添加多个值

    1
    2
    3
    4
    db.test.updateOne(
    { _id: 2 },
    { $push: { tags: { $each: [ "a", "b", "c" ] } } }
    )
  • 可以配合$slice来限制

将数组作为集合使用$addToSet
删除元素$pop/$pull
基于位置的数组更改$
1
"tags.$.name"
使用数组过滤器进行更新
1
2
3
4
5
6
7
db.test.updateOne(
{ _id: 2 },
{ $set: { "tags.$[elem].name: "mongo" } },
{
arrayFilters:["elem.enabled": true]
}
)

比较顺序

  • 如果对混合类型的键进行排序,会有一个预定义的排序.从最小值到最大值.
1
2
3
4
5
6
7
8
9
10
11
12
13
最小值
null
数字(整型,长整型,双精度浮点型,小数型)
字符串
对象/文档
数组
二进制数据
对象ID
布尔型
日期
时间戳
正则表达式
最大值

MongoDB默认文件或目录

  • 二进制可执行脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    root@xxxxn:/usr/bin# ll|grep mongo
    -rwxr-xr-x 1 root root 27637096 Apr 14 2020 mongo*
    -rwxr-xr-x 1 root root 50315656 Apr 14 2020 mongod*
    -rwxr-xr-x 1 root root 8794584 Apr 10 2018 mongodump*
    -rwxr-xr-x 1 root root 6980920 Apr 10 2018 mongoexport*
    -rwxr-xr-x 1 root root 6915352 Apr 10 2018 mongofiles*
    -rwxr-xr-x 1 root root 7060344 Apr 10 2018 mongoimport*
    -rwxr-xr-x 1 root root 49877352 Apr 14 2020 mongoperf*
    -rwxr-xr-x 1 root root 8958776 Apr 10 2018 mongoreplay*
    -rwxr-xr-x 1 root root 9864792 Apr 10 2018 mongorestore*
    -rwxr-xr-x 1 root root 27392584 Apr 14 2020 mongos*
    -rwxr-xr-x 1 root root 7066040 Apr 10 2018 mongostat*
    -rwxr-xr-x 1 root root 6862104 Apr 10 2018 mongotop*
  • 配置文件

    1
    /etc/mongodb.conf
  • 数据和日志文件

    1
    2
    /var/lib/mongodb
    /var/log/mongodb

基础操作

1
2
3
4
# 启动mongodb服务
mongod --dbpath=/data/db --logpath=/data/logs/mongdb.log
# 通过配置文件启动mongo
mongod -f mongdb.conf
1
2
3
4
5
use admin;

db.auth('username','password');

use <custom_db_name>;
  • 或者

    1
    mongosh mongodb://localhost:27017/admin  -u admin  -p admin

查询运算符

比较运算符

名称 描述
$eq 匹配等于指定值的值。
$gt 匹配大于指定值的值。
$gte 匹配大于或等于指定值的值。
$in 匹配数组中指定的任何值。
$lt 匹配小于指定值的值。
$lte 匹配小于或等于指定值的值。
$ne 匹配不等于指定值的所有值。
$nin 不匹配数组中指定的任何值。

逻辑运算符

名称 描述
$and 使用逻辑 AND 联接查询子句,返回与两个子句的条件匹配的所有文档。
$not 反转查询表达式的效果,并返回与查询表达式匹配的文档。
$nor 使用逻辑 NOR 联接查询子句将返回无法匹配两个子句的所有文档。
$or 使用逻辑 OR 联接查询子句将返回与任一子句的条件匹配的所有文档。

Element

名称 描述
$exists 匹配具有指定字段的文档。
$type 如果字段属于指定类型,则选择文档。

Evaluation

名称 描述
$expr 允许在查询语言中使用聚合表达式。
$jsonSchema 根据给定的 JSON 架构验证文档。
$mod 对字段的值执行模运算,并选择具有指定结果的文档。
$regex 选择值与指定正则表达式匹配的文档。
$text 执行文本搜索
$where 匹配满足 JavaScript 表达式的文档。

Array

名称 描述
$all 匹配包含查询中指定的所有元素的数组。
$elemMatch 如果数组字段中的元素与所有指定的$elemMatch条件匹配,则选择文档。
$size 如果数组字段为指定大小,则选择文档。

常用命令

批量删除数据

1
2
3
4
5
6
7
8
9
db.collection.deleteMany(
<filter>,
{
writeConcern: <document>,
collation: <document>
}
)

db.ai_task_record_info.deleteMany({'taskInfo.state': {$in: [ 'CALCULATE_SUCCEEDED', 'EXPIRED']},'taskInfo.startTime': {$gte: ISODate('2022-01-01T03:34:30.000Z'),$lte: ISODate('2022-07-30T03:34:30.000Z')}})

批量删除某个属性

1
2
3
4
5
6
7
8
db.collection.updateMany({},{$unset:{BodyPartExamined:""}});
{
acknowledged: true,
insertedId: null,
matchedCount: 336,
modifiedCount: 336,
upsertedCount: 0
}

若某个属性没有,则批量添加某个属性

1
2
3
4
5
6
7
8
db.collection.updateMany({BodyPartExamined:{$exists:false}},{$set:{BodyPartExamined:""}})
{
acknowledged: true,
insertedId: null,
matchedCount: 336,
modifiedCount: 336,
upsertedCount: 0
}

更新数组列表中某个符合条件的数据其中某一个值

1
2
3
4
5
6
7
8
9
10
11
12
{
"case_uid": "xxxx01",
"stlPathList": [
{
"objectName": "xxxx/6497aa86b76cc018d47c8e86.stl",
"position": "femur"
},
{
"objectName": "xxxxx/6497aa8bb76cc018d47c8f07.stl",
"position": "tibia"
}
]}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
db.collection.updateOne(
{
case_uid: "xxxx01",
"stlPathList.position": "femur"
},
{
$set: {
"stlPathList.$.objectName": "newObjectValue"
}
}
);

db.case.updateMany(
{ "case_uid": "xxxx01", "stlPathList.position": "tibia",
"stlPathList": { $exists: true } },
{ $set: { "stlPathList.$[elem].objectName": "gggg" } },
{ arrayFilters: [ { "elem.position": "femur" } ] }
)

更新数组列表中某个符合条件的整个对象数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
db.collection.updateOne(
{
case_uid: "xxxx01",
"stlPathList.position": "femur"
},
{
$set: {
"stlPathList.$": {
"objectName": "xxxxx",
"position": "tibia11"
}
}
}
);

删除数组列表中某个符合条件的整个对象数据

1
2
3
4
db.collection.updateOne(
{ },
{ $pull: { "stlPathList": { "position": "tibia11" } } }
)

移除集合中某个属性

1
db.collection.updateMany({},{$unset:{fieldName:""}})

备份/还原数据

  • 第一种方式:使用archive
1
2
3
mongodump --port 27017 --authenticationDatabase admin --db db --username admin --password admin --archive --gzip > /data/backup/backup-2023-05-09-15-59-54.gz

mongorestore -h localhost:27017 -u admin -p admin --authenticationDatabase admin --db db --numInsertionWorkersPerCollection 8 --batchSize 1000 --gzip --archive < backup-2023-05-09-15-59-54.gz
  • 第二种方式
1
2
3
4
mongodump --port 27017 --authenticationDatabase admin --db db --username admin --password admin -o /data/backup/db-2023-05-09-16-16-14

mongorestore -h localhost:27017 -u admin -p admin --authenticationDatabase admin --db db --numInsertionWorkersPerCollection 8 --batchSize 1000 --dir db-2023-05-09-16-16-14

  • 基于shell

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    #--------------------------------------------
    # mongodb定时备份脚本
    #--------------------------------------------
    #! /bin/bash

    # 命令执行路径
    MONGOD=/usr/bin/mongodump
    OUT_DIR=/data/backup/mongo/mongod_bak_tmp
    # 压缩后的备份存放路径
    TAR_DIR=/data/backup/mongo/mongod_bak_list
    # 压缩时间为当前系统时间/删除时间为七天前
    TAR_DATE=$(date +%F)
    DEL_DATE=$(date +%F -d "-7 day")

    # 数据库配置
    DB_HOST=ip:port
    DB_NAME==******
    DB_AUTHSOURCE=admin
    DB_USERNAME=******
    DB_PASSWORD=******

    if [[ ! -d ${OUT_DIR} ]];then
    mkdir -p ${OUT_DIR}
    fi

    if [[ ! -d ${TAR_DIR} ]];then
    mkdir -p ${TAR_DIR}
    fi

    TAR_BAK="mongo_bak_${TAR_DATE}.tar.gz"
    cd ${OUT_DIR}
    rm -rf ${OUT_DIR}/*
    ${MONGOD} -h ${DB_HOST} -u ${DB_USERNAME} -p ${DB_PASSWORD} --authenticationDatabase ${DB_AUTHSOURCE} -d ${DB_NAME} -o ${OUT_DIR}
    # 压缩归档
    tar -zcvPf ${TAR_DIR}/${TAR_BAK} ${OUT_DIR}
    # 清除历史归档(七天前)
    for i in `find ${TAR_DIR} -maxdepth 1 \( -type d -o -type l \)`;
    do
    find -L $i -maxdepth 1 -type f \( -name "*${DEL_DATE}*" -a -name "*.tar.gz" \) -exec rm -f {} \;
    done
    • 其他:可以将添加crontab定时任务使用Shell编写,基于Jenkins控制发布,做到自动化运维与减少误操作.一般执行crontab -e命令都是直接往/var/spool/cron下创建一个文件,这个文件的名称就是你的当前用户名,内容就是你添加的任务具体内容.依据这一点可以做到自动化的crontab发布,Shell脚本大致如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      crontab_reload(){
      echo "30 0 * * * ${SCRIPT_DIR}/auto/crontab/mongo_back.sh" > /var/spool/cron/root
      # 重启crontab
      /sbin/service crond restart
      service crond status
      echo "get current crontab"
      crontab -l
      echo "crontab reload done"
      }

查询MongoDB的状态

1
mongostat --authenticationDatabase admin  --username admin --password admin
1
2
3
4
5
6
7
8
9
10
11
12
insert query update delete getmore command dirty  used flushes vsize   res qrw arw net_in net_out conn         set repl                time
*0 1 *0 *0 0 19|0 0.0% 79.8% 0 42.1G 37.3G 0|0 1|0 2.92k 71.4k 11 skyward-pro PRI Feb 28 15:28:15.727

*0 *0 *0 *0 0 12|0 0.0% 79.8% 0 42.1G 37.3G 0|0 1|0 1.66k 66.6k 11 skyward-pro PRI Feb 28 15:28:16.727
*0 *0 *0 *0 2 15|0 0.0% 79.8% 0 42.1G 37.3G 0|0 1|0 3.94k 68.6k 11 skyward-pro PRI Feb 28 15:28:17.726
*0 *0 *0 *0 0 13|0 0.0% 79.8% 1 42.1G 37.3G 0|0 1|0 1.67k 66.6k 11 skyward-pro PRI Feb 28 15:28:18.726
*0 *0 *0 *0 0 12|0 0.0% 79.8% 0 42.1G 37.3G 0|0 1|0 1.66k 66.5k 11 skyward-pro PRI Feb 28 15:28:19.727
*0 *0 *0 *0 0 13|0 0.0% 79.8% 0 42.1G 37.3G 0|0 1|0 1.67k 66.6k 11 skyward-pro PRI Feb 28 15:28:20.727
*0 2 4 3 7 25|0 0.0% 79.8% 0 42.1G 37.3G 0|0 1|0 11.3k 78.2k 11 skyward-pro PRI Feb 28 15:28:21.726
*0 *0 *0 *0 0 12|0 0.0% 79.8% 0 42.1G 37.3G 0|0 1|0 1.66k 66.6k 11 skyward-pro PRI Feb 28 15:28:22.726
*0 *0 *0 *0 0 13|0 0.0% 79.8% 0 42.1G 37.3G 0|0 1|0 1.66k 66.6k 11 skyward-pro PRI Feb 28 15:28:23.726
*0 *0 *0 *0 0 13|0 0.0% 79.8% 0 42.1G 37.3G 0|0 1|0 1.67k 66.6k 11 skyward-pro PRI Feb 28 15:28:24.726
  • insertqueryupdatedelete 等列表示对应操作的次数。
  • mapped 列表示数据文件和索引文件映射到内存的大小。
  • vsize 列表示虚拟内存的大小。
  • res 列表示实际物理内存的大小。
  • faults 列表示页错误的次数。
  • locked 列表示全局锁定的百分比。
  • qr|qw 列表示读操作和写操作在队列中的数量。
  • ar|aw 列表示活跃读操作和写操作的数量。
  • netIn 列表示从客户端接收的网络流量。
  • netOut 列表示向客户端发送的网络流量。
  • conn 列表示当前的连接数。
  • time 表示输出时间。

修改Collection的名称

1
2
3
4
5
use admin;

db.auth("usernmae","passwrod");

db.collection.renameCollection("new_collection_name");

$addToSet

  • $addToSet添加值到一个数组中去,如果数组中已经存在该值那么将不会有任何的操作

    1
    2
    3
    4
    5
    6
    { $addToSet: { <field1>: <value1>, ... } }

    db.test.update(
    { _id: 1 },
    { $addToSet: {letters: [ "c", "d" ] } }
    )
$each
  • $addToSet操作符和$each修饰符可以配合使用,$each修饰符允许$addToSet操作符添加多个元素到数组字段中

    1
    2
    3
    4
    db.test.update(
    { _id: 2 },
    { $addToSet: { tags: { $each: [ "a", "b", "c" ] } } }
    )

根据某个字段的值拆分为两个字段

1
2
3
4
5
6
7
8
db.collection.updateMany({ StudySeconds: { $exists: true } }, [
{
$set: {
StudyDate: { $dateToString: { format: "%Y-%m-%d", date: { $toDate: { $multiply: ["$StudySeconds", 1000] } } } },
StudyTime: { $dateToString: { format: "%H:%M:%S", date: { $toDate: { $multiply: ["$StudySeconds", 1000] } } } }
}
}
])

日志等级

1
2
3
4
# 获取日志等级信息
db.getLogComponents()
# 设置某个模块的日志等级
db.setLogLevel(2, "transaction")

查询

查询某个数组字段,数组长度最长的一条记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
db.collection.aggregate([
{
$project: {
sopCount: { $size: "$SOPInstanceUID" },
SOPInstanceUID: 1
}
},
{
$sort: { sopCount: -1 }
},
{
$limit: 1
}
])

查询数组对象中某个字段

  • 原始数据
1
2
3
4
5
6
7
8
9
10
11
12
{
_id: ObjectId('6482d929456e527714947cdd'),
array:[{
file_id:ObjectId('6482d929456e524414947cdd'),
file_name:"ceshi1"
},
{
file_id:ObjectId('6482d929456e525514947cdd'),
file_name:"ceshi2"
}
]
}
1
2
3
4
5
6
7
db.collection.find({
array: {
$elemMatch: {
"file_id": ObjectId('6482d929456e524414947cdd')
}
}
})

Aggregation

$match

  • 相当于SQL中的WHERE
1
2
3
4
5
6
7
8
9
10
11
12
13
{ $match: { <query> } }
#query输入想匹配的条件

示例
{
$match:
{
calculate_start_date:{
$gt: ISODate("2023-01-01T00:00:00Z"),
$lt: ISODate("2023-10-01T00:00:00Z")
}
}
}

$group

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
$group:
{
_id: <expression>, // Group By Expression
<field1>: { <accumulator1> : <expression1> },
...
}
}
#_id 相当于sql中 group by 后面的字段
#field 可选,添加累加器,作用于每个分组

示例


{
$group:
{
_id: "$type",
count:{
$sum:1
}
}
}

$project

  • 相当于SQL中的SELECT
1
2
3
4
5
6
7
8
9
{ $project: { <specification(s)> } }
#控制字段展示与否,1 or true代表展示,0 or false代表隐藏

{
$project:
{
type:1
}
}

$count

1
2
{ $count: <string> }
#string中填写展示计算值的字段名称

$sort

1
2
3
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
#field:填写字段
#sort order 中的值 升序:1 降序:-1

$lookup

  • 相当于select *,commentstid from table where commentstid in(select * from comments where tid=table.tid);
1
2
3
4
5
6
7
8
9
10
11
12
13
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}
#from:需要join的集合名词
#localField:当前集合的字段名称
#foreignField:join的集合 与localField相同的字段名称
#as:join的集合中符合条件的数据字段值

$dateToString

  • 格式说明符

    说明符 描述 可能的值
    %d 每月的日期(2位数字,零填充) 0131
    %G ISO 8601格式的年份 00009999
    %H 小时(2位数字,零填充,24小时制) 0023
    %L 毫秒(3位数字,零填充) 000999
    %m 月(2位数字,零填充) 0112
    %M 分钟(2位数字,零填充) 0059
    %S 秒(2位数字,零填充) 0060
    %u ISO 8601格式的星期几编号(1-Monday,7-Sunday) 17
    %V 一年中的星期,采用ISO 8601格式 153
    %Y 年(4位数字,零填充) 00009999
    %z 与UTC的时区偏移量. +/-hh
    %Z 分钟数从UTC偏移为数字.例如,如果时区偏移量(+/-[hhmm])为+0445,则分钟偏移量为+285. +/-mmm
    %% 文字字符百分比 %

主从配置步骤

创建主节点

  • 创建存放数据和日志的目录

    1
    2
    mkdir -p /mongodb/node_1/db/data
    mkdir -p /mongodb/node_1/log
  • 新建或修改配置文件

    1
    vim /etc/mongdb.conf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    systemLog:
    # MongoDB发送所有日志输出的目标指定为文件
    destination: file
    # mongod或mongos应向其发送所有诊断日志记录信息的文件路径
    path: "/mongodb/node_1/log/mongod.log"
    # 当mongod或mongos实例重新启动时,mongod或mongos会将条目附加到现有日志文件的末尾
    logAppend: true
    storage:
    # mongod实例存储数据的目录,storage.dbPath设置仅使用于mongod
    dbPath: "/mongodb/node_1/db/data"
    journal:
    # 启用或禁用持久性日志以确保数据文件保持有效和可恢复
    enabled: true
    processManagement:
    # 启用在后台运行mongod或mongos进程的守护进程模式
    fork: true
    # 指定用于保存mongod或mongos进程的进程ID的文件位置,其中mongod或mongos将写入其PID
    pidFilePath: "/mongodb/node_1/log/mongod.pid"
    net:
    # 服务示例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip
    # bindAll: true
    # 服务实例绑定的IP
    bindIp: localhost,192.168.0.1
    # 绑定的端口
    port: 27017
    replication:
    # 副本集的名称
    replSetName: node
  • 启动节点服务

    1
    mongod -f /etc/mongdb.conf

创建副本节点

  • 建立存放数据和日志的目录

    1
    2
    mkdir -p /mongodb/node_2/db/data
    mkdir -p /mongodb/node_2/log
  • 新建或修改配置文件

    1
    vim /etc/mongdb.conf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    systemLog:
    # MongoDB发送所有日志输出的目标指定为文件
    destination: file
    # mongod或mongos应向其发送所有诊断日志记录信息的文件路径
    path: "/mongodb/node_2/log/mongod.log"
    # 当mongod或mongos实例重新启动时,mongod或mongos会将条目附加到现有日志文件的末尾
    logAppend: true
    storage:
    # mongod实例存储数据的目录,storage.dbPath设置仅使用于mongod
    dbPath: "/mongodb/node_2/db/data"
    journal:
    # 启用或禁用持久性日志以确保数据文件保持有效和可恢复
    enabled: true
    processManagement:
    # 启用在后台运行mongod或mongos进程的守护进程模式
    fork: true
    # 指定用于保存mongod或mongos进程的进程ID的文件位置,其中mongod或mongos将写入其PID
    pidFilePath: "/mongodb/node_2/log/mongod.pid"
    net:
    # 服务示例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip
    # bindAll: true
    # 服务实例绑定的IP
    bindIp: localhost,192.168.0.2
    # 绑定的端口
    port: 27018
    replication:
    # 副本集的名称
    replSetName: node
  • 启动节点服务

    1
    mongod -f /etc/mongdb.conf

创建仲裁节点

  • 建立存放数据和日志的目录

    1
    2
    mkdir -p /mongodb/node_3/db/data
    mkdir -p /mongodb/node_3/log
  • 新建或修改配置文件

    1
    vim /etc/mongdb.conf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    systemLog:
    # MongoDB发送所有日志输出的目标指定为文件
    destination: file
    # mongod或mongos应向其发送所有诊断日志记录信息的文件路径
    path: "mkdir -p /mongodb/node_3/log/mongod.log"
    # 当mongod或mongos实例重新启动时,mongod或mongos会将条目附加到现有日志文件的末尾
    logAppend: true
    storage:
    # mongod实例存储数据的目录,storage.dbPath设置仅使用于mongod
    dbPath: "/mongodb/node_3/db/data"
    journal:
    # 启用或禁用持久性日志以确保数据文件保持有效和可恢复
    enabled: true
    processManagement:
    # 启用在后台运行mongod或mongos进程的守护进程模式
    fork: true
    # 指定用于保存mongod或mongos进程的进程ID的文件位置,其中mongod或mongos将写入其PID
    pidFilePath: "/mongodb/node_3/log/mongod.pid"
    net:
    # 服务示例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip
    # bindAll: true
    # 服务实例绑定的IP
    bindIp: localhost,192.168.0.3
    # 绑定的端口
    port: 27019
    replication:
    # 副本集的名称
    replSetName: node
  • 启动节点服务

    1
    mongod -f /etc/mongdb.conf

初始化配置副本集合主节点

  • 使用客户端命令连接主节点

    1
    mongo --host=<master host> --port=<master port>
  • 准备初始化新的副本集

    1
    rs.initiate()
    • "ok"的值为1,说明创建成功
    • 命令行提示符发生变化,变成了一个从节点角色,此时默认不能读写,稍等片刻,回车,变成主节点.

查看副本集的配置内容

  • 返回包含副本集配置的文档
1
rs.conf();

查看副本集状态

1
rs.status()
rs.status()各个字段的含义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
{
set: 'dev',
date: 2023-06-05T02:32:53.388Z,
myState: 1,
term: Long("84"),
syncingTo: '',
syncSourceHost: '',
syncSourceId: -1,
heartbeatIntervalMillis: Long("2000"),
optimes: {
lastCommittedOpTime: { ts: Timestamp({ t: 1685932362, i: 10 }), t: Long("84") },
readConcernMajorityOpTime: { ts: Timestamp({ t: 1685932362, i: 10 }), t: Long("84") },
appliedOpTime: { ts: Timestamp({ t: 1685932362, i: 10 }), t: Long("84") },
durableOpTime: { ts: Timestamp({ t: 1685932362, i: 10 }), t: Long("84") }
},
members: [
{
_id: 0,
name: 'dev.primary.host:27017',
health: 1,
state: 1,
stateStr: 'PRIMARY',
uptime: 16746014,
optime: [Object],
optimeDate: 2023-06-05T02:32:42.000Z,
syncingTo: '',
syncSourceHost: '',
syncSourceId: -1,
infoMessage: '',
electionTime: Timestamp({ t: 1669186373, i: 1 }),
electionDate: 2022-11-23T06:52:53.000Z,
configVersion: 281004,
self: true,
lastHeartbeatMessage: ''
},
{
_id: 1,
name: 'dev.second.host:27017',
health: 1,
state: 2,
stateStr: 'SECONDARY',
uptime: 293,
optime: [Object],
optimeDurable: [Object],
optimeDate: 2023-06-05T02:32:42.000Z,
optimeDurableDate: 2023-06-05T02:32:42.000Z,
lastHeartbeat: 2023-06-05T02:32:52.623Z,
lastHeartbeatRecv: 2023-06-05T02:32:51.572Z,
pingMs: Long("31"),
lastHeartbeatMessage: '',
syncingTo: 'dev.primary.host:27017',
syncSourceHost: 'dev.primary.host:27017',
syncSourceId: 0,
infoMessage: '',
configVersion: 281004
}
],
ok: 1,
operationTime: Timestamp({ t: 1685932362, i: 10 }),
'$clusterTime': {
clusterTime: Timestamp({ t: 1685932362, i: 10 }),
signature: {
hash: Binary(Buffer.from("3fe8cac50d6730647af5676b4bdab3735c1572a0", "hex"), 0),
keyId: Long("7214158379843846145")
}
}
}
  • set: 副本集的名称.

  • date: 副本集状态信息的生成时间.

  • myState: 当前节点的状态,包括以下几种:

    • 0:初始化状态.

    • 1:正在启动.

    • 2:正在运行并可读写.

    • 3:进行中的重新同步.

    • 4:暂停,不可读写.

    • 5:非常规终止.

    • 6:正在启动,但需要等待其他节点.

    • 7:正在从其他节点进行同步.

  • term: 当前副本集的轮次.

  • syncSourceHost: 执行同步的节点的主机名和端口号.

  • syncSourceId: 执行同步的节点的 ID.

  • heartbeatIntervalMillis: 心跳间隔时间(毫秒).

  • optimes: 记录了每个节点最近的操作时间.

    • lastCommittedOpTime: 最近的已提交的操作时间戳.

    • readConcernMajorityOpTime: 读取大多数节点时的操作时间戳.

    • appliedOpTime: 最近的已应用的操作时间戳.

  • members: 一个数组,记录了每个节点的状态信息,包括以下几个字段:

    • _id: 节点的 ID.

    • name: 节点的主机名和端口号.

    • health: 节点的健康状态(1 表示健康,0 表示不健康).

    • state: 节点的状态(参见 myState).

    • stateStr:节点状态的字符串表示.

    • uptime: 节点已运行的时间(秒).

    • optime: 节点最近的操作时间戳.

    • optimeDate: 节点最近的操作时间戳的日期时间.

    • lastHeartbeat: 最近一次心跳的时间戳.

    • lastHeartbeatRecv: 最近一次接收到心跳的时间戳.

    • pingMs: 节点之间的延迟(毫秒).

    • lastSync: 最近一次同步的时间戳.

    • lastSyncRecv: 最近一次接收到同步信息的时间戳.

    • synckey: 同步信息的键.

    • syncingTo: 正在同步的节点的主机名和端口号.

    • configVersion: 节点的配置版本号.

    • electionTime: 节点上一次选举的时间戳.

    • electionDate: 节点上一次选举的日期时间.

    • infoMessage: 节点的信息消息.

添加副本从节点

1
rs.add(host,arbiterOnly);
1
rs.add("host:port");

添加仲裁从节点

1
rs.addArb("host:port");

主节点的信息

1
2
3
4
5
6
7
8
// 获取当前配置
var cfg = rs.conf()

// 修改主节点的 host 信息
cfg.members[0].host = "192.168.1.100:27017"

// 应用新的配置
rs.reconfig(cfg, { force: true })

副本集的数据读写操作

  • 设置读权限,在从节点上设置其为奴隶节点,运行在从成员上运行读的操作

    1
    rs.slaveOk();

成员状态

STARTUP
  • 这是成员在第一次启动时的状态,这时 MongoDB 正在尝试加载它的副本集配置.一旦配置被加载,它就转换到STARTUP2状态
STARTUP2
  • 初始同步过程时会持续处于这个状态,通常只需几秒.成员会创造出几个线程来处理复制和选举,然后转换到下一个状态:RECOVERING.
RECOVERING
  • 此状态表明成员运行正常,但不能处理读请求.这可能是因为以下几种情况.在启动时,成员必须做一些检查以确保自己处于有效的状态,之后才能接受读请求.因此,在启动过程中,所有的成员在成为从节点之前都需要经历短暂的 RECOVERING 状态.在处理一些耗时操作(比如压缩或者相应replSetMaintenance命令)时,成员也可能进入RECOVERING 状态.
  • 如果一个成员远远落后于其他成员而无法赶上时,也会进入 RECOVERING 状态.通常来说,这是需要进行重新同步的无效状态.这时,该成员不会进入错误状态,因为它希望找到一个拥有足够长 oplog 的成员,从而引导自己回到非过时状态.
ARBITER
  • 这是仲裁者节点独有的一个特殊状态,并且其在正常运行期间应该始终处于这个状态.

以下一些状态表明系统存在问题.

DOWN
  • 如果一个成员被正常启动,但后来变为不可访问,那么就会进入这种状态.注意,被报告为DOWN状态的成员实际上可能仍在运行,只是由于网络问题而无法访问.
UNKNOWN
  • 如果一个成员从未能访问到另一个成员,那么就不知道它处于什么状态,因此会将其报告为UNKNOWN.这通常表示这个未知成员已停止运行或两个成员之间存在网络问题.
REMOVED
  • 这个状态表示此成员已被从副本集中移除.如果移除的成员被添加回副本集,它就会转换回“正常”的状态.
ROLLBACK
  • 当成员正在回滚数据时会处于此状态.在回滚过程结束时,服务器会转换回 RECOVERING 状态,然后成为从节点

使用Docker部署

遇到的问题

img

  • docker-compose.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    version: '3.1'

    services:
    mongo_primary_mark:
    image: mongo:4.0.1
    container_name: mongo_primary_mark
    command: mongod -f /etc/mongod.conf
    ports:
    - 27017:27017
    volumes:
    - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/db:/data/db
    - /Users/holelin/data/dockerfiles/mark/config/mongodb.conf:/etc/mongod.conf
    - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/mongodb.log:/data/mongodb.log
    environment:
    - TZ=Asia/Shanghai
    - MONGO_INITDB_ROOT_USERNAME=admin
    - MONGO_INITDB_ROOT_PASSWORD=admin
  • mongod.conf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    systemLog:
    quiet: false
    path: /data/mongodb.log
    logAppend: true
    destination: file
    net:
    bindIp: 127.0.0.1
    port: 27017
    processManagement:
    fork: true
    storage:
    dbPath: /data/db
    journal:
    enabled: true
    replication:
    oplogSizeMB: 500
    replSetName: rs
  • 改正一: 修改docker-compose.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    version: '3.1'

    services:
    mongo_primary_mark:
    image: mongo:4.0.1
    container_name: mongo_primary_mark
    command: mongod -f /etc/mongod.conf
    ports:
    - 27017:27017
    volumes:
    - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/db:/data/db
    - /Users/holelin/data/dockerfiles/mark/config/mongodb.conf:/etc/mongod.conf
    - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/mongodb.log:/data/mongodb.log
    - /Users/holelin/data/dockerfiles/data/mongo_primary/.dbshell:/home/mongodb/.dbshell
    environment:
    - TZ=Asia/Shanghai
    - MONGO_INITDB_ROOT_USERNAME=admin
    - MONGO_INITDB_ROOT_PASSWORD=admin
    1
    mongo_primary_mark    | 2024-03-04T20:53:34.296+0800 E -        [main] Error saving history file: FileOpenFailed: Unable to open() file /home/mongodb/.dbshell: Unknown error
    • 报错信息消失
  • 改正二: 修改docker-compose.yaml,注释MONGO_INITDB_ROOT_USERNAMEMONGO_INITDB_ROOT_PASSWORD环境变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    version: '3.1'

    services:
    mongo_primary_mark:
    image: mongo:4.0.1
    container_name: mongo_primary_mark
    command: mongod -f /etc/mongod.conf
    ports:
    - 27017:27017
    volumes:
    - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/db:/data/db
    - /Users/holelin/data/dockerfiles/mark/config/mongodb.conf:/etc/mongod.conf
    - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/mongodb.log:/data/mongodb.log
    # - /Users/holelin/data/dockerfiles/data/mongo_primary/.dbshell:/home/mongodb/.dbshell
    # - /Users/holelin/data/dockerfiles/config/mongo-key:/data/config/key:rw
    environment:
    - TZ=Asia/Shanghai
    # - MONGO_INITDB_ROOT_USERNAME=admin
    # - MONGO_INITDB_ROOT_PASSWORD=admin
    • 修改mongodb.conf,注释processManagement.fork

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      systemLog:
      quiet: false
      path: /data/mongodb.log
      logAppend: true
      destination: file
      net:
      bindIp: 127.0.0.1,192.168.0.103
      port: 27017
      # processManagement:
      # fork: true
      storage:
      dbPath: /data/db
      journal:
      enabled: true
      replication:
      oplogSizeMB: 500
      replSetName: rs
    • 查看日志,日志正常启动

  • 配置副本集,修改mongodb.conf(至此问题解决)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    security:
    clusterAuthMode: keyFile
    keyFile: /data/config/key
    authorization: enabled
    systemLog:
    quiet: false
    path: /data/mongodb.log
    logAppend: true
    destination: file
    net:
    bindIp: 127.0.0.1
    port: 27017
    storage:
    dbPath: /data/db
    journal:
    enabled: true
    replication:
    oplogSizeMB: 500
    replSetName: rs
    • 生成keyfiles

      1
      2
      openssl rand -base64 756 > <path-to-keyfile>
      chmod 400 <path-to-keyfile>
    • 修改docker-compose.yaml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      version: '3.1'

      services:
      mongo_primary_mark:
      image: mongo:4.0.1
      container_name: mongo_primary_mark
      command: mongod -f /etc/mongod.conf
      ports:
      - 27017:27017
      volumes:
      - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/db:/data/db
      - /Users/holelin/data/dockerfiles/mark/config/mongodb.conf:/etc/mongod.conf
      - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/mongodb.log:/data/mongodb.log
      - /Users/holelin/data/dockerfiles/config/mongo-key:/data/config/key:rw
      environment:
      - TZ=Asia/Shanghai
  • 再配置两个节点,配置为副本集

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    # Use root/example as user/password credentials
    version: '3.1'

    services:
    mongo_primary_mark:
    image: mongo:4.0.1
    container_name: mongo_primary_mark
    command: mongod -f /etc/mongod.conf
    ports:
    - 27017:27017
    volumes:
    - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/db:/data/db
    - /Users/holelin/data/dockerfiles/mark/config/mongodb.conf:/etc/mongod.conf
    - /Users/holelin/data/dockerfiles/mark/data/mongo_primary/mongodb.log:/data/mongodb.log
    - /Users/holelin/data/dockerfiles/config/mongo-key:/data/config/key:rw
    environment:
    - TZ=Asia/Shanghai
    mongo_second_mark:
    image: mongo:4.0.1
    container_name: mongo_second_mark
    command: mongod -f /etc/mongod.conf
    ports:
    - 27018:27017
    volumes:
    - /Users/holelin/data/dockerfiles/mark/data/mongo_second/db:/data/db
    - /Users/holelin/data/dockerfiles/mark/config/mongodb.conf:/etc/mongod.conf
    - /Users/holelin/data/dockerfiles/mark/data/mongo_second/mongodb.log:/data/mongodb.log
    - /Users/holelin/data/dockerfiles/config/mongo-key:/data/config/key:rw
    environment:
    - TZ=Asia/Shanghai
    mongo_third_mark:
    image: mongo:4.0.1
    container_name: mongo_third_mark
    command: mongod -f /etc/mongod.conf
    ports:
    - 27019:27017
    volumes:
    - /Users/holelin/data/dockerfiles/mark/data/mongo_third/db:/data/db
    - /Users/holelin/data/dockerfiles/mark/config/mongodb.conf:/etc/mongod.conf
    - /Users/holelin/data/dockerfiles/mark/data/mongo_third/mongodb.log:/data/mongodb.log
    - /Users/holelin/data/dockerfiles/config/mongo-key:/data/config/key:rw
    environment:
    - TZ=Asia/Shanghai
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    security:
    clusterAuthMode: keyFile
    keyFile: /data/config/key
    authorization: enabled
    systemLog:
    quiet: false
    path: /data/mongodb.log
    logAppend: true
    destination: file
    net:
    bindIp: 127.0.0.1,192.168.0.103
    port: 27017
    # processManagement:
    # fork: true
    storage:
    dbPath: /data/db
    journal:
    enabled: true
    replication:
    oplogSizeMB: 500
    replSetName: rs
  • 进入任意容器docker exec -it mongo_primary_mark bash

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    docker exec -it mongo_primary_mark bash
    root@76fcbe7ed5bf:/# mongo
    MongoDB shell version v4.0.1
    connecting to: mongodb://127.0.0.1:27017
    MongoDB server version: 4.0.1
    Welcome to the MongoDB shell.
    For interactive help, type "help".
    For more comprehensive documentation, see
    http://docs.mongodb.org/
    Questions? Try the support group
    http://groups.google.com/group/mongodb-user
    > use admin
    switched to db admin
    > config = { "_id": "rs", "members":
    ... [{ "_id": 0, "host": "192.168.0.103:27017", "priority": 1 },
    ... { "_id": 1, "host": "192.168.0.103:27018", "priority": 1 },
    ... { "_id": 2, "host": "192.168.0.103:27019", "priority": 1 }
    ... ] }
    {
    "_id" : "rs",
    "members" : [
    {
    "_id" : 0,
    "host" : "192.168.0.103:27017",
    "priority" : 1
    },
    {
    "_id" : 1,
    "host" : "192.168.0.103:27018",
    "priority" : 1
    },
    {
    "_id" : 2,
    "host" : "192.168.0.103:27019",
    "priority" : 1
    }
    ]
    }
    > rs.initiate(config)
    { "ok" : 1 }
    rs:PRIMARY> rs.status()
    {
    "set" : "rs",
    "date" : ISODate("2024-03-04T13:39:34.634Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "syncingTo" : "",
    "syncSourceHost" : "",
    "syncSourceId" : -1,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
    "lastCommittedOpTime" : {
    "ts" : Timestamp(1709559571, 1),
    "t" : NumberLong(1)
    },
    "readConcernMajorityOpTime" : {
    "ts" : Timestamp(1709559571, 1),
    "t" : NumberLong(1)
    },
    "appliedOpTime" : {
    "ts" : Timestamp(1709559571, 1),
    "t" : NumberLong(1)
    },
    "durableOpTime" : {
    "ts" : Timestamp(1709559571, 1),
    "t" : NumberLong(1)
    }
    },
    "lastStableCheckpointTimestamp" : Timestamp(1709559570, 1),
    "members" : [
    {
    "_id" : 0,
    "name" : "192.168.0.103:27017",
    "health" : 1,
    "state" : 1,
    "stateStr" : "PRIMARY",
    "uptime" : 71,
    "optime" : {
    "ts" : Timestamp(1709559571, 1),
    "t" : NumberLong(1)
    },
    "optimeDate" : ISODate("2024-03-04T13:39:31Z"),
    "syncingTo" : "",
    "syncSourceHost" : "",
    "syncSourceId" : -1,
    "infoMessage" : "could not find member to sync from",
    "electionTime" : Timestamp(1709559568, 1),
    "electionDate" : ISODate("2024-03-04T13:39:28Z"),
    "configVersion" : 1,
    "self" : true,
    "lastHeartbeatMessage" : ""
    },
    {
    "_id" : 1,
    "name" : "192.168.0.103:27018",
    "health" : 1,
    "state" : 2,
    "stateStr" : "SECONDARY",
    "uptime" : 16,
    "optime" : {
    "ts" : Timestamp(1709559571, 1),
    "t" : NumberLong(1)
    },
    "optimeDurable" : {
    "ts" : Timestamp(1709559571, 1),
    "t" : NumberLong(1)
    },
    "optimeDate" : ISODate("2024-03-04T13:39:31Z"),
    "optimeDurableDate" : ISODate("2024-03-04T13:39:31Z"),
    "lastHeartbeat" : ISODate("2024-03-04T13:39:32.981Z"),
    "lastHeartbeatRecv" : ISODate("2024-03-04T13:39:33.876Z"),
    "pingMs" : NumberLong(2),
    "lastHeartbeatMessage" : "",
    "syncingTo" : "192.168.0.103:27017",
    "syncSourceHost" : "192.168.0.103:27017",
    "syncSourceId" : 0,
    "infoMessage" : "",
    "configVersion" : 1
    },
    {
    "_id" : 2,
    "name" : "192.168.0.103:27019",
    "health" : 1,
    "state" : 2,
    "stateStr" : "SECONDARY",
    "uptime" : 16,
    "optime" : {
    "ts" : Timestamp(1709559571, 1),
    "t" : NumberLong(1)
    },
    "optimeDurable" : {
    "ts" : Timestamp(1709559571, 1),
    "t" : NumberLong(1)
    },
    "optimeDate" : ISODate("2024-03-04T13:39:31Z"),
    "optimeDurableDate" : ISODate("2024-03-04T13:39:31Z"),
    "lastHeartbeat" : ISODate("2024-03-04T13:39:32.981Z"),
    "lastHeartbeatRecv" : ISODate("2024-03-04T13:39:33.876Z"),
    "pingMs" : NumberLong(2),
    "lastHeartbeatMessage" : "",
    "syncingTo" : "192.168.0.103:27017",
    "syncSourceHost" : "192.168.0.103:27017",
    "syncSourceId" : 0,
    "infoMessage" : "",
    "configVersion" : 1
    }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1709559571, 1),
    "$clusterTime" : {
    "clusterTime" : Timestamp(1709559571, 1),
    "signature" : {
    "hash" : BinData(0,"74pi5pLvoVJHxJn0Y5zFLrKf0qU="),
    "keyId" : NumberLong("7342502443713822721")
    }
    }
    }
    rs:PRIMARY> db.createUser({'user':'admin','pwd':'admin','roles':['root']})
    Successfully added user: { "user" : "admin", "roles" : [ "root" ] }

备份还原

遇到的问题

1
2024-03-04T17:31:49.565+0800	Failed: restore error: error applying oplog: applyOps: not authorized on admin to execute command { applyOps: [ { ts: Timestamp(1709540239, 1), h: 3559914748497557050, v: 2, op: "c", ns: "config.$cmd", o: { create: "system.sessions", idIndex: { v: 2, key: { _id: 1 }, name: "_id_", ns: "config.system.sessions" } }, o2: {} } ], $db: "admin" }
  • 背景: 使用db.createUser({'user':'admin','pwd':'admin','roles':['root']})拥有root权限的账号操作,报错如上错误.

  • 解决办法: 自定义角色并赋予该角色anyResourceanyAction的权限

    1
    2
    db.createRole({role:'sysadmin',roles:[], privileges:[ {resource:{anyResource:true},actions:['anyAction']}]})
    db.grantRolesToUser( "root" , [ { role: "sysadmin", db: "admin" } ])

备份脚本

全量备份
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/bash
# Author:hukey

host='192.168.0.103'
port='27017'
sourcepath=' /usr/bin/'
targetpath='/mongodb/backup'
nowtime=$(date "+%Y%m%d")


start(){
$sourcepath/mongodump --host $host --port $port --username admin --password admin --authenticationDatabase admin --oplog --gzip --out ${targetpath}/${nowtime}
}

execute(){
echo "=========================$(date) backup all mongodb back start ${nowtime}========="
start
if [ $? -eq 0 ];then
echo "The MongoDB BackUp Successfully!"
else
echo "The MongoDB BackUp Failure"
fi
}

if [ ! -d "${targetpath}/${nowtime}" ];then
mkdir -p "${targetpath}/${nowtime}"
fi

execute

backtime=$(date -d '-7 days' "+%Y%m%d")
if [ -d "${targetpath}/${backtime}/" ];then
rm -rf "${targetpath}/${backtime}/"
echo "=======${targetpath}/${backtime}/===删除完毕=="
fi

echo "========================= $(date) backup all mongodb back end ${nowtime}========="
增量备份
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/bin/bash
# Author:hukey
command_linebin='/usr/bin/mongo'
port=27017

if [ ! -d "/mongodb/backup/mongodbOplog_bak/mongo-$port" ];then
mkdir -p /mongodb/backup/mongodbOplog_bak/mongo-$port
fi

if [ ! -d "/mongodb/backup/mongodbOplog_bak/log-$port" ];then
mkdir -p /mongodb/backup/mongodbOplog_bak/log-$port
fi

bkdatapath=/mongodb/backup/mongodbOplog_bak/mongo-$port
bklogpath=/mongodb/backup/mongodbOplog_bak/log-$port

logfilename=$(date +"%Y%m%d")

echo "===MongoDB 端口为" $port "的差异备份开始,开始时间为" $(date -d today +"%Y%m%d%H%M%S")

paramBakEndDate=$(date +%s)
echo "===本次备份时间参数中的结束时间为:" $paramBakEndDate

diffTime=$(expr 65 \* 60)
echo "===备份设置的间隔时间为:" $diffTime

paramBakStartDate=$(expr $paramBakEndDate - $diffTime)
echo "===本次备份时间参数中的开始时间为:" $paramBakStartDate

diffTime=$(expr 61 \* 60)
paramAfterBakRequestStartDate=$(expr $paramBakEndDate - $diffTime)
echo "===为保证备份的连续性,本次备份后,oplog中的开始时间需小于:" $paramAfterBakRequestStartDate

bkfilename=$(date -d today +"%Y%m%d%H%M%S")

command_line="${command_linebin} 192.168.0.103:27017"

opmes=$(/bin/echo "db.printReplicationInfo()" | $command_line --quiet)

echo $opmes > /tmp/opdoctime$port.tmplog
opbktmplogfile=/tmp/opdoctime$port.tmplog
opstartmes=$(grep "oplog first event time" $opbktmplogfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}' )
oplogRecordFirst=$(date -d "$opstartmes" +%s)
echo "===oplog集合记录的开始时间为[格式化]:" $oplogRecordFirst
if [ $oplogRecordFirst -le $paramBakStartDate ]; then
echo "Message --检查设置备份时间合理。备份参数的开始时间在oplog记录的时间范围内。"
else
echo "Fatal Error --检查设置的备份时间合不合理。备份参数的开始时间不在oplog记录的时间范围内。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。"
fi

/usr/bin/mongodump -h 192.168.0.103 --port $port --username admin --password admin --authenticationDatabase admin -d local -c oplog.rs --query '{ts:{$gte:Timestamp('$paramBakStartDate',1),$lte:Timestamp('$paramBakEndDate',9999)}}' -o $bkdatapath/mongodboplog$bkfilename


opmes=$(/bin/echo "db.printReplicationInfo()" | $command_line --quiet)
echo $opmes > /tmp/opdoctime$port.tmplog
opbktmplogfile=/tmp/opdoctime$port.tmplog
opstartmes=$(grep "oplog first event time" $opbktmplogfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}' )
oplogRecordFirst=$(date -d "$opstartmes" +%s)
echo "===执行备份后,oplog集合记录的开始时间为[时间格式化]:" $oplogRecordFirst

if [ $oplogRecordFirst -le $paramAfterBakRequestStartDate ]; then
echo "Message --备份后,检查oplog集合中数据的开始时间,即集合中最早的一笔数据,时间不小于61分钟的时间(即参数 paramAfterBakRequestStartDate)。这样可以保证每个增量备份含有最近一个小时的全部op操作,满足文件的持续完整性,逐个还原无丢失数据风险。"
else
echo "Fatal Error --备份后,检查oplog集合的涵盖的时间范围过小(小于61min)。设置的备份时间不合理合理,备份后的文件不能完全涵盖最近60分钟的数据。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。"
fi

if [ -d "$bkdatapath/mongodboplog$bkfilename" ]
then
echo "Message --检查此次备份文件已经产生.文件信息为:" $bkdatapath/mongodboplog$bkfilename >> $bklogpath/$logfilename.log
else
echo "Fatal Error --备份过程已执行,但是未检测到备份产生的文件,请检查!" >> $bklogpath/$logfilename.log
fi

keepbaktime=$(date -d '-3 days' "+%Y%m%d%H")*
if [ -d $bkdatapath/mongodboplog$keepbaktime ]; then
rm -rf $bkdatapath/mongodboplog$keepbaktime
echo "Message -- $bkdatapath/mongodboplog$keepbaktime 删除完毕" >> $bklogpath/$logfilename.log
fi

echo "===MongoDB 端口为" $port "的差异备份结束,结束时间为:" $(date -d today +"%Y%m%d%H%M%S")
  • 第二版

  • 配置文件backup_config.conf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # MongoDB连接信息
    mongodb_bin='/usr/bin/mongo'
    mongodb_dump_bin='/usr/bin/mongo'
    mongodb_port=27017
    mongodb_host='192.168.0.103'
    mongodb_username='admin'
    mongodb_password='admin'
    mongodb_auth_database='admin'

    # 备份路径
    backup_base_path='/mongodb/backup/mongodbOplog_bak'
  • 脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    #!/bin/bash

    # 导入配置文件
    source ./backup_config.conf

    if [ ! -d "$backup_base_path/mongo-$mongodb_port" ]; then
    mkdir -p "$backup_base_path/mongo-$mongodb_port"
    fi

    if [ ! -d "$backup_base_path/log-$mongodb_port" ]; then
    mkdir -p "$backup_base_path/log-$mongodb_port"
    fi

    backup_data_path="$backup_base_path/mongo-$mongodb_port"
    backup_log_path="$backup_base_path/log-$mongodb_port"

    # 日志文件名
    log_filename=$(date +"%Y%m%d")

    echo "===MongoDB 端口为 $mongodb_port 的差异备份开始,开始时间为 $(date -d today +'%Y%m%d%H%M%S')"

    # 计算备份时间参数
    backup_end_time=$(date +%s)
    echo "===本次备份时间参数中的结束时间为: $backup_end_time"

    backup_interval=$((65 * 60))
    echo "===备份设置的间隔时间为: $backup_interval"

    backup_start_time=$((backup_end_time - backup_interval))
    echo "===本次备份时间参数中的开始时间为: $backup_start_time"

    backup_check_time=$((61 * 60))
    backup_after_request_start_time=$((backup_end_time - backup_check_time))
    echo "===为保证备份的连续性,本次备份后,oplog中的开始时间需小于: $backup_after_request_start_time"

    # 查询oplog信息
    oplog_info=$(/bin/echo "db.printReplicationInfo()" | $mongodb_bin --quiet)

    echo "$oplog_info" > /tmp/opdoctime$mongodb_port.tmplog
    oplog_tmp_logfile=/tmp/opdoctime$mongodb_port.tmplog
    oplog_start_time=$(grep "oplog first event time" $oplog_tmp_logfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}')
    oplog_record_first=$(date -d "$oplog_start_time" +%s)
    echo "===oplog集合记录的开始时间为[格式化]: $oplog_record_first"

    if [ $oplog_record_first -le $backup_start_time ]; then
    echo "Message --检查设置备份时间合理。备份参数的开始时间在oplog记录的时间范围内。"
    else
    echo "Fatal Error --检查设置的备份时间合不合理。备份参数的开始时间不在oplog记录的时间范围内。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。"
    fi

    # 执行备份操作
    $mongodb_dump_bin -h $mongodb_host --port $mongodb_port --username $mongodb_username --password $mongodb_password --authenticationDatabase $mongodb_auth_database -d local -c oplog.rs --query '{ts:{$gte:Timestamp('$backup_start_time',1),$lte:Timestamp('$backup_end_time',9999)}}' -o $backup_data_path/mongodboplog$(date -d today +"%Y%m%d%H%M%S")

    # 再次查询oplog信息
    oplog_info=$(/bin/echo "db.printReplicationInfo()" | $mongodb_bin --quiet)
    echo "$oplog_info" > /tmp/opdoctime$mongodb_port.tmplog
    oplog_tmp_logfile=/tmp/opdoctime$mongodb_port.tmplog
    oplog_start_time=$(grep "oplog first event time" $oplog_tmp_logfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}')
    oplog_record_first=$(date -d "$oplog_start_time" +%s)
    echo "===执行备份后,oplog集合记录的开始时间为[时间格式化]: $oplog_record_first"

    if [ $oplog_record_first -le $backup_after_request_start_time ]; then
    echo "Message --备份后,检查oplog集合中数据的开始时间,即集合中最早的一笔数据,时间不小于61分钟的时间(即参数 backup_after_request_start_time)。这样可以保证每个增量备份含有最近一个小时的全部op操作,满足文件的持续完整性,逐个还原无丢失数据风险。"
    else
    echo "Fatal Error --备份后,检查oplog集合的涵盖的时间范围过小(小于61min)。设置的备份时间不合理合理,备份后的文件不能完全涵盖最近60分钟的数据。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。"
    fi

    # 检查备份文件是否生成
    if [ -d "$backup_data_path/mongodboplog$(date -d today +"%Y%m%d%H%M%S")" ]; then
    echo "Message --检查此次备份文件已经产生.文件信息为: $backup_data_path/mongodboplog$(date -d today +"%Y%m%d%H%M%S")" >> $backup_log_path/$log_filename.log
    else
    echo "Fatal Error --备份过程已执行,但是未检测到备份产生的文件,请检查!" >> $backup_log_path/$log_filename.log
    fi

    # 删除三天前的备份文件
    keep_backup_time=$(date -d '-3 days' "+%Y%m%d%H")
    if [ -d $backup_data_path/mongodboplog$keep_backup_time ]; then
    rm -rf $backup_data_path/mongodboplog$keep_backup_time
    echo "Message -- $backup_data_path/mongodboplog$keep_backup_time 删除完毕" >> $backup_log_path/$log_filename.log
    fi

    echo "===MongoDB 端口为 $mongodb_port 的差异备份结束,结束时间为: $(date -d today +'%Y%m%d%H%M%S')"

还原脚本

全量还原
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash
# Author: hukey

echo -e "\033[31;1m*****[ Mongodb ] Database Restore Script*****\033[0m"
host=192.168.0.103
mongo_bin=/usr/bin/
backpath='/mongodb/backup'

echo -e "\033[32;1m[ Choose the date for the full database restore ] \033[0m"
for backfile in `ls $backpath`; do
echo $backfile
done

read -p ">>>" date_bak

if [[ $date_bak == "" ]] || [[ $date_bak == '.' ]] || [[ $date_bak == '..' ]]; then
echo -e "\033[31;1mInput cannot contain special characters.\033[0m"
exit 1
fi

if [ -d $backpath/$date_bak ]; then
read -p "Please confirm whether to restore the full database backup [y/n]:" choice

if [ "$choice" == "y" ]; then
echo -e "\033[32;1mRestoring full database backup, please wait...\033[0m"
$mongo_bin/mongorestore --host $host --port 27017 --username admin --password admin --authenticationDatabase admin --oplogReplay --gzip $backpath/$date_bak/
if [ $? -eq 0 ]; then
echo -e "\033[32;1m--------Full database restore successful.--------\033[0m"
else
echo -e "\033[31;1mRestore failed, please manually check!\033[0m"
exit 3
fi
else
exit 2
fi
else
echo "\033[31;1mIncorrect input information.\033[0m"
exit 1
fi
增量还原
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/bin/bash
# Author:hukey
command_linebin='/usr/bin/mongo'
port=27017

if [ ! -d "/mongodb/backup/mongodbOplog_bak/mongo-$port" ];then
mkdir -p /mongodb/backup/mongodbOplog_bak/mongo-$port
fi

if [ ! -d "/mongodb/backup/mongodbOplog_bak/log-$port" ];then
mkdir -p /mongodb/backup/mongodbOplog_bak/log-$port
fi

bkdatapath=/mongodb/backup/mongodbOplog_bak/mongo-$port
bklogpath=/mongodb/backup/mongodbOplog_bak/log-$port

logfilename=$(date +"%Y%m%d")

echo "===MongoDB 端口为" $port "的差异备份开始,开始时间为" $(date -d today +"%Y%m%d%H%M%S")

paramBakEndDate=$(date +%s)
echo "===本次备份时间参数中的结束时间为:" $paramBakEndDate

diffTime=$(expr 65 \* 60)
echo "===备份设置的间隔时间为:" $diffTime

paramBakStartDate=$(expr $paramBakEndDate - $diffTime)
echo "===本次备份时间参数中的开始时间为:" $paramBakStartDate

diffTime=$(expr 61 \* 60)
paramAfterBakRequestStartDate=$(expr $paramBakEndDate - $diffTime)
echo "===为保证备份的连续性,本次备份后,oplog中的开始时间需小于:" $paramAfterBakRequestStartDate

bkfilename=$(date -d today +"%Y%m%d%H%M%S")

command_line="${command_linebin} 192.168.0.103:27017"

opmes=$(/bin/echo "db.printReplicationInfo()" | $command_line --quiet)

echo $opmes > /tmp/opdoctime$port.tmplog
opbktmplogfile=/tmp/opdoctime$port.tmplog
opstartmes=$(grep "oplog first event time" $opbktmplogfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}' )
oplogRecordFirst=$(date -d "$opstartmes" +%s)
echo "===oplog集合记录的开始时间为[格式化]:" $oplogRecordFirst
if [ $oplogRecordFirst -le $paramBakStartDate ]; then
echo "Message --检查设置备份时间合理。备份参数的开始时间在oplog记录的时间范围内。"
else
echo "Fatal Error --检查设置的备份时间不合理合理。备份参数的开始时间不在oplog记录的时间范围内。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。"
fi

/usr/bin/mongodump -h 192.168.0.103 --port $port --username admin --password admin --authenticationDatabase admin -d local -c oplog.rs --query '{ts:{$gte:Timestamp('$paramBakStartDate',1),$lte:Timestamp('$paramBakEndDate',9999)}}' -o $bkdatapath/mongodboplog$bkfilename


opmes=$(/bin/echo "db.printReplicationInfo()" | $command_line --quiet)
echo $opmes > /tmp/opdoctime$port.tmplog
opbktmplogfile=/tmp/opdoctime$port.tmplog
opstartmes=$(grep "oplog first event time" $opbktmplogfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}' )
oplogRecordFirst=$(date -d "$opstartmes" +%s)
echo "===执行备份后,oplog集合记录的开始时间为[时间格式化]:" $oplogRecordFirst

if [ $oplogRecordFirst -le $paramAfterBakRequestStartDate ]; then
echo "Message --备份后,检查oplog集合中数据的开始时间,即集合中最早的一笔数据,时间不小于61分钟的时间(即参数 paramAfterBakRequestStartDate)。这样可以保证每个增量备份含有最近一个小时的全部op操作,满足文件的持续完整性,逐个还原无丢失数据风险。"
else
echo "Fatal Error --备份后,检查oplog集合的涵盖的时间范围过小(小于61min)。设置的备份时间不合理合理,备份后的文件不能完全涵盖最近60分钟的数据。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。"
fi

if [ -d "$bkdatapath/mongodboplog$bkfilename" ]
then
echo "Message --检查此次备份文件已经产生.文件信息为:" $bkdatapath/mongodboplog$bkfilename >> $bklogpath/$logfilename.log
else
echo "Fatal Error --备份过程已执行,但是未检测到备份产生的文件,请检查!" >> $bklogpath/$logfilename.log
fi

keepbaktime=$(date -d '-3 days' "+%Y%m%d%H")*
if [ -d $bkdatapath/mongodboplog$keepbaktime ]; then
rm -rf $bkdatapath/mongodboplog$keepbaktime
echo "Message -- $bkdatapath/mongodboplog$keepbaktime 删除完毕" >> $bklogpath/$logfilename.log
fi

echo "===MongoDB 端口为" $port "的差异备份结束,结束时间为:" $(date -d today +"%Y%m%d%H%M%S")

复制数据文件

1
2
3
db.fsyncLock();
cp -R /data/db/* /mnt/external-drive/backup
db.fsyncUnlock();

MongoTemplate

查询数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//  查询name=zs
Query query = Query.query(Criteria.where("name").is("zs"));
mongoTemplate.find(query,User.class);
mongoTemplate.find(query,User.class,"mongodb_user");

// 查询所有
mongoTemplate.findAll(User.class);
mongoTemplate.findAll(User.class,"mongodb_user");

// 分页查询 page页码,pageSize每页展示几个
Pageable pageable = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.desc("date")));
Query query = new Query().with(pageable);
return this.mongoTemplate.find(query, User.class,"mongodb_user");

// 查询多个
Query query= Query.query(Criteria.where("id").in("id1","id2","id3")).with(Sort.by(Sort.Order.desc("date")));
List<Publish> list= this.mongoTemplate.find(query, User.class);

// 查询数量
Criteria criteria = Criteria.where("userId").is("12345")
.and("name").is(new ObjectId("张三"))
.and("address").is("上海");
Query query = Query.query(criteria);
long count = this.mongoTemplate.count(query, User.class);

插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<User> list = new ArrayList<>();
User user= new User();//
user.setName("admin");
user.setAddress("测试");
list.add(user);

// 保存对象到mongodb
mongoTemplate.save(user);
mongoTemplate.insert(user);
// 根据集合名称保存对象到mongodb
mongoTemplate.save(user,"mongodb_user");
mongoTemplate.insert(user,"mongodb_user");
// 根据集合名称保存list到mongodb
mongoTemplate.save(list,"mongodb_user");
mongoTemplate.insert(list,"mongodb_user");
mongoTemplate.insert(list,User.class);

更新数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
User user = new User();
user.setId("5d1312aeb1829c279c6c256b");
user.setName("admin");
user.setAddress("测试");

Query query = Query.query(Criteria.where("_id").is("5d1312aeb1829c279c6c256b"));
Update update = Update.update("name","zs");
// 更新一条数据
mongoTemplate.updateFirst(query,update, User.class);
mongoTemplate.updateFirst(query,update, "mongodb_user");
mongoTemplate.updateFirst(query,update, User.class,"mongodb_user");
// 更新多条数据
mongoTemplate.updateMulti(query,update, User.class);
mongoTemplate.updateMulti(query,update,"mongodb_user");
mongoTemplate.updateMulti(query,update, User.class,"mongodb_user");
// 更新数据,如果数据不存在就新增
mongoTemplate.upsert(query,update, User.class);
mongoTemplate.upsert(query,update,"mongodb_user");
mongoTemplate.upsert(query,update, User.class,"mongodb_user");

删除数据

1
2
3
4
5
6
7
8
9
10
11
12
List<MongoDbJavaTest> list = new ArrayList<>();
User user= new User();
user.setId("5d1312aeb1829c279c6c256b");
list.add(user);

Query query = Query.query(Criteria.where("_id").in("5d1312aeb1829c279c6c256b","5d13133ab1829c29d02ce29c"));
// 根据条件删除
mongoTemplate.remove(query);
mongoTemplate.remove(user);
mongoTemplate.remove(User.class);
// 根据条件删除(可删除多条)
mongoTemplate.remove(query,User.class,"mongodb_user");

GridFS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MongoDatabase db = mongoDbFactory.getDb();
GridFSBucket gridFSBucket = GridFSBuckets.create(db, collectionName);
GridFsTemplate gridFsTemplate = new GridFsTemplate(mongoDbFactory, converter, collectionName);
//根据文件id查询文件
GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where(StringConstants.MONGODB_ID).is(fileId)));
if (Objects.nonNull(gridFSFile)) {
//打开一个下载流对象
GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
//创建GridFsResource对象,获取流
GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);
long length = gridFSFile.getLength();
try (InputStream is = gridFsResource.getInputStream()) {
}
}catch (IOException e) {
throw new BusinessException(e.getMessage());
}

用户权限

Built-In Roles 内置角色
数据库用户角色 read、readWrite
数据库管理角色 dbAdmin、dbOwner、userAdmin
集群管理角色 clusterAdmin、clusterManager、clusterMonitor、hostManager
备份恢复角色 backup、restore
所有数据库角色 readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
超级用户角色 root
内部角色 __system
角色 作用
read 允许用户读取指定数据库
readWrite 允许用户读写指定数据库
dbAdmin 允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile
userAdmin 允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户
clusterAdmin 只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限.
readAnyDatabase 只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase 只在admin数据库中可用,赋予用户所有数据库的读写权限
userAdminAnyDatabase 只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase 只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限.
root 只在admin数据库中可用.超级账号,超级权限.
角色 作用
read 读取所有非系统集合及系统集合system.indexes、system.jssystem.namespaces中的数据
readWrite 提供与read相同的特权,以及修改所有非系统集合和system.js集合中数据的能力
dbAdmin 执行管理任务,比如与模式相关的任务、索引和收集统计信息(不授予用户和角色管理权限)
userAdmin 在当前数据库中创建和修改角色及用户
dbOwner 结合了readWrite、dbAdminuserAdmin这3个角色的权限
clusterManager 对集群进行管理和监控
clusterMonitor 为监控工具,比如MongoDB CloudManagerOpsManager中的监控代理,提供只读访问权限
hostManager 监控和管理服务器
clusterAdmin 结合了clusterManager、clusterMonitorhostManager这3个角色的权限,以及dropDatabase操作
backup 提供足够的权限来使用MongoDB CloudManagerOpsManager备份代理,或者使用MongoDB备份整个mongod实例
restore 提供从除去system.profile集合数据的备份中恢复数据所需的权限
readAnyDatabase 除去localconfig,提供在所有数据库上与read相同的权限,以及在整个集群上执行listDatabases操作的权限
readWriteAnyDatabase 除去localconfig,提供在所有数据库上与readWrite相同的权限,以及在整个集群上执行listDatabases操作的权限
userAdminAnyDatabase 除去local和config,提供在所有数据库上与userAdmin相同的权限(实际上就是超级用户的角色)
dbAdminAnyDatabase 除去localconfig,提供在所有数据库上与dbAdmin相同的权限,以及在整个集群上执行listDatabases操作的权限
root 提供结合了readWriteAnyDatabase、dbAdminAnyDatabase、userAdminAnyDatabase、clusterAdmin、restorebackup几个角色的操作和对所有资源的访问权限

创建用户

1
2
3
4
5
6
7
8
9
10
11
# 在test库下,创建一个用户并设置其对test数据库为数据库管理员
db.createUser({
user: "holelin",
pwd: "<password>",
roles: [
{ role: "dbAdmin", db: "test" }
]
})

# 授予某项权限给某个用户
db.grantRolesToUser("holelin",[{"role":"readWrite","db":"test"}])

管理

查看一下用于启动的命令行选项

1
db.serverCmdLineOpts()

mongo shell中关闭服务器

1
db.shutdownServer()

把主节点变为从节点

1
2
3
rs.stepDown()

rs.stepDown(600) // 10分钟

阻止选举

  • 如果需要对主节点进行一些维护,但不想让任何其他符合条件的成员在这段过渡期间成为主节点,则可以对每个成员执行 freeze 来强制它们保持为从节点
1
rs.freeze(10000)
  • 如果在这段时间之内完成了主节点上的维护,并希望释放其他成员,则只需在每个成员上再次运行命令,将时间指定为0秒
1
rs.freeze(0)

计算延迟

  • 可以使用rs.status()来查看成员的复制状态,也可以运行rs.printReplicationInfo()rs.printSecondaryReplicationInfo()来快速地获取一份摘要.

查看oplog大小

1
2
3
use local
# MB 为单位显示集合大小
db.oplog.rs.stats(1024*1024).maxSize

查看正在运行的操作

1
db.currentOp()
  • opid: 操作的唯一标识符.可以使用这个字段来终止操作.
  • active: 操作是否正在运行.如果该字段为 false,则意味着操作已经让出或正在等待其他操作交出锁.
  • secs_running: 操作的持续时间(秒).可以使用这个字段来查找耗时过长的查询.
  • microsecs_running: 操作的持续时间(微秒).可以使用这个字段来查找耗时过长的查询.
  • op: 操作类型.这个字段通常为 queryinsertupdateremove.注意,数据库命令是作为查询处理的.
  • desc: 客户端的标识符.这个字段可以与日志中的消息相关联.在上面的示例中,与连接相关的每个日志消息都会以 [conn3] 作为前缀,因此可以用它来筛选日志以获取相关信息.
  • locks: 描述操作所获取的锁的类型.
  • waitingForLock: 操作当前是否处于阻塞中并等待获取锁。
  • numYields: 操作释放锁以允许其他操作进行的次数。通常,搜索文档(查询、更新和删除)的任何操作都可能会让出锁。一个操作只有在有其他操作进入队列并等待获取它的锁时才会让出自己的锁。基本上,如果没有操作处于 waitingForLock 状态,则当前操作不会让出锁。
  • lockstats.timeAcquiringMicros: 操作为了获取锁所花费的时间。

终止操作

1
db.killOp(opid)

使用系统分析器

1
2
3
db.setProfilingLevel(2)
# 关闭分析器
db.setProfilingLevel(0)

日志

  • 如果要调试应用程序的某个特定问题,那么可以使用一些选项从日志中获取更多信息。可以运行setParameter 命令以更改日志级别,或者使用--setParameter选项将日志级别作为字符串传递,从而在启动时对其进行设置

    1
    2
    3
    db.adminCommand({"setParameter" : 1, "logLevel" : 3})
    # 关闭
    db.adminCommand({"setParameter" : 1, "logLevel" : 0})
  • 设置一个每天或每周轮换日志的定期任务,如果 MongoDB 启动时使用了--logpath选项,则向进程发送SIGUSR1信号会使其轮换日志。还可以使用 logRotate 命令来执行同样的操作

    1
    db.adminCommand({"logRotate" : 1})

版本升级

  • MongoDb版本升级,需要按照MongoDb升级指南进行操作,不能跨大版本升级,例如从4.0版本升级到7.0版本,则需要按这样的升级路径进行升级4.0-->4.2-->4.4-->5.0-->6.0-->7.0,升级的操作基本类似,基本分为五步(副本集形式4.0升级到4.2步骤为例)

    1. 确定当前featureCompatibilityVersion,确保所有的副本集的版本一致且为4.0

      1
      db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
    2. 升级各个副本集二级成员,先停止各个副本集二级成员服务,若用二进制文件部署,则需要替换二进制文件,若用Docker镜像部署,则需要更新Docker镜像

    3. 将主节点降级,执行rs.stepDown()退出主节点并强制选举新的主节点,然后升级主节点

    4. 使用rs.status()观察各个副本集的状态

    5. 最后更新featureCompatibilityVersion为此次升级的版本

      1
      db.adminCommand( { setFeatureCompatibilityVersion: "4.4" } )