MongoShake——基于MongoDB的跨数据中心的数据复制平台

摘要: MongoShake是基于MongoDB的通用型平台服务,作为数据连通的桥梁,打通各个闭环节点的通道。通过MongoShake的订阅消费,可以灵活对接以适应不同场景,例如日志订阅、数据中心同步、监控审计等。其中,集群数据同步作为核心应用场景,能够灵活实现灾备和多活的业务场景。

背景

在当前的数据库系统生态中,大部分系统都支持多个节点实例间的数据同步机制,如Mysql Master/Slave主从同步,Redis AOF主从同步等,MongoDB更是支持3节点及以上的副本集同步,上述机制很好的支撑了一个逻辑单元的数据冗余高可用。

跨逻辑单元,甚至跨单元、跨数据中心的数据同步,在业务层有时候就显得很重要,它使得同城多机房的负载均衡,多机房的互备,甚至是异地多数据中心容灾和多活成为可能。由于目前MongoDB副本集内置的主从同步对于这种业务场景有较大的局限性,为此,我们开发了MongoShake系统,可以应用在实例间复制,机房间、跨数据中心复制,满足灾备和多活需求。

另外,数据备份是作为MongoShake核心但不是唯一的功能。MongoShake作为一个平台型服务,用户可以通过对接MongoShake,实现数据的订阅消费来满足不同的业务场景。

简介

MongoShake是一个以golang语言进行编写的通用的平台型服务,通过读取MongoDB集群的Oplog操作日志,对MongoDB的数据进行复制,后续通过操作日志实现特定需求。日志可以提供很多场景化的应用,为此,我们在设计时就考虑了把MongoShake做成通用的平台型服务。通过操作日志,我们提供日志数据订阅消费PUB/SUB功能,可通过SDK、Kafka、MetaQ等方式灵活对接以适应不同场景(如日志订阅、数据中心同步、Cache异步淘汰等)。集群数据同步是其中核心应用场景,通过抓取oplog后进行回放达到同步目的,实现灾备和多活的业务场景。

应用场景举例

  1. MongoDB集群间数据的异步复制,免去业务双写开销。
  2. MongoDB集群间数据的镜像备份(当前1.0开源版本支持受限)
  3. 日志离线分析
  4. 日志订阅
  5. 数据路由。根据业务需求,结合日志订阅和过滤机制,可以获取关注的数据,达到数据路由的功能。
  6. Cache同步。日志分析的结果,知道哪些Cache可以被淘汰,哪些Cache可以进行预加载,反向推动Cache的更新。
  7. 基于日志的集群监控

功能介绍

MongoShake从源库抓取oplog数据,然后发送到各个不同的tunnel通道。现有通道类型有:

  1. Direct:直接写入目的MongoDB

  2. RPC:通过net/rpc方式连接

  3. TCP:通过tcp方式连接

  4. File:通过文件方式对接

  5. Kafka:通过Kafka方式对接

  6. Mock:用于测试,不写入tunnel,抛弃所有数据

消费者可以通过对接tunnel通道获取关注的数据,例如对接Direct通道直接写入目的MongoDB,或者对接RPC进行同步数据传输等。此外,用户还可以自己创建自己的API进行灵活接入。下面2张图给出了基本的架构和数据流。

未分类

未分类

MongoShake对接的源数据库支持单个mongod,replica set和sharding三种模式。目的数据库支持mongod和mongos。如果源端数据库为replica set,我们建议对接备库以减少主库的压力;如果为sharding模式,那么每个shard都将对接到MongoShake并进行并行抓取。对于目的库来说,可以对接多个mongos,不同的数据将会哈希后写入不同的mongos。

并行复制

MongoShake提供了并行复制的能力,复制的粒度选项(shard_key)可以为:id,collection或者auto,不同的文档或表可能进入不同的哈希队列并发执行。id表示按文档进行哈希;collection表示按表哈希;auto表示自动配置,如果有表存在唯一键,则退化为collection,否则则等价于id。

HA方案

MongoShake定期将同步上下文进行存储,存储对象可以为第三方API(注册中心)或者源库。目前的上下文内容为“已经成功同步的oplog时间戳”。在这种情况下,当服务切换或者重启后,通过对接该API或者数据库,新服务能够继续提供服务。

此外,MongoShake还提供了Hypervisor机制用于在服务挂掉的时候,将服务重新拉起。

过滤

提供黑名单和白名单机制选择性同步db和collection。

压缩

支持oplog在发送前进行压缩,目前支持的压缩格式有gzip, zlib, 或deflate。

Gid

一个数据库的数据可能会包含不同来源:自己产生的和从别处复制的数据。如果没有相应的措施,可能会导致数据的环形复制,比如A的数据复制到B,又被从B复制到A,导致服务产生风暴被打挂了。或者从B回写入A时因为唯一键约束写入失败。从而导致服务的不稳定。

在阿里云上的MongoDB版本中,我们提供了防止环形复制的功能。其主要原理是,通过修改MongoDB内核,在oplog中打入gid标识当前数据库信息,并在复制过程中通过op_command命令携带gid信息,那么每条数据都有来源信息。如果只需要当前数据库产生的数据,那么只抓取gid等于该数据库id的oplog即可。所以,在环形复制的场景下,MongoShake从A数据库抓取gid等于id_A(A的gid)的数据,从B数据库抓取gid等于id_B(B的gid)的数据即可解决这个问题。

说明:由于MongoDB内核gid部分的修改尚未开源,所以开源版本下此功能受限,但在阿里云MongoDB版本已支持。这也是为什么我们前面提到的“MongoDB集群间数据的镜像备份”在目前开源版本下功能受限的原因。

Checkpoint

MongShake采用了ACK机制确保oplog成功回放,如果失败将会引发重传,传输重传的过程类似于TCP的滑动窗口机制。这主要是为了保证应用层可靠性而设计的,比如解压缩失败等等。为了更好的进行说明,我们先来定义几个名词:

LSN(Log Sequence Number),表示已经传输的最新的oplog序号。

LSN_ACK(Acked Log Sequence Number),表示已经收到ack确认的最大LSN,即写入tunnel成功的LSN。

LSN_CKPT(Checkpoint Log Sequence Number),表示已经做了checkpoint的LSN,即已经持久化的LSN。

LSN、LSN_ACK和LSN_CKPT的值均来自于Oplog的时间戳ts字段,其中隐含约束是:LSN_CKPT<=LSN_ACK<=LSN

未分类

如上图所示,LSN=16表示已经传输了16条oplog,如果没有重传的话,下次将传输LSN=17;LSN_ACK=13表示前13条都已经收到确认,如果需要重传,最早将从LSN=14开始;LSN_CKPT=8表示已经持久化checkpoint=8。持久化的意义在于,如果此时MongoShake挂掉重启后,源数据库的oplog将从LSN_CKPT位置开始读取而不是从头LSN=1开始读。因为oplog DML的幂等性,同一数据多次传输不会产生问题。但对于DDL,重传可能会导致错误。

排障和限速

MongoShake对外提供Restful API,提供实时查看进程内部各队列数据的同步情况,便于问题排查。另外,还提供限速功能,方便用户进行实时控制,减轻数据库压力。

冲突检测

目前MongoShake支持表级别(collection)和文档级别(id)的并发,id级别的并发需要db没有唯一索引约束,而表级别并发在表数量小或者有些表分布非常不均匀的情况下性能不佳。所以在表级别并发情况下,需要既能均匀分布的并发,又能解决表内唯一键冲突的情况。为此,如果tunnel类型是direct时候,我们提供了写入前的冲突检测功能。

目前索引类型仅支持唯一索引,不支持前缀索引、稀疏索引、TTL索引等其他索引。

冲突检测功能的前提需要满足两个前提约束条件:

  1. MongoShake认为同步的MongoDB Schema是一致的,也不会监听Oplog的System.indexes表的改动

  2. 冲突索引以Oplog中记录的为准,不以当前MongoDB中索引作为参考。

另外,MongoShake在同步过程中对索引的操作可能会引发异常情况:

  1. 正在创建索引。如果是后台建索引,这段时间的写请求是看不到该索引的,但内部对该索引可见,同时可能会导致内存使用率会过高。如果是前台建索引,所有用户请求是阻塞的,如果阻塞时间过久,将会引发重传。

  2. 如果目的库存在的唯一索引在源库没有,造成数据不一致,不进行处理。

  3. oplog产生后,源库才增加或删除了唯一索引,重传可能导致索引的增删存在问题,我们也不进行处理。

为了支持冲突检测功能,我们修改了MongoDB内核,使得oplog中带入uk字段,标识涉及到的唯一索引信息,如:

{
    "ts" : Timestamp(1484805725, 2),
    "t" : NumberLong(3),
    "h" : NumberLong("-6270930433887838315"),
    "v" : 2,
    "op" : "u",
    "ns" : "benchmark.sbtest10",
    "o" : { "_id" : 1, "uid" : 1111, "other.sid":"22222", "mid":8907298448, "bid":123 }
    "o2" : {"_id" : 1}
    "uk" : {
            "uid": "1110"
            "mid^bid": [8907298448, 123]
            "other.sid_1": "22221"
    }
}

uk下面的key表示唯一键的列名,key用“^”连接的表示联合索引,上面记录中存在3个唯一索引:uid、mid和bid的联合索引、other.sid_1。value在增删改下具有不同意义:如果是增加操作,则value为空;如果是删除或者修改操作,则记录删除或修改前的值。

具体处理流程如下:将连续的k个oplog打包成一个batch,流水式分析每个batch之内的依赖,划分成段。如果存在冲突,则根据依赖和时序关系,将batch切分成多个段;如果不存在冲突,则划分成一个段。然后对段内进行并发写入,段与段之间顺序写入。段内并发的意思是多个并发线程同时对段内数据执行写操作,但同一个段内的同一个id必须保证有序;段之间保证顺序执行:只有前面一个段全部执行完毕,才会执行后续段的写入。

如果一个batch中,存在不同的id的oplog同时操作同一个唯一键,则认为这些oplog存在时序关系,也叫依赖关系。我们必须将存在依赖关系的oplog拆分到2个段中。

MongoShake中处理存在依赖关系的方式有2种:

(1) 插入barrier

通过插入barrier将batch进行拆分,每个段内进行并发。举个例子,如下图所示:

未分类

ID表示文档id,op表示操作,i为插入,u为更新,d为删除,uk表示该文档下的所有唯一键, uk={a:3} => uk={a:1}表示将唯一键的值从a=3改为a=1,a为唯一键。

在开始的时候,batch中有9条oplog,通过分析uk关系对其进行拆分,比如第3条和第4条,在id不一致的情况下操作了同一个uk={a:3},那么第3条和第4条之间需要插入barrier(修改前或者修改后无论哪个相同都算冲突),同理第5条和第6条,第6条和第7条。同一个id操作同一个uk是允许的在一个段内是允许的,所以第2条和第3条可以分到同一个段中。拆分后,段内根据id进行并发,同一个id仍然保持有序:比如第一个段中的第1条和第2,3条可以进行并发,但是第2条和第3条需要顺序执行。

(2) 根据关系依赖图进行拆分

每条oplog对应一个时间序号N,那么每个序号N都可能存在一个M使得:

如果M和N操作了同一个唯一索引的相同值,且M序号小于N,则构建M到N的一条有向边。
如果M和N的文档ID相同且M序号小于N,则同样构建M到N的一条有向边。
由于依赖按时间有序,所以一定不存在环。
所以这个图就变成了一个有向无环图,每次根据拓扑排序算法并发写入入度为0(没有入边)的点即可,对于入度非0的点等待入度变为0后再写入,即等待前序结点执行完毕后再执行写入。

下图给出了一个例子:一共有10个oplog结点,一个横线表示文档ID相同,右图箭头方向表示存在唯一键冲突的依赖关系。那么,该图一共分为4次执行:并发处理写入1,2,4,5,然后是3,6,8,其次是7,10,最后是9。

未分类

未分类

说明:由于MongoDB中冲突检测uk部分的修改尚未开源,所以开源版本下此功能受限,但在阿里云MongoDB版本已支持。

架构和数据流

未分类

上图展示了MongoShake内部架构和数据流细节。总体来说,整个MongoShake可以大体分为3大部分:Syncer、Worker和Replayer,其中Replayer只用于tunnel类型为direct的情况。

Syncer负责从源数据库拉取数据,如果源是Mongod或者ReplicaSet,那么Syncer只有1个,如果是Sharding模式,那么需要有多个Syncer与Shard一一对应。在Syncer内部,首先fetcher用mgo.v2库从源库中抓取数据然后batch打包后放入PendingQueue队列,deserializer线程从PendingQueue中抓取数据进行解序列化处理。Batcher将从LogsQueue中抓取的数据进行重新组织,将前往同一个Worker的数据聚集在一起,然后hash发送到对应Worker队列。

Worker主要功能就是从WorkerQueue中抓取数据,然后进行发送,由于采用ack机制,所以会内部维持几个队列,分别为未发送队列和已发送队列,前者存储未发送的数据,后者存储发送但是没有收到ack确认的数据。发送后,未发送队列的数据会转移到已发送队列;收到了对端的ack回复,已发送队列中seq小于ack的数据将会被删除,从而保证了可靠性。

Worker可以对接不同的Tunnel通道,满足用户不同的需求。如果通道类型是direct,那么将会对接Replayer进行直接写入目的MongoDB操作,Worker与Replayer一一对应。首先,Replayer将收到的数据根据冲突检测规则分发到不同的ExecutorQueue,然后executor从队列中抓取进行并发写入。为了保证写入的高效性,MongoShake在写入前还会对相邻的相同Operation和相同Namespace的Oplog进行合并。

用户使用案例

高德地图 App是国内首屈一指的地图及导航应用,阿里云MongoDB数据库服务为该应用提供了部分功能的存储支撑,存储亿级别数据。现在高德地图使用国内双中心的策略,通过地理位置等信息路由最近中心提升服务质量,业务方(高德地图)通过用户路由到三个城市数据中心,如下图所示,机房数据之间无依赖计算。

未分类

这三个城市地理上从北到南横跨了整个中国 ,这对多数据中心如何做好复制、容灾提出了挑战,如果某个地域的机房、网络出现问题,可以平滑的将流量切换到另一个地方,做到用户几乎无感知?

目前我们的策略是,拓扑采用机房两两互联方式,每个机房的数据都将同步到另外两个机房。然后通过高德的路由层,将用户请求路由到不同的数据中心,读写均发送在同一个数据中心,保证一定的事务性。然后再通过MongoShake,双向异步复制两个数据中心的数据,这样保证每个数据中心都有全量的数据(保证最终一致性) 。任意机房出现问题,另两个机房中的一个可以通过切换后提供读写服务。下图展示了城市1和城市2机房的同步情况。

未分类

遇到某个单元不能访问的问题,通过MongoShake对外开放的Restful管理接口,可以获得各个机房的同步偏移量和时间戳,通过判断采集和写入值即可判断异步复制是否在某个时间点已经完成。再配合业务方的DNS切流,切走单元流量并保证原有单元的请求在新单元是可以读写的,如下图所示。

未分类

性能测试数据

具体测试数据请参考性能测试文档。

开源

MongoShake的github地址https://github.com/aliyun/mongo-shake?spm=a2c4e.11153940.blogcont603329.16.12416563VjpGZE , 欢迎加入!

后续

MongoShake将会长期维护,大版本和小版本将会进行持续迭代。欢迎提问留言以及加入一起进行开源开发。

另外,阿里云MongoDB团队招人,包括云平台开发和数据库内核开发,语言不限,欢迎投递简历以及来信咨询:[email protected]

mongodb集群故障转移实践

简介

NOSQL有这些优势:

  1. 大数据量,可以通过廉价服务器存储大量的数据,轻松摆脱传统mysql单表存储量级限制。
  2. 高扩展性,Nosql去掉了关系数据库的关系型特性,很容易横向扩展,摆脱了以往老是纵向扩展的诟病。
  3. 高性能,Nosql通过简单的key-value方式获取数据,非常快速。还有NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多。
  4. 灵活的数据模型,NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。
  5. 高可用,NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如mongodb通过mongos、mongo分片就可以快速配置出高可用配置。
  6. 支持查询、聚合、完全索引,包含内部对象
  7. 支持复制和故障转移、自动恢复
  8. 易扩展

在nosql数据库里,大部分的查询都是键值对(key、value)的方式。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中最像关系数据库的。支持类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。所以这个非常方便,我们可以用sql操作MongoDB,从关系型数据库迁移过来,开发人员学习成本会大大减少。如果再对底层的sql API做一层封装,开发基本可以感觉不到mongodb和关系型数据库的区别。

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

安装mongodb

安装环境

  • 操作系统:Centos7.2

  • mongodb版本: v3.6.1

下载安装

wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-amazon-3.6.1.tgz
tar zxvf mongodb-linux-x86_64-amazon-3.6.1.tgz 
mv  /root/mongodb-linux-x86_64-amazon-3.6.1 /usr/local/mongodb/

创建数据/日志目录

mkdir -p /data/mongodb/{data, logs}

mkdir /data/mongodb/data/mongod
touch /data/mongodb/logs/mongo.logs

创建配置文件

mkdir /usr/local/mongodb/config
cd  /usr/local/mongodb/config && touch mongo.conf

配置文件

1.普通配置文件示例

dbpath=/data/mongodb/data/mongod
logpath=/data/mongodb/logs/mongo.log
logappend=true
replSet=mongo-rs
bind_ip=0.0.0.0
port=27017
fork=true
journal=true

mongodb3.x版本后就是要yaml语法格式的配置文件,下面是yaml配置文件格式如下:
官方yaml配置文件选项参考:https://docs.mongodb.org/manual/reference/configuration-options/#configuration-file
注意:只能使用空格,不支持tab键

2.yaml格式配置文件示例

storage:
    dbPath: /data/mongodb/data/mongod
    journal:
        enabled: true
systemLog:
    destination: file
    path: /data/mongodb/logs/mongo.log
    logAppend: true
    logRotate: rename
net:
    bindIp: 0.0.0.0
    port: 27017
processManagement:
    pidFilePath: /var/run/pid/mongodb.pid
    fork: true
replication:
    oplogSizeMB: 20480
    replSetName: mongo-rs

配置文件参数说明

1.基本参数

--quiet     # 安静输出
--port arg  # 指定服务端口号,默认端口27017
--bind_ip arg   # 绑定服务IP,若绑定127.0.0.1,则只能本机访问,不指定默认本地所有IP
--logpath arg   # 指定MongoDB日志文件,注意是指定文件不是目录
--logappend     # 使用追加的方式写日志
--pidfilepath arg   # PID File 的完整路径,如果没有设置,则没有PID文件
--keyFile arg   # 集群的私钥的完整路径,只对于Replica Set 架构有效
--unixSocketPrefix arg  # UNIX域套接字替代目录,(默认为 /tmp)
--fork  # 以守护进程的方式运行MongoDB,创建服务器进程
--auth  # 启用验证
--cpu   # 定期显示CPU的CPU利用率和iowait
--dbpath arg    # 指定数据库路径
--diaglog arg   # diaglog选项 0=off 1=W 2=R 3=both 7=W+some reads
--directoryperdb    # 设置每个数据库将被保存在一个单独的目录
--journal   # 启用日志选项,MongoDB的数据操作将会写入到journal文件夹的文件里
--journalOptions arg    # 启用日志诊断选项
--ipv6  # 启用IPv6选项
--jsonp     # 允许JSONP形式通过HTTP访问(有安全影响)
--maxConns arg  # 最大同时连接数 默认2000
--noauth    # 不启用验证
--nohttpinterface   # 关闭http接口,默认关闭27018端口访问
--noprealloc    # 禁用数据文件预分配(往往影响性能)
--noscripting   # 禁用脚本引擎
--notablescan   # 不允许表扫描
--nounixsocket  # 禁用Unix套接字监听
--nssize arg (=16)  # 设置信数据库.ns文件大小(MB)
--objcheck  # 在收到客户数据,检查的有效性,
--profile arg   # 档案参数 0=off 1=slow, 2=all
--quota     # 限制每个数据库的文件数,设置默认为8
--quotaFiles arg    # number of files allower per db, requires --quota
--rest  # 开启简单的rest API
--repair    # 修复所有数据库run repair on all dbs
--repairpath arg    # 修复库生成的文件的目录,默认为目录名称dbpath
--slowms arg (=100)     # value of slow for profile and console log
--smallfiles    # 使用较小的默认文件
--syncdelay arg (=60)   # 数据写入磁盘的时间秒数(0=never,不推荐)
--sysinfo   # 打印一些诊断系统信息
--upgrade   # 如果需要升级数据库

2.Replicaton 参数

--fastsync  # 从一个dbpath里启用从库复制服务,该dbpath的数据库是主库的快照,可用于快速启用同步
--autoresync    # 如果从库与主库同步数据差得多,自动重新同步,
--oplogSize arg     # 设置oplog的大小(MB)

3.主/从参数

--master    # 主库模式
--slave     # 从库模式
--source arg    # 从库 端口号
--only arg  # 指定单一的数据库复制
--slavedelay arg    # 设置从库同步主库的延迟时间

4.Replica set(副本集)选项

--replSet arg   # 设置副本集名称 

Sharding(分片)选项
--configsvr     # 声明这是一个集群的config服务,默认端口27019,默认目录/data/configdb
--shardsvr  # 声明这是一个集群的分片,默认端口27018
--noMoveParanoia    # 关闭偏执为moveChunk数据保存

启动

mongod –quiet -f /usr/local/mongodb/config/mongo.conf
配置文件里设置里fork:true,所以会在后台启动,值得注意的是,用到了”–fork”参数就必须启用”–logpath”参数,如不指定配置文件启动,如下:

mongod --dbpath=/data/mongodb/data/mongod --fork --logpath=/data/mongodb/logs/mongo.logs

集群搭建

官方不建议再使用主从集群模式,推荐的集群方式是Replica Set(副本集),主从模式其实就是一个单副本的应用,没有很好的扩展性和容错性。而副本集具有多个副本保证了容错性,就算一个副本挂掉了还有很多副本存在,并且解决了上面第一个问题“主节点挂掉了,整个集群内会自动切换”。

副本集的设计结构

未分类

由图可以看到客户端连接到整个副本集,不关心具体哪一台机器是否挂掉。主服务器负责整个副本集的读写,副本集定期同步数据备份,一但主节点挂掉,副本节点就会选举一个新的主服务器,这一切对于应用服务器不需要关心。

未分类

配置步骤

准备三台机器

172.29.142.17  主
172.29.142.18  备
172.28.226.199 仲裁

按照第二步安装依次在三台机器上安装并启动

/usr/local/mongodb/bin/mongod --quiet -f /usr/local/mongodb/config/mongo.conf

初始化集群配置

三台服务启动并不能表示他们在一个集群,因此需要将集群初始化。连接任意一个节点(不要是仲裁点),执行如下:

rs.initiate({
 _id:"mongo-rs", #集群名称 
 members:[ {_id:0,host:'172.29.142.18:27017',priority:2}, #主
 {_id:1,host:'172.29.142.17:27017',priority:1}, #备
 {_id:2,host:'172.28.226.199:27017',arbiterOnly:true}]  #仲裁节点
}) 

成功上面会返回OK,然后查看集群状态,下面是在备节点上执行的

rs.status()

返回集群的名称和members信息,如:

{
    "set" : "mongo-rs",
    "date" : ISODate("2018-06-26T14:56:08.032Z"),
    "myState" : 2,
    "term" : NumberLong(2),
    "syncingTo" : "172.29.142.18:27017",
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1530024958, 1),
            "t" : NumberLong(2)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1530024958, 1),
            "t" : NumberLong(2)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1530024958, 1),
            "t" : NumberLong(2)
        }
    },
    "members" : [ 
        {
            "_id" : 0,
            "name" : "172.29.142.18:27017",
            "health" : 1.0,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 382251,
            "optime" : {
                "ts" : Timestamp(1530024958, 1),
                "t" : NumberLong(2)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1530024958, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2018-06-26T14:55:58.000Z"),
            "optimeDurableDate" : ISODate("2018-06-26T14:55:58.000Z"),
            "lastHeartbeat" : ISODate("2018-06-26T14:56:07.329Z"),
            "lastHeartbeatRecv" : ISODate("2018-06-26T14:56:06.453Z"),
            "pingMs" : NumberLong(0),
            "electionTime" : Timestamp(1529642739, 1),
            "electionDate" : ISODate("2018-06-22T04:45:39.000Z"),
            "configVersion" : 1
        }, 
        {
            "_id" : 1,
            "name" : "172.29.142.17:27017",
            "health" : 1.0,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 382552,
            "optime" : {
                "ts" : Timestamp(1530024958, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2018-06-26T14:55:58.000Z"),
            "syncingTo" : "172.29.142.18:27017",
            "configVersion" : 1,
            "self" : true
        }, 
        {
            "_id" : 2,
            "name" : "172.28.226.199:27017",
            "health" : 1.0,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 168617,
            "lastHeartbeat" : ISODate("2018-06-26T14:56:06.895Z"),
            "lastHeartbeatRecv" : ISODate("2018-06-26T14:56:04.092Z"),
            "pingMs" : NumberLong(35),
            "configVersion" : 1
        }
    ],
    "ok" : 1.0
}

View Code

返回参数说明

“health” : 1, #代表机器正常
“stteStr” : “PRIMARY”, #代表是主节点,可读写,其中有以下几下状态:

STARTUP:刚加入到复制集中,配置还未加载
STARTUP2:配置已加载完,初始化状态
RECOVERING:正在恢复,不适用读
ARBITER: 仲裁者
DOWN:节点不可到达
UNKNOWN:未获取其他节点状态而不知是什么状态,一般发生在只有两个成员的架构,脑裂
REMOVED:移除复制集
ROLLBACK:数据回滚,在回滚结束时,转移到RECOVERING或SECONDARY状态
FATAL:出错。查看日志grep “replSet FATAL”找出错原因,重新做同步
PRIMARY:主节点
SECONDARY:备份节点

测试副本集数据复制

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

repset:SECONDARY> db.getMongo().setSlaveOk();

这个很好测试,直接在主节点插入一条数据,在备节点查询即可

或者可以使用客户端以集群模式连接mongo集群:

未分类

点Test 测试连接:

未分类

三个节点的数据是同步的。

测试副本集故障转移功能

1.查看集群当前状态,如上返回

当前172.29.142.18是Primary, 172.29.142.17是Secondary

未分类

2.停掉主节点172.29.142.18,查看另两台的选票结果

未分类

此时17变成了主节点,原先的仲裁节点不变,重新启动第一次的Primary,则主节点又发生变化,不再截图,整个过程业务是不中断的。只要有一台可用即可。

Nodejs连接mongo集群示例

这里强烈不推荐连接单台mongo服务,因为如果一个mongo节点挂掉,业务就挂了,连接集群的话有一台可用就行。

下面举了个nodejs连接mongo集群的示例:

const mongoose = require('mongoose');
let url = "mongodb://172.29.142.17:27017/testdb,mongodb://172.29.142.18:27017/testdb,mongodb://172.28.226.199:27017/testdb";
let options = {
  "replset": {
    "ha": true,
    "haInterval": 1000,
    "replicaSet": "mongo-rs",
    "connectWithNoPrimary": true,
    "auto_reconnect": true,
    "socketOptions": {
      "keepAlive": 120,
      connectTimeoutMS: 30000
    }
  }
}


mongoose.connect(url, options).connection
  .on('error', function (error) {
    console.log('mongo 连接错误', error)
  }).on('disconnected', mongoConnect).once('open', function () {
    console.log('mongo 连接成功');
  })

MongoDB客户端命令总结

一、常用命令

1、登录命令行(40008为自定义的端口)

mongo --port 40008

2、删除当前使用数据库

db.dropDatabase();

3、从指定主机上克隆数据库

db.cloneDatabase(“127.0.0.1”); 将指定机器上的数据库的数据克隆到当前数据库

4、从指定的机器上复制指定数据库数据到某个数据库

db.copyDatabase("mydb", "temp", "127.0.0.1");将本机的mydb的数据复制到temp数据库中

5、查看当前使用的数据库

db.getName();

6、显示当前db状态

db.stats();

7、当前db版本

db.version();

8、查看当前db的链接机器地址

db.getMongo();

9、显示当前所有用户

show users;

二、聚集集合查询

1、查询所有记录

db.userInfo.find();

相当于:select* from userInfo;

注:默认每页显示20条记录,当显示不下的情况下,可以用it迭代命令查询下一页数据。键入it命令不能带“;”,但是你可以设置每页显示数据的大小,用DBQuery.shellBatchSize= 50;这样每页就显示50条记录了。

2、查询去掉后的当前聚集集合中的某列的重复数据

db.userInfo.distinct("name");

会过滤掉name中的相同数据
相当于:select distict name from userInfo;

3、查询age = 22的记录

db.userInfo.find({"age": 22});

相当于: select * from userInfo where age = 22;

4、查询age > 22的记录

db.userInfo.find({age: {$gt: 22}});

相当于:select * from userInfo where age >22;

5、查询age < 22的记录

db.userInfo.find({age: {$lt: 22}});

相当于:select * from userInfo where age <22;

6、查询age >= 25的记录

db.userInfo.find({age: {$gte: 25}});

相当于:select * from userInfo where age >= 25;

7、查询age <= 25的记录

db.userInfo.find({age: {$lte: 25}});

8、查询age >= 23 并且 age <= 26

db.userInfo.find({age: {$gte: 23, $lte: 26}});

9、查询name中包含 mongo的数据

db.userInfo.find({name: /mongo/});
//相当于%%
select * from userInfo where name like ‘%mongo%';

10、查询name中以mongo开头的

db.userInfo.find({name: /^mongo/});
select * from userInfo where name like ‘mongo%';

11、查询指定列name、age数据

db.userInfo.find({}, {name: 1, age: 1});

相当于:select name, age from userInfo;

注:当然name也可以用true或false,当用ture的情况下河name:1效果一样,如果用false就是排除name,显示name以外的列信息。

12、查询指定列name、age数据, age > 25

db.userInfo.find({age: {$gt: 25}}, {name: 1, age: 1});

相当于:select name, age from userInfo where age >25;

13、按照年龄排序

升序:db.userInfo.find().sort({age: 1});
降序:db.userInfo.find().sort({age: -1});

14、查询name = zhangsan, age = 22的数据

db.userInfo.find({name: 'zhangsan', age: 22});

相当于:select * from userInfo where name = ‘zhangsan’ and age = ‘22′;

15、查询前5条数据

db.userInfo.find().limit(5);

相当于:select top 5 * from userInfo;

16、查询10条以后的数据

db.userInfo.find().skip(10);
相当于:select * from userInfo where id not in (
select top 10 * from userInfo);

17、查询在5-10之间的数据

db.userInfo.find().limit(10).skip(5);

注:可用于分页,limit是pageSize,skip是第几页*pageSize

18、or 查询

db.userInfo.find({$or: [{age: 22}, {age: 25}]});

相当于:select * from userInfo where age = 22 or age = 25;

19、查询第一条数据

db.userInfo.findOne();
相当于:selecttop 1 * from userInfo;
db.userInfo.find().limit(1);

20、查询某个结果集的记录条数

db.userInfo.find({age: {$gte: 25}}).count();

相当于:select count(*) from userInfo where age >= 20;

三、索引

1、创建索引

db.userInfo.ensureIndex({name: 1});
db.userInfo.ensureIndex({name: 1, ts: -1});

2、查询当前聚集集合所有索引

db.userInfo.getIndexes();

3、查看总索引记录大小

db.userInfo.totalIndexSize();

4、读取当前集合的所有index信息

db.users.reIndex();

5、删除指定索引

db.users.dropIndex("name_1");

6、删除所有索引索引

db.users.dropIndexes();

四、修改、添加、删除集合数据

1、添加

db.users.save({name: ‘zhangsan', age: 25, sex: true});

2、修改

db.users.update({age: 25}, {$set: {name: 'changeName'}}, false, true);
相当于:update users set name = ‘changeName' where age = 25;
db.users.update({name: 'Lisi'}, {$inc: {age: 50}}, false, true);
相当于:update users set age = age + 50 where name = ‘Lisi';
db.users.update({name: 'Lisi'}, {$inc: {age: 50}, $set: {name: 'hoho'}}, false, true);
相当于:update users set age = age + 50, name = ‘hoho' where name = ‘Lisi';

3、删除一条数据

db.users.remove({age: 132});

4、清空表数据

db.users.drop();

五、其他

1、循环添加数据

> for (var i = 0; i < 30; i++) {
... db.users.save({name: "u_" + i, age: 22 + i, sex: i % 2});
... };

这样就循环添加了30条数据,同样也可以省略括号的写法

> for (var i = 0; i < 30; i++) db.users.save({name: "u_" + i, age: 22 + i, sex: i % 2});

2、查询之前的错误信息

db.getPrevError();

3、清除错误记录

db.resetError();

MongoDB 任意时间点恢复

简介

Mongodump是MongoDB附带的基础逻辑备份工具。它将备份完整的数据库/集合的BSON副本,并且可以选择备份保持数据库一致性的日志(oplog),备份的oplog记录的是备份执行期间数据库的变更日志。Mongorestore是用于恢复由Mongodump创建的逻辑备份的工具。我将在本文的步骤中使用这俩工具来恢复备份数据。本文假设一个基于mongodump的一致性备份(通过使用 –oplog),并将备份恢复到MongoDB实例。

本实例中,mongodump将会备份恢复基础的集合数据,并单独搜集基于时间点备份恢复所需的oplog变更,恢复时将其应用到数据。

需要指出的是:Percona开发了一个名为 mongodb_consistent_backup 的备份工具,它是’mongodump’的包装,增加了集群范围的备份一致性。 由mongodb_consistent_backup创建的备份(在Dump/Mongodump模式下)可以使用与常规“mongodump”备份相同的步骤进行恢复。
附:mongodb_consistent_backup: https://github.com/Percona-Lab/mongodb_consistent_backup

实验步骤

Stage 1:获得一个Mongodump备份

mongodump参数:

--host/--port (and –user/–password)
即使您使用的是默认主机/端口(localhost:27017),也是必需的。如果启用了授权,则还要添加 –user / –password 参数。

--oplog
任何replset成员都需要!此参数使“mongodump”在备份过程中捕获oplog更改日志,以保持一致的时间点。

--gzip
可选的。适用于mongodump > = 3.2,启用备份文件的内联压缩。

附:–oplog创建一个名为oplog.bson文件作为mongodump结果的一部分,包含了mongodump操作期间的oplog条目,实现了某个时间点的一致性快照。可用mongorestore –oplogReplay进行恢复。它的实际作用是在导出的同时生成一个oplog.bson文件,存放在你开始进行dump到dump结束之间所有的oplog。用图形来说明下oplog.bson的覆盖范围:

未分类

注意:mongodump时,–oplog只能在master节点(主从结构)或者副本集的成员中执行。也就是说,mongodump –oplog不能在主从结构的slave上执行(当然,估计也没人用主从了)。

备份命令:

$ mongodump --host localhost --port 27017 --oplog --gzip
2016-08-15T12:32:28.930+0200    writing wikipedia.pages to
2016-08-15T12:32:31.932+0200    [#########...............]  wikipedia.pages  674/1700   (39.6%)
2016-08-15T12:32:34.931+0200    [####################....]  wikipedia.pages  1436/1700  (84.5%)
2016-08-15T12:32:37.509+0200    [########################]  wikipedia.pages  2119/1700  (124.6%)
2016-08-15T12:32:37.510+0200    done dumping wikipedia.pages (2119 documents)
2016-08-15T12:32:37.521+0200    writing captured oplog to
2016-08-15T12:32:37.931+0200    [##......................]  .oplog  44/492   (8.9%)
2016-08-15T12:32:39.648+0200    [########################]  .oplog  504/492  (102.4%)
2016-08-15T12:32:39.648+0200    dumped 504 oplog entries

stage 2 恢复备份的数据

步骤:

  1. 找到分片的PRIMARY成员(肯定要在主节点做恢复)

  2. 三重检查是否将正确的备份恢复至正确的分片/主机

  3. 将基于mongodump的备份还原到PRIMARY节点(具体可参考官方文档)

  4. 检查是否有错误

  5. 检查所有SECONDARY成员是否与PRIMARY同步。

stage 3 获取时间点恢复的Oplogs

在这个阶段,我们将收集从备份时间点(向前滚动数据)到我们想要恢复的时间点(oplog位置)所需的更改。

在下面的这个例子中,假设有人在oplog时间戳“Timestamp(1470923942,3)”中意外删除了整个集合,我们想修复它。如果我们递减“时间戳(1470923942,3)”的增量(第2个数字),我们将获取到在意外命令之前进行的最后更改,在本例中为“时间戳(1470923942,2)”。 通过使用时间戳,我们可以捕获并重放oplog,从备份时间点到问题/错误操作之前。

我们需要开始和结束时间戳来获取oplog中的数据。在所有情况下,这都需要手动收集。

用到的脚本:
【dump_oplog_range.sh】

#!/bin/bash
#
# This tool will dump out a BSON file of MongoDB oplog changes based on a range of Timestamp() objects.
# The captured oplog changes can be applied to a host using 'mongorestore --oplogReplay --dir /path/to/dump'.
set -e
TS_START=$1
TS_END=$2
MONGODUMP_EXTRA=$3
function usage_exit() {
  echo "Usage $0: [Start-BSON-Timestamp] [End-BSON-Timestamp] [Extra-Mongodump-Flags (in quotes for multiple)]"
  exit 1
}
function check_bson_timestamp() {
  local TS=$1
  echo "$TS" | grep -qP "^Timestamp(d+,sd+)$"
  if [ $? -gt 0 ]; then
    echo "ERROR: Both timestamp fields must be in BSON Timestamp format, eg: 'Timestamp(########, #)'!"
    usage_exit
  fi
}
if [ -z "$TS_START" ] || [ -z "$TS_END" ]; then
  usage_exit
else
  check_bson_timestamp "$TS_START"
  check_bson_timestamp "$TS_END"
fi
MONGODUMP_QUERY='{ "ts" : { "$gte" : '$TS_START' }, "ts" : { "$lte" : '$TS_END' } }'
MONGODUMP_FLAGS='--db=local --collection=oplog.rs'
[ ! -z "$MONGODUMP_EXTRA" ] && MONGODUMP_FLAGS="$MONGODUMP_FLAGS $MONGODUMP_EXTRA"
if [ -d dump ]; then
  echo "'dump' subdirectory already exists! Exiting!"
  exit 1
fi
echo "# Dumping oplogs from '$TS_START' to '$TS_END'..."
mkdir dump
mongodump $MONGODUMP_FLAGS --query "$MONGODUMP_QUERY" --out - >dump/oplog.bson
if [ -f dump/oplog.bson ]; then
  echo "# Done!"
else
  echo "ERROR: Cannot find oplog.bson file! Exiting!"
  exit 1
fi

脚本用法:

$ ./dump_oplog_range.sh
Usage ./dump_oplog_range.sh: [Start-BSON-Timestamp] [End-BSON-Timestamp] [Extra-Mongodump-Flags (in quotes for multiple)]

操作步骤:

1、找到包含PITR(point-in-time-recovery)还原所需的oplog的PRIMARY成员。

2、确定需要恢复的“end”Timestamp()。 这个oplog时间应该在问题出现之前。

3、确定备份开始之前的“start”Timestamp()。
这个时间戳不需要精确,所以Timestamp()对象等于“在备份开始前几分钟”都没有问题,但时间越精确,您需要重放的更改就更少(可以节省恢复时间)。

4、使用MongoToolsAndSnippets脚本: “get_oplog_range.sh”(就是上面贴的“帮助脚本”)将需要恢复的oplog时间范围dump到您选择的时间点。 在这个例子中,我在两个时间点之间收集oplog(同时在引号中传入–username / –password作为第三个参数):

1) 起始时间戳:本示例中的“阶段2:恢复集合数据”中的mongodump备份之前的BSON时间戳。在本例中,“时间戳(1470923918,0)”是在mongodump执行之前几秒钟的时间(不需要精确)。
2) 结束时间戳:在此示例中,恢复到哪个时间点的BSON时间戳。“时间戳(1470923942,2)”是发生问题之前的最后一次oplog更改。

$ wget -q https://raw.githubusercontent.com/percona/MongoToolsAndSnippets/master/rdba/dump_oplog_range.sh
$ bash ./dump_oplog_range.sh 'Timestamp(1470923918, 0)' 'Timestamp(1470923942, 2)' '--username=secret --password=secret --host=mongo01.example.com --port=27024'
# Dumping oplogs from 'Timestamp(1470923918, 0)' to 'Timestamp(1470923942, 2)'...
2016-08-12T13:11:17.676+0200    writing local.oplog.rs to stdout
2016-08-12T13:11:18.120+0200    dumped 22 documents
# Done!

注意:所有额外的mongodump 参数(第三个参数)必须用引号括起来!

5、通过查找 ‘oplog.bson’ 文件并检查文件中是否有一些数据(下面的例子中为168mb)来检查它的工作情况:

$ ls -alh dump/oplog.bson
-rw-rw-r--. 1 tim tim 168M Aug 12 13:11 dump/oplog.bson

stage 4 应用Oplogs进行时间点恢复(PITR)

在此阶段,我们将在阶段3中收集的基于时间范围的oplog应用于恢复的数据集,以便将数据从备份时间带到发生问题之前的特定时间点。

mongorestore 参数:

--host/--port(--user / --password)
即使您使用的是默认主机/端口(localhost:27017),也是必需的。如果启用授权,则还要添加 –user / –password 参数。

--oplogReplay
必须。这一步用于重放oplog。

--dir
必须。 mongodump数据的路径。

步骤:

1、将仅包含“oplog.bson”文件(在阶段3中捕获)的“dump”目录复制到需要应用oplog更改的主机(还原主机)。

2、在“dump”目录中运行“mongorestore”以将oplog重放到实例中。注意确保“dump”目录只包含“oplog.bson”文件。

$ mongorestore --host localhost --port 27017 --oplogReplay --dir ./dump
2016-08-12T13:12:28.105+0200    building a list of dbs and collections to restore from dump dir
2016-08-12T13:12:28.106+0200    replaying oplog
2016-08-12T13:12:31.109+0200    oplog   80.0 MB
2016-08-12T13:12:34.109+0200    oplog   143.8 MB
2016-08-12T13:12:35.501+0200    oplog   167.8 MB
2016-08-12T13:12:35.501+0200    done

3、使用任何可能的方式验证恢复的数据,(例如:.count()查询,一些随机的.find()查询等)。

使用centos系统中的crontab命令对mongodb定时备份恢复

通过centos脚本来执行备份操作,使用crontab命令实现定时备份与恢复功能,并删除指定天数前的备份

具体操作:

1、创建Mongodb数据库备份目录

mkdir -p /home/backup/mongod_bak/mongod_bak_now
mkdir -p /home/backup/mongod_bak/mongod_bak_list

2、新建Mongodb数据库备份脚本

vi /home/crontab/mongod_bak.sh #新建文件,输入以下代码
#!/bin/sh
DUMP=/usr/local/mongodb/bin/mongodump #mongodump备份文件执行路径
OUT_DIR=/home/backup/mongod_bak/mongod_bak_now #临时备份目录
TAR_DIR=/home/backup/mongod_bak/mongod_bak_list #备份存放路径
DATE=`date +%Y_%m_%d` #获取当前系统时间
DB_USER=username #数据库账号
DB_PASS=123456 #数据库密码
DAYS=7 #DAYS=7代表删除7天前的备份,即只保留最近7天的备份
TAR_BAK="mongod_bak_$DATE.tar.gz" #最终保存的数据库备份文件名
cd $OUT_DIR
rm -rf $OUT_DIR/*
mkdir -p $OUT_DIR/$DATE
$DUMP -u $DB_USER -p $DB_PASS -o $OUT_DIR/$DATE #备份全部数据库
tar -zcvf $TAR_DIR/$TAR_BAK $OUT_DIR/$DATE #压缩为.tar.gz格式
find $TAR_DIR/ -mtime +$DAYS -delete #删除7天前的备份文件

3、修改文件属性,使其可执行

chmod +x /home/crontab/mongod_bak.sh

4、修改/etc/crontab #添加计划任务

crontab -e

在下面添加

30 1 * * * root /home/crontab/mongod_bak.sh #表示每天凌晨1点30执行备份

5、重新启动crond使设置生效

/sbin/service crond reload #重新载入配置
chkconfig --level 35 crond on #加入开机自动启动:
/sbin/service crond start  #启动服务
crontab -l #列出crontab文件

每天在/home/backup/mongod_bak/mongod_bak_list目录下面可以看到mongod_bak_2017_02_28.tar.gz这样的压缩文件。

至此,Linux下自动备份Mongodb数据库并删除指定天数前的备份完成。

Mongodb定时恢复备份

恢复全部数据库:

mongorestore Cdrop Cdirectoryperdb 
/home/backup/mongod_bak/mongod_bak_now/2017_02_28/

恢复单个数据库:

mongorestore Cdrop -d dataname Cdirectoryperdb 
/home/backup/mongod_bak/mongod_bak_now/2017_02_28/dataname

Cdrop参数:恢复数据之前删除原来数据库数据,避免数据重复。

Cdirectoryperdb参数:数据库备份目录

-d参数:后面跟要恢复的数据库名称

crontab命令:

crontab命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行。该词来源于希腊语 chronos(χρ?νο?),原意是时间。

通常,crontab储存的指令被守护进程激活, crond常常在后台运行,每一分钟检查是否有预定的作业需要执行。这类作业一般称为cron jobs。

安装crontab:

[root@CentOS ~]# yum install vixie-cron
[root@CentOS ~]# yum install crontabs

说明:
vixie-cron软件包是cron的主程序;
crontabs软件包是用来安装、卸装、或列举用来驱动 cron 守护进程的表格的程序。

cron 是linux的内置服务,但它不自动起来,可以用以下的方法启动、关闭这个服务:

/sbin/service crond start #启动服务
/sbin/service crond stop #关闭服务
/sbin/service crond restart #重启服务
/sbin/service crond reload #重新载入配置

查看crontab服务状态:

service crond status

手动启动crontab服务:

service crond start

其他命令:

#查看crontab服务是否已设置为开机启动,执行命令:
ntsysv

#加入开机自动启动:
chkconfig --level 35 crond on

#列出crontab文件
crontab -l

#编辑crontab文件
crontab -e

#删除crontab文件
$ crontab -r

#恢复丢失的crontab文件
#假设你在自己的$HOME目录下还有一个备份,那么可以将其拷贝到/var/spool/cron/<username>,其中<username >是用户名
#或者使用如下命令其中,<filename>是你在$HOME目录中副本的文件名
crontab <filename>

mongodb导入导出备份恢复

mongodb数据库同样离不开必要的维护,如备份、恢复、导入、导出。
其实备份和恢复比导入和导出要方便些,而且一般不会出错,所以大部分时候使用备份和恢复操作就可以了

1. 备份Mongodb

mongodump -h RuiyIp -d dataname -o /home/aa/dev/mongodb/data
mongodump mongo导出数据库命令 mongodump --help 可以查看该命令下所有的帮助

-h 导出源
-d 要导出的数据库名称
-o 数据库要导出的位置

在终端滚过N行之后,数据库导出完成,可以去/home/aa/dev/mongodb/data 目录下查看导出的文件,bson格式的(我导出后没有马上就看到文件,等了一会才出现的,原因不明)

2. 恢复使用:mongorestore 命令

mongorestore -d cmsdev  /home/xx/dev/mongodb/data/cmsdev

-d 使用的数据库名称

后面直接加你刚才导出的目录,这样是直接恢复所有表
如果-c 是恢复一个表

3. 导入

mongoimport -d my_mongodb -c user user.dat

参数说明:

-d 指明使用的库, 本例中为” my_mongodb”
-c 指明要导出的表, 本例中为”user”
可以看到导入数据的时候会隐式创建表结构

4. 导出

mongoexport -d my_mongodb -c user -o user.dat

参数说明:

-d 指明使用的库, 本例中为” my_mongodb”
-c 指明要导出的表, 本例中为”user”
-o 指明要导出的文件名, 本例中为”user.dat”

从上面可以看到导出的方式使用的是JSON 的样式

docker构建 mongodb 集群服务

安装

docker run -p 27018:27017 -v /root/docker/mongo/data:/data/db  -d --name=mongo361 mongo --bind_ip_all --auth

进入容器

docker exec -it mongo361 mongo admin

创建 所有数据库角色

db.createUser({ user: 'zan', pwd: 'zan', roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] });

更多角色说明: https://docs.mongodb.com/manual/reference/built-in-roles/#built-in-roles

认证进入 操作状态

db.auth("zan","zan")
use test

test 只读账号

db.createUser({user:"test",pwd:"test",roles:[{role: "read", db: "test"}]})

test1 读写账号

db.createUser({user:"test1",pwd:"test",roles:[{role: "readWrite", db: "test"}]})

使用docker直接运行mongodb,解决Kitematic崩溃问题

在新大学习webapp 使用express mongodb数据库,并且mongodb是在docker容器下的,docker自带的用户界面UI Kitematic经常崩溃,
但是docker却没有问题,我就想能不能在 terminal 使用mongodb ,下面我就简单说下怎么使用docker的

未分类

先看看后面会使用到的几个命令

docker ps
docker images
docker start
docker stop
docker pull
docker run
docker exec
docker   COMMAND --help 最重要的一个

1. 使用terminal

未分类

Windows自带的powershell
打开powershell terminal 使用 docker ps 看下docker容器中都启动了那些服务.

未分类

如果已经运行了 mongodb 可以使用 docker stop SERVER-NAME SERVER-NAME 是使用 docker ps 中查看到的;

2. 下载镜像文件

使用 docker images 查看是否有可用的镜像文件

如果没有可以使用 docker pull mongodb 命令从官网上下载 https://store.docker.com/ 或者直接使用
https://store.docker.com/images/mongo 下载

未分类

然后根据自己现在运行的docker容器类型选择,默认是linux容器,下图是查看方式.点一下就会切换到windows containers.

未分类

3. 在docker容器中安装mongodb

docker run -d --name mongo-test -p 27017:27017 mongo

-d Run container in background and print container ID 老实说我也不太懂
– name 是指给你的mongodb起个名字
-p 这个参数一定要有,不然你本地的webapp连接不到数据库,
它的意思是将虚拟机中的mongodb的端口号(mongodb端口号使用docker ps 查看)映射到你的电脑localhost:27017上面 ,当然你的映射端 口号根据自己需要可以自定义.

4. 进入到 mongdb 创建数据库

使用如下命令

docker exec -it mongo-test mongo 

看下数据库 show dbs;

创建数据库 use myapp;

好了现在可以试试你的webapp是不是能连接上了. localhost:27017

MongoDB 分片集群技术

1.1 MongoDB复制集简介

一组Mongodb复制集,就是一组mongod进程,这些进程维护同一个数据集合。复制集提供了数据冗余和高等级的可靠性,这是生产部署的基础。

1.1.1 复制集的目的

  保证数据在生产部署时的冗余和可靠性,通过在不同的机器上保存副本来保证数据的不会因为单点损坏而丢失。能够随时应对数据丢失、机器损坏带来的风险。

  换一句话来说,还能提高读取能力,用户的读取服务器和写入服务器在不同的地方,而且,由不同的服务器为不同的用户提供服务,提高整个系统的负载。

1.1.2 简单介绍

  一组复制集就是一组mongod实例掌管同一个数据集,实例可以在不同的机器上面。实例中包含一个主导,接受客户端所有的写入操作,其他都是副本实例,从主服务器上获得数据并保持同步。

  主服务器很重要,包含了所有的改变操作(写)的日志。但是副本服务器集群包含有所有的主服务器数据,因此当主服务器挂掉了,就会在副本服务器上重新选取一个成为主服务器。

  每个复制集还有一个仲裁者,仲裁者不存储数据,只是负责通过心跳包来确认集群中集合的数量,并在主服务器选举的时候作为仲裁决定结果。

1.2 复制的基本架构

  基本的架构由3台服务器组成,一个三成员的复制集,由三个有数据,或者两个有数据,一个作为仲裁者。

1.2.1 三个存储数据的复制集

具有三个存储数据的成员的复制集有:

一个主库;

两个从库组成,主库宕机时,这两个从库都可以被选为主库。

未分类

当主库宕机后,两个从库都会进行竞选,其中一个变为主库,当原主库恢复后,作为从库加入当前的复制集群即可。

未分类

1.2.2 当存在arbiter节点

在三个成员的复制集中,有两个正常的主从,及一台arbiter节点:

一个主库

一个从库,可以在选举中成为主库

一个aribiter节点,在选举中,只进行投票,不能成为主库

未分类

说明:

  由于arbiter节点没有复制数据,因此这个架构中仅提供一个完整的数据副本。arbiter节点只需要更少的资源,代价是更有限的冗余和容错。

当主库宕机时,将会选择从库成为主,主库修复后,将其加入到现有的复制集群中即可。

未分类

1.2.3 Primary选举

  复制集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作,获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary。

『大多数』的定义

  假设复制集内投票成员(后续介绍)数量为N,则大多数为 N/2 + 1,当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。

未分类

  通常建议将复制集成员数量设置为奇数,从上表可以看出3个节点和4个节点的复制集都只能容忍1个节点失效,从『服务可用性』的角度看,其效果是一样的。(但无疑4个节点能提供更可靠的数据存储)

1.3 复制集中成员说明

1.3.1 所有成员说明

未分类

1.3.2 Priority 0节点

  作为一个辅助可以作为一个备用。在一些复制集中,可能无法在合理的时间内添加新成员的时候。备用成员保持数据的当前最新数据能够替换不可用的成员。

未分类

1.3.3 Hidden 节点(隐藏节点)

  客户端将不会把读请求分发到隐藏节点上,即使我们设定了 复制集读选项 。

  这些隐藏节点将不会收到来自应用程序的请求。我们可以将隐藏节点专用于报表节点或是备份节点。 延时节点也应该是一个隐藏节点。

未分类

1.3.4 Delayed 节点(延时节点)

  延时节点的数据集是延时的,因此它可以帮助我们在人为误操作或是其他意外情况下恢复数据。

  举个例子,当应用升级失败,或是误操作删除了表和数据库时,我们可以通过延时节点进行数据恢复。

未分类

1.4 配置MongoDB复制集

1.4.1 环境说明

系统环境说明:

[root@MongoDB ~]# cat /etc/redhat-release 
CentOS release 6.9 (Final)
[root@MongoDB ~]# uname -r
2.6.32-696.el6.x86_64
[root@MongoDB ~]# /etc/init.d/iptables status
iptables: Firewall is not running.
[root@MongoDB ~]# getenforce 
Disabled
[root@MongoDB ~]# hostname -I
10.0.0.152 172.16.1.152

软件版本说明

本次使用的mongodb版本为:mongodb-linux-x86_64-3.2.8.tgz

1.4.2 前期准备,在root用户下操作

本次复制集复制采用Mongodb多实例进行

所有的操作都基于安装完成的mongodb服务,详情参照:http://www.cnblogs.com/clsn/p/8214194.html#_label3

#创建mongod用户
    useradd -u800 mongod
    echo 123456|passwd --stdin mongod 
# 安装mongodb
    mkdir -p /mongodb/bin
   cd  /mongodb
   wget http://downloads.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.2.8.tgz
    tar xf  mongodb-linux-x86_64-3.2.8.tgz
    cd mongodb-linux-x86_64-3.2.8/bin/ &&
    cp * /mongodb/bin
    chown -R mongod.mongod /mongodb
# 切换到mongod用户进行后续操作
    su - mongod

1.4.3 创建所需目录

for  i in 28017 28018 28019 28020
    do 
      mkdir -p /mongodb/$i/conf  
      mkdir -p /mongodb/$i/data  
      mkdir -p /mongodb/$i/log
done 

1.4.4 配置多实例环境

编辑第一个实例配置文件

cat >>/mongodb/28017/conf/mongod.conf<<'EOF'
systemLog:
  destination: file
  path: /mongodb/28017/log/mongodb.log
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28017/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      # cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
processManagement:
  fork: true
net:
  port: 28017
replication:
  oplogSizeMB: 2048
  replSetName: my_repl
EOF

复制配置文件

for i in 28018 28019 28020
  do  
   cp  /mongodb/28017/conf/mongod.conf  /mongodb/$i/conf/
done

修改配置文件

for i in 28018 28019 28020
  do 
    sed  -i  "s#28017#$i#g" /mongodb/$i/conf/mongod.conf
done

启动服务

for i in 28017 28018 28019 28020
  do  
    mongod -f /mongodb/$i/conf/mongod.conf 
done
# 关闭服务的方法

for i in 28017 28018 28019 28020
   do  
     mongod --shutdown  -f /mongodb/$i/conf/mongod.conf 
done

1.4.5 配置复制集

登陆数据库,配置mongodb复制

shell> mongo --port 28017

config = {_id: 'my_repl', members: [
                          {_id: 0, host: '10.0.0.152:28017'},
                          {_id: 1, host: '10.0.0.152:28018'},
                          {_id: 2, host: '10.0.0.152:28019'}]
          }

初始化这个配置

> rs.initiate(config)

到此复制集配置完成

1.4.6 测试主从复制

在主节点插入数据

my_repl:PRIMARY> db.movies.insert([ { "title" : "Jaws", "year" : 1975, "imdb_rating" : 8.1 },
   { "title" : "Batman", "year" : 1989, "imdb_rating" : 7.6 },
  ] );

在主节点查看数据

my_repl:PRIMARY> db.movies.find().pretty()
{
    "_id" : ObjectId("5a4d9ec184b9b2076686b0ac"),
    "title" : "Jaws",
    "year" : 1975,
    "imdb_rating" : 8.1
}
{
    "_id" : ObjectId("5a4d9ec184b9b2076686b0ad"),
    "title" : "Batman",
    "year" : 1989,
    "imdb_rating" : 7.6
}

注:在mongodb复制集当中,默认从库不允许读写。

在从库打开配置(危险)

注意:严禁在从库做任何修改操作

my_repl:SECONDARY> rs.slaveOk()
my_repl:SECONDARY> show tables;
movies
my_repl:SECONDARY> db.movies.find().pretty()
{
    "_id" : ObjectId("5a4d9ec184b9b2076686b0ac"),
    "title" : "Jaws",
    "year" : 1975,
    "imdb_rating" : 8.1
}
{
    "_id" : ObjectId("5a4d9ec184b9b2076686b0ad"),
    "title" : "Batman",
    "year" : 1989,
    "imdb_rating" : 7.6
}

  
在从库查看完成在登陆到主库

1.4.7 复制集管理操作

(1)查看复制集状态:

rs.status();     # 查看整体复制集状态
rs.isMaster();   #  查看当前是否是主节点

(2)添加删除节点

rs.add("ip:port");     #  新增从节点
rs.addArb("ip:port"); #  新增仲裁节点
rs.remove("ip:port"); #  删除一个节点

注:

添加特殊节点时,

1>可以在搭建过程中设置特殊节点

2>可以通过修改配置的方式将普通从节点设置为特殊节点

/*找到需要改为延迟性同步的数组号*/;

(3)配置延时节点(一般延时节点也配置成hidden)

cfg=rs.conf() 
cfg.members[2].priority=0
cfg.members[2].slaveDelay=120
cfg.members[2].hidden=true

注:这里的2是rs.conf()显示的顺序(除主库之外),非ID

重写复制集配置

rs.reconfig(cfg)   

也可将延时节点配置为arbiter节点

cfg.members[2].arbiterOnly=true

配置成功后,通过以下命令查询配置后的属性

rs.conf();

1.4.8 副本集其他操作命令

查看副本集的配置信息

my_repl:PRIMARY> rs.config()

查看副本集各成员的状态

my_repl:PRIMARY> rs.status()

1.4.8.1 副本集角色切换(不要人为随便操作)

rs.stepDown()
rs.freeze(300)  # 锁定从,使其不会转变成主库,freeze()和stepDown单位都是秒。
rs.slaveOk()    # 设置副本节点可读:在副本节点执行

插入数据

> use app
switched to db app
app> db.createCollection('a')
{ "ok" : 0, "errmsg" : "not master", "code" : 10107 }

查看副本节点

> rs.printSlaveReplicationInfo()
source: 192.168.1.22:27017
    syncedTo: Thu May 26 2016 10:28:56 GMT+0800 (CST)
    0 secs (0 hrs) behind the primary

MongoDB分片(Sharding)技术

  分片(sharding)是MongoDB用来将大型集合分割到不同服务器(或者说一个集群)上所采用的方法。尽管分片起源于关系型数据库分区,但MongoDB分片完全又是另一回事。

  和MySQL分区方案相比,MongoDB的最大区别在于它几乎能自动完成所有事情,只要告诉MongoDB要分配数据,它就能自动维护数据在不同服务器之间的均衡。

2.1 MongoDB分片介绍

2.1.1 分片的目的

  高数据量和吞吐量的数据库应用会对单机的性能造成较大压力,大的查询量会将单机的CPU耗尽,大的数据量对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。

为了解决这些问题,有两个基本的方法: 垂直扩展和水平扩展。

垂直扩展:增加更多的CPU和存储资源来扩展容量。

水平扩展:将数据集分布在多个服务器上。水平扩展即分片。

2.1.2 分片设计思想

  分片为应对高吞吐量与大数据量提供了方法。使用分片减少了每个分片需要处理的请求数,因此,通过水平扩展,集群可以提高自己的存储容量和吞吐量。举例来说,当插入一条数据时,应用只需要访问存储这条数据的分片.

  使用分片减少了每个分片存储的数据。

  例如,如果数据库1tb的数据集,并有4个分片,然后每个分片可能仅持有256 GB的数据。如果有40个分片,那么每个切分可能只有25GB的数据。

未分类

2.1.3 分片机制提供了如下三种优势

1. 对集群进行抽象,让集群“不可见

  MongoDB自带了一个叫做mongos的专有路由进程。mongos就是掌握统一路口的路由器,其会将客户端发来的请求准确无误的路由到集群中的一个或者一组服务器上,同时会把接收到的响应拼装起来发回到客户端。

2. 保证集群总是可读写

  MongoDB通过多种途径来确保集群的可用性和可靠性。将MongoDB的分片和复制功能结合使用,在确保数据分片到多台服务器的同时,也确保了每分数据都有相应的备份,这样就可以确保有服务器换掉时,其他的从库可以立即接替坏掉的部分继续工作。

3. 使集群易于扩展

  当系统需要更多的空间和资源的时候,MongoDB使我们可以按需方便的扩充系统容量。

2.1.4 分片集群架构

未分类

未分类

分片集群的构造

(1)mongos :数据路由,和客户端打交道的模块。mongos本身没有任何数据,他也不知道该怎么处理这数据,去找config server

(2)config server:所有存、取数据的方式,所有shard节点的信息,分片功能的一些配置信息。可以理解为真实数据的元数据。

(3)shard:真正的数据存储位置,以chunk为单位存数据。

  Mongos本身并不持久化数据,Sharded cluster所有的元数据都会存储到Config Server,而用户的数据会议分散存储到各个shard。Mongos启动后,会从配置服务器加载元数据,开始提供服务,将用户的请求正确路由到对应的碎片。

Mongos的路由功能

当数据写入时,MongoDB Cluster根据分片键设计写入数据。

当外部语句发起数据查询时,MongoDB根据数据分布自动路由至指定节点返回数据。

2.2 集群中数据分布

2.2.1 Chunk是什么

  在一个shard server内部,MongoDB还是会把数据分为chunks,每个chunk代表这个shard server内部一部分数据。chunk的产生,会有以下两个用途:

  Splitting:当一个chunk的大小超过配置中的chunk size时,MongoDB的后台进程会把这个chunk切分成更小的chunk,从而避免chunk过大的情况

  Balancing:在MongoDB中,balancer是一个后台进程,负责chunk的迁移,从而均衡各个shard server的负载,系统初始1个chunk,chunk size默认值64M,生产库上选择适合业务的chunk size是最好的。ongoDB会自动拆分和迁移chunks。

分片集群的数据分布(shard节点)

(1)使用chunk来存储数据

(2)进群搭建完成之后,默认开启一个chunk,大小是64M,

(3)存储需求超过64M,chunk会进行分裂,如果单位时间存储需求很大,设置更大的chunk

(4)chunk会被自动均衡迁移。

2.2.2 chunksize的选择

适合业务的chunksize是最好的。

chunk的分裂和迁移非常消耗IO资源;chunk分裂的时机:在插入和更新,读数据不会分裂。

chunksize的选择:

  小的chunksize:数据均衡是迁移速度快,数据分布更均匀。数据分裂频繁,路由节点消耗更多资源。大的chunksize:数据分裂少。数据块移动集中消耗IO资源。通常100-200M

2.2.3 chunk分裂及迁移

  随着数据的增长,其中的数据大小超过了配置的chunk size,默认是64M,则这个chunk就会分裂成两个。数据的增长会让chunk分裂得越来越多。

未分类

  这时候,各个shard 上的chunk数量就会不平衡。这时候,mongos中的一个组件balancer 就会执行自动平衡。把chunk从chunk数量最多的shard节点挪动到数量最少的节点。

未分类

chunkSize 对分裂及迁移的影响

  MongoDB 默认的 chunkSize 为64MB,如无特殊需求,建议保持默认值;chunkSize 会直接影响到 chunk 分裂、迁移的行为。

  chunkSize 越小,chunk 分裂及迁移越多,数据分布越均衡;反之,chunkSize 越大,chunk 分裂及迁移会更少,但可能导致数据分布不均。

  chunkSize 太小,容易出现 jumbo chunk(即shardKey 的某个取值出现频率很高,这些文档只能放到一个 chunk 里,无法再分裂)而无法迁移;chunkSize 越大,则可能出现 chunk 内文档数太多(chunk 内文档数不能超过 250000 )而无法迁移。

  chunk 自动分裂只会在数据写入时触发,所以如果将 chunkSize 改小,系统需要一定的时间来将 chunk 分裂到指定的大小。

  chunk 只会分裂,不会合并,所以即使将 chunkSize 改大,现有的 chunk 数量不会减少,但 chunk 大小会随着写入不断增长,直到达到目标大小。

2.3 数据区分

2.3.1 分片键shard key

  MongoDB中数据的分片是、以集合为基本单位的,集合中的数据通过片键(Shard key)被分成多部分。其实片键就是在集合中选一个键,用该键的值作为数据拆分的依据。

  所以一个好的片键对分片至关重要。片键必须是一个索引,通过sh.shardCollection加会自动创建索引(前提是此集合不存在的情况下)。一个自增的片键对写入和数据均匀分布就不是很好,因为自增的片键总会在一个分片上写入,后续达到某个阀值可能会写到别的分片。但是按照片键查询会非常高效。

  随机片键对数据的均匀分布效果很好。注意尽量避免在多个分片上进行查询。在所有分片上查询,mongos会对结果进行归并排序。

  对集合进行分片时,你需要选择一个片键,片键是每条记录都必须包含的,且建立了索引的单个字段或复合字段,MongoDB按照片键将数据划分到不同的数据块中,并将数据块均衡地分布到所有分片中。

  为了按照片键划分数据块,MongoDB使用基于范围的分片方式或者 基于哈希的分片方式。

注意:

分片键是不可变。

分片键必须有索引。

分片键大小限制512bytes。

分片键用于路由查询。

MongoDB不接受已进行collection级分片的collection上插入无分片

键的文档(也不支持空值插入)

2.3.2 以范围为基础的分片Sharded Cluster

  Sharded Cluster支持将单个集合的数据分散存储在多shard上,用户可以指定根据集合内文档的某个字段即shard key来进行范围分片(range sharding)。

未分类

  对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分。

  假设有一个数字的片键:想象一个从负无穷到正无穷的直线,每一个片键的值都在直线上画了一个点。MongoDB把这条直线划分为更短的不重叠的片段,并称之为数据块,每个数据块包含了片键在一定范围内的数据。在使用片键做范围划分的系统中,拥有”相近”片键的文档很可能存储在同一个数据块中,因此也会存储在同一个分片中。

2.3.3 基于哈希的分片

  分片过程中利用哈希索引作为分片的单个键,且哈希分片的片键只能使用一个字段,而基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。

未分类

  对于基于哈希的分片,MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有”相近”片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。

  Hash分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找出满足条件的文档。

2.3.4 分片键选择建议

1、递增的sharding key

数据文件挪动小。(优势)

因为数据文件递增,所以会把insert的写IO永久放在最后一片上,造成最后一片的写热点。同时,随着最后一片的数据量增大,将不断的发生迁移至之前的片上。

2、随机的sharding key

数据分布均匀,insert的写IO均匀分布在多个片上。(优势)

大量的随机IO,磁盘不堪重荷。

3、混合型key

大方向随机递增,小范围随机分布。

为了防止出现大量的chunk均衡迁移,可能造成的IO压力。我们需要设置合理分片使用策略(片键的选择、分片算法(range、hash))

分片注意:

分片键是不可变、分片键必须有索引、分片键大小限制512bytes、分片键用于路由查询。

MongoDB不接受已进行collection级分片的collection上插入无分片键的文档(也不支持空值插入)

2.4 部署分片集群

本集群的部署基于1.1的复制集搭建完成。

2.4.1 环境准备

创建程序所需的目录

for  i in 17 18 19 20 21 22 23 24 25 26 
  do 
  mkdir -p /mongodb/280$i/conf  
  mkdir -p /mongodb/280$i/data  
  mkdir -p /mongodb/280$i/log
done

2.4.2 shard集群配置

编辑shard集群配置文件

cat > /mongodb/28021/conf/mongod.conf <<'EOF'
systemLog:
  destination: file
  path: /mongodb/28021/log/mongodb.log   
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28021/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 10.0.0.152
  port: 28021
replication:
  oplogSizeMB: 2048
  replSetName: sh1
sharding:
  clusterRole: shardsvr
processManagement: 
  fork: true
EOF

复制shard集群配置文件

for  i in  22 23 24 25 26  
  do  
   cp  /mongodb/28021/conf/mongod.conf  /mongodb/280$i/conf/
done

修改配置文件端口

for  i in   22 23 24 25 26  
  do 
    sed  -i  "s#28021#280$i#g" /mongodb/280$i/conf/mongod.conf
done

修改配置文件复制集名称(replSetName)

for  i in    24 25 26  
  do 
    sed  -i  "s#sh1#sh2#g" /mongodb/280$i/conf/mongod.conf
done

启动shard集群

for  i in  21 22 23 24 25 26
  do  
    mongod -f /mongodb/280$i/conf/mongod.conf 
done

配置复制集1

mongo --host 10.0.0.152 --port 28021  admin

配置复制集

config = {_id: 'sh1', members: [
                          {_id: 0, host: '10.0.0.152:28021'},
                          {_id: 1, host: '10.0.0.152:28022'},
                          {_id: 2, host: '10.0.0.152:28023',"arbiterOnly":true}]
           }  
 # 初始化配置
rs.initiate(config)  

配置复制集2

mongo --host 10.0.0.152 --port 28024  admin

配置复制集

config = {_id: 'sh2', members: [
                          {_id: 0, host: '10.0.0.152:28024'},
                          {_id: 1, host: '10.0.0.152:28025'},
                          {_id: 2, host: '10.0.0.152:28026',"arbiterOnly":true}]
           }
# 初始化配置
rs.initiate(config)

2.4.3 config集群配置

创建主节点配置文件

cat > /mongodb/28018/conf/mongod.conf <<'EOF'
systemLog:
  destination: file
  path: /mongodb/28018/log/mongodb.conf
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28018/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 10.0.0.152
  port: 28018
replication:
  oplogSizeMB: 2048
  replSetName: configReplSet
sharding:
  clusterRole: configsvr
processManagement: 
  fork: true
EOF

将配置文件分发到从节点

for  i in 19 20 
  do  
   cp  /mongodb/28018/conf/mongod.conf  /mongodb/280$i/conf/
done

修改配置文件端口信息

for  i in 19 20  
  do 
    sed  -i  "s#28018#280$i#g" /mongodb/280$i/conf/mongod.conf
done

启动config server集群

for  i in  18 19 20 
  do  
    mongod -f /mongodb/280$i/conf/mongod.conf 
done

配置config server复制集

mongo --host 10.0.0.152 --port 28018  admin

配置复制集信息

config = {_id: 'configReplSet', members: [
                          {_id: 0, host: '10.0.0.152:28018'},
                          {_id: 1, host: '10.0.0.152:28019'},
                          {_id: 2, host: '10.0.0.152:28020'}]
           }
# 初始化配置
rs.initiate(config)    

注:config server 使用复制集不用有arbiter节点。3.4版本以后config必须为复制集

2.4.4 mongos节点配置

修改配置文件

cat > /mongodb/28017/conf/mongos.conf <<'EOF'
systemLog:
  destination: file
  path: /mongodb/28017/log/mongos.log
  logAppend: true
net:
  bindIp: 10.0.0.152
  port: 28017
sharding:
  configDB: configReplSet/10.0.0.152:28108,10.0.0.152:28019,10.0.0.152:28020
processManagement: 
  fork: true
EOF

启动mongos

mongos -f /mongodb/28017/conf/mongos.conf

登陆到mongos

mongo 10.0.0.152:28017/admin

添加分片节点

db.runCommand( { addshard : "sh1/10.0.0.152:28021,10.0.0.152:28022,10.0.0.152:28023",name:"shard1"} )
db.runCommand( { addshard : "sh2/10.0.0.152:28024,10.0.0.152:28025,10.0.0.152:28026",name:"shard2"} )

列出分片

mongos> db.runCommand( { listshards : 1 } )
{
    "shards" : [
        {
            "_id" : "shard2",
            "host" : "sh2/10.0.0.152:28024,10.0.0.152:28025"
        },
        {
            "_id" : "shard1",
            "host" : "sh1/10.0.0.152:28021,10.0.0.152:28022"
        }
    ],
    "ok" : 1
}

整体状态查看

mongos> sh.status();

至此MongoDB的分片集群就搭建完成。

2.4.5 数据库分片配置

激活数据库分片功能

语法:( { enablesharding : "数据库名称" } )

mongos> db.runCommand( { enablesharding : "test" } )

指定分片建对集合分片,范围片键–创建索引

mongos> use test 
mongos> db.vast.ensureIndex( { id: 1 } )
mongos> use admin
mongos> db.runCommand( { shardcollection : "test.vast",key : {id: 1} } )

集合分片验证

mongos> use test
mongos> for(i=0;i<20000;i++){ db.vast1.insert({"id":i,"name":"clsn","age":70,"date":new Date()}); }
mongos> db.vast.stats()

插入数据的条数尽量大些,能够看出更好的效果。

2.5 分片集群的操作

2.5.1 不同分片键的配置

范围片键

admin> sh.shardCollection("数据库名称.集合名称",key : {分片键: 1}  )
或
admin> db.runCommand( { shardcollection : "数据库名称.集合名称",key : {分片键: 1} } )

eg:

admin > sh.shardCollection("test.vast",key : {id: 1}  )
或
admin> db.runCommand( { shardcollection : "test.vast",key : {id: 1} } )

哈希片键

admin > sh.shardCollection( "数据库名.集合名", { 片键: "hashed" } )

创建哈希索引

admin> db.vast.ensureIndex( { a: "hashed" } )
admin > sh.shardCollection( "test.vast", { a: "hashed" } )

2.5.2 分片集群的操作

判断是否Shard集群

admin> db.runCommand({ isdbgrid : 1})

列出所有分片信息

admin> db.runCommand({ listshards : 1})

列出开启分片的数据库

admin> use config
config> db.databases.find( { "partitioned": true } )
config> db.databases.find() //列出所有数据库分片情况

查看分片的片键

config> db.collections.find()
{
    "_id" : "test.vast",
    "lastmodEpoch" : ObjectId("58a599f19c898bbfb818b63c"),
    "lastmod" : ISODate("1970-02-19T17:02:47.296Z"),
    "dropped" : false,
    "key" : {
        "id" : 1
    },
    "unique" : false
}

查看分片的详细信息

admin> db.printShardingStatus()
或
admin> sh.status()

删除分片节点

sh.getBalancerState()
mongos> db.runCommand( { removeShard: "shard2" } )

2.6 balance操作

查看mongo集群是否开启了 balance 状态

mongos> sh.getBalancerState()
true

当然你也可以通过在路由节点mongos上执行sh.status() 查看balance状态。

如果balance开启,查看是否正在有数据的迁移

连接mongo集群的路由节点

mongos> sh.isBalancerRunning()
false

2.6.1 设置balance 窗口

(1)连接mongo集群的路由节点

(2)切换到配置节点

use config

(3)确定balance 开启中

sh.getBalancerState()

如果未开启,执行命令

sh.setBalancerState( true )

(4)修改balance 窗口的时间

db.settings.update(
   { _id: "balancer" },
   { $set: { activeWindow : { start : "<start-time>", stop : "<stop-time>" } } },
   { upsert: true }
)

eg:

db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "00:00", stop : "5:00" } } }, true )

当你设置了activeWindow,就不能用sh.startBalancer() 启动balance

NOTE

The balancer window must be sufficient to complete the migration of all data inserted during the day.

As data insert rates can change based on activity and usage patterns, it is important to ensure that the balancing window you select will be sufficient to support the needs of your deployment.

(5)删除balance 窗口

use config
db.settings.update({ _id : "balancer" }, { $unset : { activeWindow : true } })

2.6.2 关闭balance

默认balance 的运行可以在任何时间,只迁移需要迁移的chunk,如果要关闭balance运行,停止一段时间可以用下列方法:

(1) 连接到路由mongos节点

(2) 停止balance

sh.stopBalancer()

(3) 查看balance状态

sh.getBalancerState()

(4)停止balance 后,没有迁移进程正在迁移,可以执行下列命令

use config
while( sh.isBalancerRunning() ) {
          print("waiting...");
          sleep(1000);
}

2.6.3 重新打开balance

如果你关闭了balance,准备重新打开balance

(1) 连接到路由mongos节点

(2) 打开balance

sh.setBalancerState(true)

如果驱动没有命令 sh.startBalancer(),可以用下列命令

use config
db.settings.update( { _id: "balancer" }, { $set : { stopped: false } } , { upsert: true } )

2.6.4 关于集合的balance

关闭某个集合的balance

sh.disableBalancing("students.grades")

打开某个集合的balance

sh.enableBalancing("students.grades")

确定某个集合的balance是开启或者关闭

db.getSiblingDB("config").collections.findOne({_id : "students.grades"}).noBalance;

2.6.5 问题解决

mongodb在做自动分片平衡的时候,或引起数据库响应的缓慢,可以通过禁用自动平衡以及设置自动平衡进行的时间来解决这一问题。

(1)禁用分片的自动平衡

// connect to mongos
> use config
> db.settings.update( { _id: "balancer" }, { $set : { stopped: true } } , true );

(2)自定义 自动平衡进行的时间段

// connect to mongos
> use config
> db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "21:00", stop : "9:00" } } }, true )

MongoDB分片启动失败解决方法

MongoDB机器未能正常关机,机器掉电重启等各种情况,都可能引起mongodb数据文件异常,然后下次启动的时候,会报错大概如下

517322 2018-01-03T22:20:45.939+0800 I NETWORK  [Balancer] scoped connection to 10.249.3.12:20000,10.249.3.11:20000,10.249.3.11:20003 not being returned to the pool
517323 2018-01-03T22:20:45.939+0800 I SHARDING [Balancer] caught exception while doing balance: ReplicaSetMonitor no master found for set: shard1
517324 2018-01-03T22:20:46.540+0800 I NETWORK  [LockPinger] SyncClusterConnection connecting to [10.249.3.12:20000]
517325 2018-01-03T22:20:46.541+0800 I NETWORK  [LockPinger] SyncClusterConnection connecting to [10.249.3.11:20000]
517326 2018-01-03T22:20:46.541+0800 I NETWORK  [LockPinger] SyncClusterConnection connecting to [10.249.3.11:20003]
517327 2018-01-03T22:20:49.748+0800 W SHARDING config servers 10.249.3.12:20000 and 10.249.3.11:20000 differ
517328 2018-01-03T22:20:49.749+0800 W SHARDING config servers 10.249.3.12:20000 and 10.249.3.11:20000 differ
517329 2018-01-03T22:20:49.751+0800 W SHARDING config servers 10.249.3.12:20000 and 10.249.3.11:20000 differ
517330 2018-01-03T22:20:49.752+0800 W SHARDING config servers 10.249.3.12:20000 and 10.249.3.11:20000 differ

重点在differ这里,两个分片不一致,导致不能启动,解决方法如下:
分别安全关闭10.249.3.11和12的MongoDB

/usr/local/mongodb/bin/mongo -host 127.0.0.1 -port 20000
> use admin; 
> db.shutdownServer();

进入到3.11机器的对应数据目录,将文件整个打包,

进入到3.12机器的数据目录/data/mongodb/config20001,删除所有文件,记得备份,

最后scp 11.zip 10.249.3.12:/data/mongodb/config20001 ,然后解压,最后启动mongodb即可。