上一篇文章练习了,MongoDB 的以下操作

  • 安装 MongoDB 服务
  • 连接 MongoDB
  • MongoDB 创建数据库
  • MongoDB 删除数据库
  • MongoDB 插入文档
  • MongoDB 删除文档
  • MongoDB 查询文档
  • MongoDB AND 条件
  • MongoDB OR 条件
  • MongoDB AND 和 OR 联合使用
  • MongoDB 条件操作符
  • MongoDB (>) 大于操作符 - $gt
  • MongoDB(>=)大于等于操作符 - $gte
  • MongoDB (<) 小于操作符 - $lt
  • MongoDB (<=) 小于操作符 - $lte
  • MongoDB 使用 (<) 和 (>) 查询 - $lt 和 $gt

手把手教你 MongoDB 的安装与详细使用(一)

http://www.ymq.io/2018/01/29/MongoDB-2/

接下来继续

  1. MongoDB Limit与Skip方法
  2. MongoDB 排序
  3. MongoDB 索引
  4. MongoDB 聚合
  5. MongoDB 主从复制(副本集)
  6. MongoDB 自动故障转移

1. MongoDB Limit与Skip方法

Limit() 方法

MongoDB Limit() 方法

如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。

插入测试数据

db.col.insert({title: 'MongoDB-1'})
db.col.insert({title: 'MongoDB-2'})
db.col.insert({title: 'MongoDB-3'})
db.col.insert({title: 'MongoDB-4'})
MongoDB Enterprise > db.col.find()
{ "_id" : ObjectId("5a6e8eaef14a3f270ba2dd0c"), "title" : "MongoDB-1" }
{ "_id" : ObjectId("5a6e8ec8f14a3f270ba2dd0d"), "title" : "MongoDB-2" }
{ "_id" : ObjectId("5a6e8ecbf14a3f270ba2dd0e"), "title" : "MongoDB-3" }
{ "_id" : ObjectId("5a6e8ed5f14a3f270ba2dd0f"), "title" : "MongoDB-4" }
MongoDB Enterprise >

语法

limit()方法基本语法如下所示:

> db.COLLECTION_NAME.find().limit(NUMBER)

以上实例为显示查询文档中的两条记录:

MongoDB Enterprise > db.col.find().limit(2)
{ "_id" : ObjectId("5a6e8eaef14a3f270ba2dd0c"), "title" : "MongoDB-1" }
{ "_id" : ObjectId("5a6e8ec8f14a3f270ba2dd0d"), "title" : "MongoDB-2" }
MongoDB Enterprise > db.col.find({},{"title":1,_id:0}).limit(2)
{ "title" : "MongoDB-1" }
{ "title" : "MongoDB-2" }
MongoDB Enterprise >

注:如果你们没有指定limit()方法中的参数则显示集合中的所有数据。

MongoDB Enterprise > db.col.find({},{"title":1,_id:0}).limit()
{ "title" : "MongoDB-1" }
{ "title" : "MongoDB-2" }
{ "title" : "MongoDB-3" }
{ "title" : "MongoDB-4" }

Skip() 方法

我们除了可以使用limit()方法来读取指定数量的数据外,还可以使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。
语法

skip() 方法脚本语法格式如下:

> db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

实例

以上实例只会显示第二条文档数据

MongoDB Enterprise > db.col.find({},{"title":1,_id:0}).limit(1).skip(1)
{ "title" : "MongoDB-2" }
MongoDB Enterprise >

注:skip()方法默认参数为 0

2. MongoDB 排序

MongoDB sort()方法

在MongoDB中使用使用sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段

使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而-1是用于降序排列。

语法

sort()方法基本语法如下所示:

> db.COLLECTION_NAME.find().sort({KEY:1})

col 集合中的数据如下:

MongoDB Enterprise > db.col.find()
{ "_id" : ObjectId("5a6e8eaef14a3f270ba2dd0c"), "title" : "MongoDB-1" }
{ "_id" : ObjectId("5a6e8ec8f14a3f270ba2dd0d"), "title" : "MongoDB-2" }
{ "_id" : ObjectId("5a6e8ecbf14a3f270ba2dd0e"), "title" : "MongoDB-3" }
{ "_id" : ObjectId("5a6e8ed5f14a3f270ba2dd0f"), "title" : "MongoDB-4" }
MongoDB Enterprise >

其中 1 为升序排列,而-1是用于降序排列

以下实例演示了 col 集合中的数据按字段 title 的降序排列:

MongoDB Enterprise > db.col.find({},{"title":1,_id:0}).sort({"title":-1})
{ "title" : "MongoDB-4" }
{ "title" : "MongoDB-3" }
{ "title" : "MongoDB-2" }
{ "title" : "MongoDB-1" }
MongoDB Enterprise >

注: 如果没有指定sort()方法的排序方式,默认按照文档的升序排列。

3. MongoDB 索引

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

ensureIndex() 方法

MongoDB使用 ensureIndex() 方法来创建索引。

语法

ensureIndex()方法基本语法格式如下所示:

> db.COLLECTION_NAME.ensureIndex({KEY:1})

语法中 Key 值为你要创建的索引字段,1为指定按升序创建索引,如果你想按降序来创建索引指定为-1即可。

实例

> db.COLLECTION_NAME.ensureIndex({KEY:1})

语法中 Key 值为你要创建的索引字段,1为指定按升序创建索引,如果你想按降序来创建索引指定为-1即可。

实例

MongoDB Enterprise > db.col.ensureIndex({"title":1})
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}
MongoDB Enterprise > 
db.col.insert({title: 'MongoDB 教程', 
    description: 'MongoDB 是一个 Nosql 数据库',
    by: '搜云库教程-专注于开发技术的研究与知识分享',
    url: 'http://www.souyunku.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 100
})

ensureIndex() 方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)。

MongoDB Enterprise > db.col.ensureIndex({"title":1,"description":1})
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 3,
    "numIndexesAfter" : 4,
    "ok" : 1
}

ensureIndex() 接收可选参数,可选参数列表如下:

实例

在后台创建索引:

建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为false。

MongoDB Enterprise > db.col.ensureIndex({"url":1}, {background: true})
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 5,
    "numIndexesAfter" : 6,
    "ok" : 1
}
MongoDB Enterprise > 

4. MongoDB 聚合

MongoDB 聚合

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)。

aggregate() 方法

删除之前的测试数据

MongoDB Enterprise > db.col.remove({})
WriteResult({ "nRemoved" : 5 })
MongoDB Enterprise >

插入新的测试数据

db.col.insert({
    title: 'MongoDB 教程',
    description: 'MongoDB 是一个 Nosql 数据库',
    by_user: 'penglei',
    url: 'http://www.souyunku.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 100
})
db.col.insert({
    title: 'MongoDB 教程',
    description: 'MongoDB 是一个 Nosql 数据库',
    by_user: 'penglei',
    url: 'http://www.souyunku.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 200
})
db.col.insert({
    title: 'MongoDB 教程',
    description: 'MongoDB 是一个 Nosql 数据库',
    by_user: 'penglei',
    url: 'http://www.souyunku.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 300
})
MongoDB Enterprise > db.col.find()
{ "_id" : ObjectId("5a6ebfab5326a260464a4072"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by_user" : "penglei", "url" : "http://www.souyunku.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
{ "_id" : ObjectId("5a6ebfab5326a260464a4073"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by_user" : "penglei", "url" : "http://www.souyunku.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 200 }
{ "_id" : ObjectId("5a6ebfab5326a260464a4074"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by_user" : "penglei", "url" : "http://www.souyunku.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 300 }
MongoDB Enterprise > 

MongoDB中聚合的方法使用aggregate()。

语法

aggregate() 方法的基本语法格式如下所示:

> db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

MongoDB Enterprise > db.col.aggregate([{$group : {_id : "$by_user", num_tutorials : {$sum : 1}}}])
{
    "_id": "penglei",
    "num_tutorials": 3
}
MongoDB Enterprise > db.col.aggregate([{$group : {_id : "$by_user", totle : {$sum : 1}}}])
{
    "_id": "penglei",
    "totle": 3
}
MongoDB Enterprise > 

以上实例类似sql语句:select by_user, count(*) from col group by by_user

在上面的例子中,我们通过字段by_user字段对数据进行分组,并计算by_user字段相同值的总和。

下表展示了一些聚合的表达式:

管道的概念

管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

这里我们介绍一下聚合框架中常用的几个操作:

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
  • $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
  • $limit:用来限制MongoDB聚合管道返回的文档数。
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group:将集合中的文档分组,可用于统计结果。
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。

管道操作符实例

1、$project实例

MongoDB Enterprise > db.col.aggregate(
    { $project : {
        title : 1 ,
        by_user : 1 ,
    }}
 );
 
{ "_id" : ObjectId("5a6ebfab5326a260464a4072"), "title" : "MongoDB 教程", "by_user" : "penglei" }
{ "_id" : ObjectId("5a6ebfab5326a260464a4073"), "title" : "MongoDB 教程", "by_user" : "penglei" }
{ "_id" : ObjectId("5a6ebfab5326a260464a4074"), "title" : "MongoDB 教程", "by_user" : "penglei" }
MongoDB Enterprise > 

这样的话结果中就只还有_id,tilte和by_user三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:

MongoDB Enterprise > db.col.aggregate(
    { $project : {
        _id : 0 ,
        title : 1 ,
        by_user : 1
    }});
{ "title" : "MongoDB 教程", "by_user" : "penglei" }
{ "title" : "MongoDB 教程", "by_user" : "penglei" }
{ "title" : "MongoDB 教程", "by_user" : "penglei" }

2.$match实例

db.col.aggregate( [
                { $match : { likes : { $gt : 90, $lte : 200 } } },
                { $group: { _id: null, count: { $sum: 1 } } }
               ] );

$match用于获取 likes 大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。

MongoDB Enterprise > db.col.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
{ "_id" : "penglei", "num_tutorial" : 3 }
MongoDB Enterprise >

以上实例类似sql语句:

select by_user as _id, count(*) as num_tutorial from mycol group by by_user

按日、按月、按年、按周、按小时、按分钟聚合操作如下:

db.getCollection('m_msg_tb').aggregate(
[
    {$match:{m_id:10001,mark_time:{$gt:new Date(2017,8,0)}}},
    {$group: {
       _id: {$dayOfMonth:'$mark_time'},
        pv: {$sum: 1}
        }
    },
    {$sort: {"_id": 1}}
])

时间关键字如下:

  • $dayOfYear: 返回该日期是这一年的第几天(全年 366 天)。
  • $dayOfMonth: 返回该日期是这一个月的第几天(1到31)。
  • $dayOfWeek: 返回的是这个周的星期几(1:星期日,7:星期六)。
  • $year: 返回该日期的年份部分。
  • $month: 返回该日期的月份部分( 1 到 12)。
  • $week: 返回该日期是所在年的第几个星期( 0 到 53)。
  • $hour: 返回该日期的小时部分。
  • $minute: 返回该日期的分钟部分。
  • $second: 返回该日期的秒部分(以0到59之间的数字形式返回日期的第二部分,但可以是60来计算闰秒)。
  • $millisecond:返回该日期的毫秒部分( 0 到 999)。
  • $dateToString: { $dateToString: { format: , date: } }。

5. MongoDB 主从复制(副本集)

MongoDB复制是将数据同步在多个服务器的过程。

复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。

复制还允许您从硬件故障和服务中断中恢复数据。

官方文档 https://docs.mongodb.com/manual/replication/

5.1 什么是复制?

  • 保障数据的安全性
  • 数据高可用性 (24*7)
  • 灾难恢复
  • 无需停机维护(如备份,重建索引,压缩)
  • 分布式读取数据

5.2 MongoDB复制原理

mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。

mongodb各个节点常见的搭配方式为:一主一从、一主多从。

主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

MongoDB复制结构图如下所示:

以上结构图中,客户端从主节点读取数据,在客户端写入数据到主节点时, 主节点与从节点进行数据交互保障数据的一致性。

5.3 副本集特征

  • N 个节点的集群
  • 任何节点可作为主节点
  • 所有写入操作都在主节点上
  • 自动故障转移
  • 自动恢复

5.4 MongoDB副本集设置

1、关闭正在运行的MongoDB服务器。

service mongod stop

2.节点建点

首先需要去你选择的mongodb数据文件存放的文件夹新建三个数据库,用来模拟三台不通的机器,博主的路径如下

mkdir -p /data/db/node1
mkdir -p /data/db/node2
mkdir -p /data/db/node3

3.启动三个数据库(dbpath),并且端口(--port 1000x),集群名称(--replSet gabriel),关闭日志选项(--nojournal),守护进程方式启动,会自动拉起(--fork),日志目录(--logpath)

mongod --dbpath /data/db/node1 --port 10001 --replSet gabriel --nojournal --fork --logpath /data/db/node1.log
mongod --dbpath /data/db/node2 --port 10002 --replSet gabriel --nojournal --fork --logpath /data/db/node2.log
mongod --dbpath /data/db/node3 --port 10003 --replSet gabriel --nojournal --fork --logpath /data/db/node3.log

4.顺便连接一个服务器,做初始化操作,这里博主连入10001端口

终端下进入

mongo localhost:10001

进入后输入初始化方法

MongoDB Enterprise gabriel:OTHER> rs.initiate({_id:"gabriel",members:[
{_id:1,host:"localhost:10001"},
{_id:2,host:"localhost:10002"},
{_id:3,host:"localhost:10003"},
]})

收到如下信息就成功了。

{
    "ok" : 1,
    "operationTime" : Timestamp(1517221411, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1517221411, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}
MongoDB Enterprise gabriel:OTHER>

此时会发现终端上的输出已经有了变化。

//从单个一个
>
//变成了
gabriel:OTHER>

5.查询状态

MongoDB Enterprise gabriel:OTHER> rs.status()
{
    "set" : "gabriel",
    "date" : ISODate("2018-01-29T10:33:21.227Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1517221984, 1),
            "t" : NumberLong(1)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1517221984, 1),
            "t" : NumberLong(1)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1517221994, 1),
            "t" : NumberLong(1)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1517221994, 1),
            "t" : NumberLong(1)
        }
    },
    "members" : [
        {
            "_id" : 1,
            "name" : "localhost:10001",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 659,
            "optime" : {
                "ts" : Timestamp(1517221994, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-01-29T10:33:14Z"),
            "electionTime" : Timestamp(1517221422, 1),
            "electionDate" : ISODate("2018-01-29T10:23:42Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "localhost:10002",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 589,
            "optime" : {
                "ts" : Timestamp(1517221994, 1),
                "t" : NumberLong(1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1517221984, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-01-29T10:33:14Z"),
            "optimeDurableDate" : ISODate("2018-01-29T10:33:04Z"),
            "lastHeartbeat" : ISODate("2018-01-29T10:33:20.972Z"),
            "lastHeartbeatRecv" : ISODate("2018-01-29T10:33:19.923Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "localhost:10001",
            "configVersion" : 1
        },
        {
            "_id" : 3,
            "name" : "localhost:10003",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 589,
            "optime" : {
                "ts" : Timestamp(1517221994, 1),
                "t" : NumberLong(1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1517221984, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-01-29T10:33:14Z"),
            "optimeDurableDate" : ISODate("2018-01-29T10:33:04Z"),
            "lastHeartbeat" : ISODate("2018-01-29T10:33:20.972Z"),
            "lastHeartbeatRecv" : ISODate("2018-01-29T10:33:19.921Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "localhost:10001",
            "configVersion" : 1
        }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1517221994, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1517221994, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

在返回中,参数set后面为集群名称,每个members下面可以看到他们各自的情况,其中stateStr是角色,主节点为(PRIMARY)。

6.进入主节点插入数据,进入从节点查看数据

博主主节点在10001接口

mongo localhost:10001

插入数据

MongoDB Enterprise gabriel:PRIMARY> use test
switched to db test
db.col.insert({title: 'MongoDB 教程', 
    description: 'MongoDB 是一个 Nosql 数据库',
    by: '搜云库教程-专注于开发技术的研究与知识分享',
    url: 'http://www.souyunku.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 100
})
MongoDB Enterprise gabriel:PRIMARY> db.col.find()
{ "_id" : ObjectId("5a6ef998525d903d07a00cdf"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "搜云库教程-专注于开发技术的研究与知识分享", "url" : "http://www.souyunku.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
MongoDB Enterprise gabriel:PRIMARY> 

博主切换从节点10002

mongo localhost:10002

切换到从节点,你会发现使用show dbs 会报错,是因为还没有开启权限,输入rs.slaveOk();就可以顺利访问了。

MongoDB Enterprise gabriel:SECONDARY> show dbs
2018-01-29T10:40:37.362+0000 E QUERY    [thread1] Error: listDatabases failed:{
    "operationTime" : Timestamp(1517222434, 1),
    "ok" : 0,
    "errmsg" : "not master and slaveOk=false",
    "code" : 13435,
    "codeName" : "NotMasterNoSlaveOk",
    "$clusterTime" : {
        "clusterTime" : Timestamp(1517222434, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:65:1
shellHelper.show@src/mongo/shell/utils.js:813:19
shellHelper@src/mongo/shell/utils.js:703:15
@(shellhelp2):1:1
MongoDB Enterprise gabriel:SECONDARY> 
MongoDB Enterprise gabriel:SECONDARY> rs.slaveOk()

再次查看

MongoDB Enterprise gabriel:SECONDARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB
MongoDB Enterprise gabriel:SECONDARY> 

切到test 库,查看数据已经同步过来了

MongoDB Enterprise gabriel:SECONDARY> use test
switched to db test
MongoDB Enterprise gabriel:SECONDARY> db.col.find()
{ "_id" : ObjectId("5a6ef998525d903d07a00cdf"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "搜云库教程-专注于开发技术的研究与知识分享", "url" : "http://www.souyunku.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
MongoDB Enterprise gabriel:SECONDARY> 

以上就是简单的主从复制建立过程,现在已经可以在从服务器看到主服务器插入的数据了。

切换从节点10003 一样的问题

删除从节点

rs.remove('ip:port')

关闭主服务器后,再重新启动,会发现原来的从服务器变为了从服务器,新启动的服务器(原来的从服务器)变为了从服务器

6. MongoDB 自动故障转移

首先通过 rs.status() 查看,可以看到主节点是10001,主节点"name" : "localhost:10001", "stateStr" : "PRIMARY" 接下来停止 10001 主节点,测试故障切换

MongoDB Enterprise gabriel:PRIMARY>  rs.status()
{
    "set" : "gabriel",
    "date" : ISODate("2018-01-30T02:39:58.468Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1517279986, 1),
            "t" : NumberLong(1)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1517279986, 1),
            "t" : NumberLong(1)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1517279996, 1),
            "t" : NumberLong(1)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1517279996, 1),
            "t" : NumberLong(1)
        }
    },
    "members" : [
        {
            "_id" : 1,
            "name" : "localhost:10001",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 58656,
            "optime" : {
                "ts" : Timestamp(1517279996, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-01-30T02:39:56Z"),
            "electionTime" : Timestamp(1517221422, 1),
            "electionDate" : ISODate("2018-01-29T10:23:42Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "localhost:10002",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 58586,
            "optime" : {
                "ts" : Timestamp(1517279996, 1),
                "t" : NumberLong(1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1517279986, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2018-01-30T02:39:56Z"),
            "optimeDurableDate" : ISODate("2018-01-30T02:39:46Z"),
            "lastHeartbeat" : ISODate("2018-01-30T02:39:58.289Z"),
            "lastHeartbeatRecv" : ISODate("2018-01-30T02:39:57.220Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "localhost:10001",
            "configVersion" : 1
        },
        {
            "_id" : 3,
            "name" : "localhost:10003",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2018-01-30T02:39:58.304Z"),
            "lastHeartbeatRecv" : ISODate("2018-01-30T02:39:21.208Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "Connection refused",
            "configVersion" : -1
        }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1517279996, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1517279996, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}
MongoDB Enterprise gabriel:PRIMARY>

连接到主节点

mongo localhost:10001

关闭主节点

mongo localhost:10001

显示所有数据库

MongoDB Enterprise gabriel:PRIMARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB

切换到admin

MongoDB Enterprise gabriel:PRIMARY> use admin
switched to db admin

停止数据库,必须进入 admin 库

MongoDB Enterprise gabriel:PRIMARY> db.shutdownServer()

响应

2018-01-30T02:51:34.503+0000 I NETWORK  [thread1] trying reconnect to localhost:10001 (127.0.0.1) failed
2018-01-30T02:51:35.398+0000 I NETWORK  [thread1] Socket recv() Connection reset by peer 127.0.0.1:10001
2018-01-30T02:51:35.398+0000 I NETWORK  [thread1] SocketException: remote: (NONE):0 error: SocketException socket exception [RECV_ERROR] server [127.0.0.1:10001] 
2018-01-30T02:51:35.399+0000 I NETWORK  [thread1] reconnect localhost:10001 (127.0.0.1) failed failed 
2018-01-30T02:51:35.404+0000 I NETWORK  [thread1] trying reconnect to localhost:10001 (127.0.0.1) failed
2018-01-30T02:51:35.404+0000 W NETWORK  [thread1] Failed to connect to 127.0.0.1:10001, in(checking socket for error after poll), reason: Connection refused
2018-01-30T02:51:35.404+0000 I NETWORK  [thread1] reconnect localhost:10001 (127.0.0.1) failed failed 
MongoDB Enterprise >

查看是否真的停止了,发现已经没有10001 节点进程了

root@souyunku-2:# ps -ef | grep mongo
root      5554     1  0 Jan29 ?        00:03:34 mongod --dbpath /data/db/node2 --port 10002 --replSet gabriel --nojournal --fork --logpath /data/db/node2.log
root     12284     1  0 02:43 ?        00:00:02 mongod --dbpath /data/db/node3 --port 10003 --replSet gabriel --nojournal --fork --logpath /data/db/node3.log
root     12436  5132  0 02:53 pts/1    00:00:00 grep --color=auto mongo
root@souyunku-2:# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:10002         0.0.0.0:*               LISTEN      5554/mongod     
tcp        0      0 127.0.0.1:10003         0.0.0.0:*               LISTEN      12284/mongod          
root@souyunku-2:/data/db#

查看是否故障专业

root@souyunku-2:# mongo localhost:10001

查看主从状态

MongoDB Enterprise gabriel:SECONDARY> rs.status()
{
    "set" : "gabriel",
    "date" : ISODate("2018-01-30T02:56:48.074Z"),
    "myState" : 2,
    "term" : NumberLong(2),
    "syncingTo" : "localhost:10003",
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1517280995, 1),
            "t" : NumberLong(2)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1517280995, 1),
            "t" : NumberLong(2)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1517281005, 1),
            "t" : NumberLong(2)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1517280995, 1),
            "t" : NumberLong(2)
        }
    },
    "members" : [
        {
            "_id" : 1,
            "name" : "localhost:10001",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2018-01-30T02:56:47.605Z"),
            "lastHeartbeatRecv" : ISODate("2018-01-30T02:51:34.519Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "Connection refused",
            "configVersion" : -1
        },
        {
            "_id" : 2,
            "name" : "localhost:10002",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 59660,
            "optime" : {
                "ts" : Timestamp(1517281005, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2018-01-30T02:56:45Z"),
            "syncingTo" : "localhost:10003",
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 3,
            "name" : "localhost:10003",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 784,
            "optime" : {
                "ts" : Timestamp(1517281005, 1),
                "t" : NumberLong(2)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1517281005, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2018-01-30T02:56:45Z"),
            "optimeDurableDate" : ISODate("2018-01-30T02:56:45Z"),
            "lastHeartbeat" : ISODate("2018-01-30T02:56:46.486Z"),
            "lastHeartbeatRecv" : ISODate("2018-01-30T02:56:47.147Z"),
            "pingMs" : NumberLong(0),
            "electionTime" : Timestamp(1517280703, 1),
            "electionDate" : ISODate("2018-01-30T02:51:43Z"),
            "configVersion" : 1
        }
    ],
    "ok" : 1,
    "operationTime" : Timestamp(1517281005, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1517281005, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}
MongoDB Enterprise gabriel:SECONDARY> 

发现 "name" : "localhost:10001","stateStr" : "(not reachable/healthy)", 健康状态已经是“无法访问状态了”

主节点已经切换成 10003 节点了

"_id" : 3,
"name" : "localhost:10003",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",

重启节点10001

mongod --dbpath /data/db/node1 --port 10001 --replSet gabriel --nojournal --fork --logpath /data/db/node1.log

参考:

Runoob 教程:http://www.runoob.com/mongodb...
Tutorials 教程:Pointhttps://www.tutorialspoint.co...
MongoDB 官网地址:https://www.mongodb.com
MongoDB 官方英文文档:https://docs.mongodb.com/manual
MongoDB 各平台下载地址:https://www.mongodb.com/downl...
MongoDB 安装 https://docs.mongodb.com/manu...

Contact

关注公众号-搜云库


架构师专栏
6.2k 声望7k 粉丝