Fork me on GitHub

MongoDB搭建Replica Set

Mongodb是NoSql数据库,它的存储方式是文档式存储,并不是Key-Value形式。Mongodb的三种集群方式的搭建:Replica Set/Sharding/Master-Slave。目前已不推荐使用主从模式;副本集提供的冗余和高可用,可以读写分离,出现故障时自动切换;分片支持部署非常大的数据集,并提高系统的吞吐量。本文只简单介绍一主一备一仲裁的Replica Set方式,主备节点存储数据,仲裁节点不存储数据。

准备环境

192.168.1.40 27011 centos7 PRIMARY(主)
192.168.1.50 27011 centos7 SECONDARY(备用节点)
192.168.1.60 27011 centos7 SECONDARY/ARBITER(备用/仲裁节点)

基本配置

1、创建数据文件夹

mkdir -p /data/mongodb/logs 
mkdir -p /data/mongodb/data
mkdir -p /data/mongodb/keyfile 

2、配置文件

集群各节点配置文件mongodb.conf如下:

systemLog:
  # 日志输出方式。file/syslog,如果是file,需指定path,默认是输出到标准输出流中
  destination: file
  # 日志文件的路径
  path: /data/mongodb/logs/mongodb.log
  # 是否追加方式写入日志,默认True
  logAppend: true
  # 为true时mongod/mongos 将会尝试减少日志的输出量
  quiet: false

net:
  # 设置端口
  port : 27011
  # 绑定ip地址访问mongodb,多个ip逗号分隔
  bindIp : 0.0.0.0

processManagement:
  # 是否以守护进程方式运行,默认false
  fork: true
  # pid文件路径 
  pidFilePath: /data/mongodb/mongo.pid

storage:
  journal:
    enabled: true
  # 数据库文件位置
  dbPath: /data/mongodb/data
  # 存储引擎,mmapv1/wiredTiger/inMemory 默认wiredTiger
  engine: wiredTiger
  wiredTiger:                                                              
      engineConfig:
         cacheSizeGB: 2                                                        
         # 为 true,将数据和索引会分成两个目录 index/collection 存储
         directoryForIndexes: false                                         
         # 日志压缩,默认压缩算法 snappy,可以选择 zlib
         journalCompressor: zlib
      collectionConfig:                                                       
         blockCompressor: zlib
      indexConfig:                                                                 
         prefixCompression: true

security:
  # 集群中members之间的认证模式,可选值为 “keyFile”、“sendKeyFile”、“sendX509”、“x509”,
  # 对mongod/mongos有效
  clusterAuthMode: keyFile
  # 当 clusterAuthMode 为 “keyFile” 时,此参数指定 keyfile 的位置,mongodb 需要有访问此文件的权限,
  # 一般x00即可
  keyFile: /data/mongodb/keyfile/jasonhzy.key 
  # disabled 或者 enabled, 表示是否开启用户访问控制(Access Control)
  # 在创建认证用户之前需先disabled,创建完用户之后即可设置enabled,否则不能创建用户
  authorization: enabled

replication:
   # oplogSize的大小,单位为MB,建议空闲磁盘的5%
   oplogSizeMB: 5120
   replSetName: jasonhzy
# 性能分析
operationProfiling:
   # 认定为查询速度缓慢的时间阈值,超过该时间的查询即为缓慢查询,会被记录到日志中, 单位毫秒,默认100
   slowOpThresholdMs: 100
   # operationProfiling模式: off关闭profiling/slowOp只包含慢操作日志/all记录所有操作, 默认off
   mode: off

3.启动mongodb

mongod -f /data/mongodb/mongodb.conf  #三台均启动

4、启动mongodb之后,客户端连接mongodb

mongo --port=27011

配置副本集

客户端连接mongodb之后进行配置

1、配置副本集及权重

> use admin
switched to db admin
> cfg = {
     _id : "jasonhzy", 
     members : [
            {_id:0,host:"192.168.1.40:27011", priority : 1},
            {_id:1,host:"192.168.1.50:27011", priority : 1},
            {_id:2,host:"192.168.1.60:27011", priority : 1, arbiterOnly:true} //仲裁节点
    ]
};
> rs.initiate(cfg); #使集群cfg配置生效
{ "ok" : 1 }

1)参数描述
_id:Replica Set的名称(需与配置文件mongodb.conf中replication.replSetName保持一致)
priority:表示优先级别,数值越大,表示是主节点
arbiterOnly:true表示仲裁节点

2)除上述之外,另一种添加节点方式:

添加secondary:rs.add({host: "192.168.1.40:27011", priority: 1 }) 
添加仲裁点:rs.addArb("192.168.1.60:27011")
          rs.add({host:"192.168.1.60:27011",arbiterOnly:true})

3)移除节点

rs.remove({host: "192.168.1.40:27017"})

2、查看状态

> rs.status()
{
    "set" : "jasonhzy",
    "date" : ISODate("2019-03-13T07:46:48.200Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    ....
    "members" : [
        {
            "_id" : 0,
            "name" : "192.168.1.40:27011",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 61467,
            "optime" : {
                "ts" : Timestamp(1553050134, 1),
                "t" : NumberLong(34)
            },
            "optimeDate" : ISODate("2019-03-20T02:48:54Z"),
            "syncingTo" : "",
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "",
            "electionTime" : Timestamp(1553049953, 1),
            "electionDate" : ISODate("2019-03-20T02:45:53Z"),
            "configVersion" : 5,
            "self" : true,
            "lastHeartbeatMessage" : ""
        },
        {
            "_id" : 1,
            "name" : "192.168.1.50:27011",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            ...
            "pingMs" : NumberLong(3)
            ...
        },
        {
            "_id" : 2,
            "name" : "192.168.1.60:27011",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            ...
            "pingMs" : NumberLong(6)
            ...
        }
    ],
    "ok" : 1
    ...
}  

参数描述:

self: 这个信息出现在执行rs.status()函数的成员信息中
stateStr: 用户描述服务器状态的字符串。stateStr有以下几下状态:
        1. PRIMARY:主节点,可读写
        2. SECONDARY:备份节点,默认不可读
        3. ARBITER: 仲裁者
        4. STARTUP:刚加入到复制集中,配置还未加载
        5. STARTUP2:配置已加载完,初始化状态(将长期处于该状态,直到同步完所有的数据和索引)
        6. RECOVERING:正在恢复,不适用读
        7. DOWN:节点不可到达
        8. UNKNOWN:未获取其他节点状态而不知是什么状态,一般发生在只有两个成员的架构,脑裂
        9. REMOVED:移除复制集
        10. ROLLBACK:数据回滚,在回滚结束时,转移到RECOVERING或SECONDARY状态
        11. FATAL:出错。查看日志grep “replSet FATAL”找出错原因,重新做同步
uptime: 从成员可到达一直到现在经历的时间,单位是秒。
optimeDate: 每个成员oplog最后一次操作发生的时间,这个时间是心跳报上来的,因此可能会存在延迟
lastHeartbeat: 当前服务器最后一次收到其他成员心跳的时间,如果网络故障等可能这个时间会大于2秒
pingMs: 心跳从当前服务器达到某个成员所花费的平均时间
errmsg: 成员在心跳请求中返回的状态信息,通过是一些状态信息,不全是错误信息。
state: 和stateStr是重复的,都表示成员状态,只是state是内部的叫法。
health: 表示是否服务器可达,可达是1,不可达是0
optime: 与optimeDate表达的信息是一样的,只是表示方式不同,一个是用新纪元开始的毫秒数表示的,
        一个是用一种更容易阅读的方式表示。
syncingTo: 表示当前服务器从哪个节点做同步。

由于rs.status()是从执行命令成员本身的角度得出的,由于网路等故障,这份报告可能不准确或者有些过时。

3、修改副本集节点

> cfg = rs.conf()
{
    "_id" : "jasonhzy",
    "version" : 5,
    "protocolVersion" : NumberLong(1),
    "members" : [
        {
            "_id" : 0,
            "host" : "192.168.1.40:27011",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 1,
            "host" : "192.168.1.50:27011",
            "arbiterOnly" : false,
            "hidden" : false,
            "priority" : 1,
            ...
        },
        {
            "_id" : 2,
            "host" : "192.168.1.60:27011",
            "arbiterOnly" : true,
            "hidden" : false,
            "priority" : 1,
            ...
        }
    ],
    "settings" : {
        "chainingAllowed" : true,
        "heartbeatIntervalMillis" : 2000,
        "heartbeatTimeoutSecs" : 10,
        "electionTimeoutMillis" : 10000,
        "catchUpTimeoutMillis" : -1,
        "catchUpTakeoverDelayMillis" : 30000,
        "getLastErrorModes" : {

        },
        "getLastErrorDefaults" : {
            "w" : 1,
            "wtimeout" : 0
        },
        "replicaSetId" : ObjectId("5c88b407092b73db0f7882b9")
    }
}
> cfg.members[0].host = "192.168.1.30:27011"
> rs.reconfig(cfg)  # 强制更新副本集配置 rs.reconfig(cfg, {"force": true}) 

参数描述:

version: 每修改一次集群的配置,副本集的version都会+1
protocolVersion: 协议版本
buildIndexes: 默认为true.用来表示同步的时候是否同步索引.如果要设置为false,则必须将priority设置为0
hidden: 表示节点是否为隐藏节点,true即隐藏节点将不对外服务,只是单纯的同步信息。rs.isMaster()方法将
   无法查看到隐藏节点的信息,但是可以使用rs.status()查看.设置隐藏节点必须首先将节点的priority设置为0     
priority: 表示权重,默认为1,如果将priority设置为0那这个节点将永远无法成为primary节点
slaveDelay: 复制延迟,这个是整数,单位为秒,用来设置复制的延时.一般用来防止误操作,延迟节点必须优先级
   设置为0, hidden设置为true,然后设置slaveDelay值
votes: 表示这个节点是否有权利进行投票.
tags: 表示标记,例如可以标记这个节点的作用等
chainingAllowed:表示是否允许链式复制,即某个secondary可以作为其它的secondary的源,默认是true.
heartbeatIntervalMillis:表示heartbeat的间隔时间,默认是没个两秒钟发送一个hearbeat包.
heartbeatTimeoutSecs:表示心跳检测超时时间,默认是10秒.
electionTimeoutMillis:表示选举超时时间,默认是10秒.

复制链问题:
数据复制时可以从主节点直接复制,也可以从备份节点开始复制,从备份节点复制可以形成复制链,如果想禁止复制链,即所有的数据都从主节点复制,可以通过chainingAllowed属性来设置,具体步骤如下:

config=rs.config()
config.settings.chainingAllowed=false
rs.reconfig(config) 

4、添加备份/延迟等节点
1)hidden(成员用于支持专用功能):设置true后此机器在读写中都不可见,并且不会被选举为Primary,但是可以投票,一般用于备份数据

rs.add({"host":"192.168.1.100:27011", "priority":0, "hidden":true})

2)Delayed(成员用于支持专用功能):可以指定一个时间延迟从primary节点同步数据。主要用于处理误删除数据马上同步到从节点导致的不一致问题。

rs.add({"host":"192.168.1.100:27011", "priority":0, "hidden":true, "slaveDelay":60}) #单位 s

3)Secondary-Only:不能成为primary节点,只能作为secondary副本节点,防止一些性能不高的节点成为主节点。
4)Non-Voting:没有选举权的secondary节点,纯粹的备份数据节点。

具体如下:

角色 primary(能否) 客户端可见 参与投票 延迟同步 复制数据
Default
Secondary-Only
Hidden
Delayed
Arbiters
Non-Voting

读写分离

mongodb默认是从主节点读写数据的,副本节点上不允许读,需要设置副本节点可以读:

jasonhzy:SECONDARY> db.getMongo().setSlaveOk();  # 或 rs.slaveOk();

支持的几种读取策略(Read Preferences)如下:

模式 描述
primary 主节点,默认模式,读操作只在主节点,如果主节点不可用,报错或者抛出异常。
primaryPreferred 首选主节点,大多情况下读操作在主节点,如果主节点不可用,如故障转移,读操作在从节点。
secondary 从节点,读操作只在从节点, 如果从节点不可用,报错或者抛出异常。
secondaryPreferred 首选从节点,大多情况下读操作在从节点,特殊情况(如单主节点架构)读操作在主节点。
nearest 最邻近节点,读操作在最邻近的成员,可能是主节点或者从节点

集群安全认证

集群之间的安全认证

集群之间的复制增加keyFile认证

#生成key
openssl rand -base64 500 > /data/mongodb/keyfile/jasonhzy.key
#修改权限
chmod 600 /data/mongodb/keyfile/jasonhzy.key

将该key放到集群中每一台的机器上,且必须保持一致

修改配置

在mongodb.conf启动配置文件中增加配置项:

security:
  keyFile=/data/mongodb/keyfile/jasonhzy.key

主库创建用户

MongoDB内建角色

1、数据库用户角色

read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库

2、数据库管理员角色

dbAdmin:允许用户进行索引创建、删除,查看统计或访问system.profile,但没有角色和用户管理的权限。
userAdmin:提供了在当前数据库中创建和修改角色和用户的能力。
dbOwner: 提供对数据库执行任何管理操作的能力。这个角色组合了readWrite、dbAdmin和userAdmin角色
        授予的特权。

3、集群管理角色

clusterAdmin : 提供最强大的集群管理访问。组合clusterManager、clusterMonitor和hostManager角色的能力,
            还提供了dropDatabase操作。
clusterManager : 在集群上提供管理和监视操作。可以访问配置和本地数据库,这些数据库分别用于分片和复制。
clusterMonitor : 提供对监控工具的只读访问,例如MongoDB云管理器和Ops管理器监控代理。
hostManager : 提供监视和管理服务器的能力。

4、备份恢复角色

backup : 提供备份数据所需的能力,使用MongoDB云管理器备份代理、Ops管理器备份代理或使用mongodump
restore : 提供使用mongorestore恢复数据所需的能力

5、所有数据库角色

readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限 
readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限 
userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限 
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限

6、超级用户角色

root:提供对readWriteAnyDatabase、dbAdminAnyDatabase、userAdminAnyDatabase、
    clusterAdmin、restore和backup的所有资源的访问

7、内部角色

__system : 提供对数据库中任何对象的任何操作的特权

创建用户

1、创建超级用户角色root

jasonhzy:PRIMARY> use admin
jasonhzy:PRIMARY> db.createUser(  
    {  
        user:"root",   
        pwd:"123456",  
        roles:[{role:"root",db:"admin"}]
    }  
);

以”forum”数据库为例,创建相应的角色用户:

use forum //创建数据库

2、创建数据库管理员

jasonhzy:PRIMARY> use forum
jasonhzy:PRIMARY> db.createUser({
    user: "admin",
    pwd: "123456",
    roles: [{ role: "dbOwner", db: "forum" }]
});

3、创建数据库用户

1)创建一个可读写的用户

jasonhzy:PRIMARY> use forum
jasonhzy:PRIMARY> db.createUser({
    user: "rw",
    pwd: "123456",
    roles: [{ role: "readWrite", db: "forum" }]
});

2)创建一个只读用户

jasonhzy:PRIMARY> use forum
jasonhzy:PRIMARY> db.createUser({
    user: "read",
    pwd: "123456",
    roles: [{ role: "read", db: "forum" }]
});

安全停止MongoDB进程

MongoDB进程如果直接kill掉进程或机器突然断电等都会可能MongoDB造成数据损坏,所以在停止MongoDB服务的时候,不要用kill -9 或 killall -9 直接干掉MongoDB的进程·

要安全停止可以有两种信号:sigint信号,或者sigterm信号

如何安全kill停止:

kill -2 8888 #其中 8888 为mongod进程号,该进程号可以通过 ps -axu |grep mongo 获取; -2 表示向mongod进程发送sigint信号
kill -4 8888 #其中 8888 为mongod进程号 ,该进程号可以通过 ps -axu |grep mongo 获取; -4 表示向mongod进程发送sigterm信号

mongod进程收到sigint信号或者sigterm信号,会做一些处理:关闭所有打开的连接,将内存数据强制刷新到磁盘,当前的操作执行完毕后在安全停止服务。

推荐停止方法:登录控制台使用mongod命令shutdown的安全停止方式:

mongo -host 127.0.0.1 -port 27011
jasonhzy:PRIMARY> use admin;                     --使用管理员数据库
jasonhzy:PRIMARY> db.shutdownServer();           --安全关闭MongoDB

常用命令

1、查看oplog状态: rs.printReplicationInfo()

jasonhzy:PRIMARY> rs.printReplicationInfo()
configured oplog size:   51200MB
log length start to end: 720145secs (200.04hrs)
oplog first event time:  Wed Mar 13 2019 15:40:55 GMT+0800 (CST)
oplog last event time:   Thu Mar 21 2019 23:43:20 GMT+0800 (CST)
now:                     Thu Mar 21 2019 23:43:29 GMT+0800 (CST)

字段说明:

configured oplog size:配置的oplog文件大小。
log length start to end:oplog日志的启用时间段。
oplog first event time:第一个事务日志的产生时间。
oplog last event time:最后一个事务日志的产生时间。
now:现在的时间。

2、查看复制节点及延迟: rs.printSlaveReplicationInfo()

jasonhzy:PRIMARY> rs.printSlaveReplicationInfo()
source: 192.168.1.50:27011
    syncedTo: Fri Mar 22 2019 11:31:57 GMT+0800 (CST)
    0 secs (0 hrs) behind the primary 
source: 192.168.1.60:27011
    syncedTo: Fri Mar 22 2019 11:31:57 GMT+0800 (CST)
    0 secs (0 hrs) behind the primary 

3、查看服务状态详情: db.serverStatus()

参考资料

数据库高可用和分区解决方案 - MongoDB 篇
Mongodb主从复制/副本集/分片集群介绍

轻轻的我走了,正如我轻轻的来