Skip to content

文章 CSDN 地址:https://blog.csdn.net/weixin_42039228/article/details/123657641?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166298847916782428658314%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166298847916782428658314&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-123657641-null-null.142^v47^control,201^v3^add_ask&utm_term=mogodb&spm=1018.2226.3001.4187

1.MongoDB

1.1 MongoDB 概述

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

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。

它支持的数据结构非常松散,是类似 json 的 bson 格式,因此可以存储比较复杂的数据类型。

Mongo 最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

MongoDB 服务端可运行在 Linux、Windows 平台,支持 32 位和 64 位应用,默认端口为 27017。

推荐运行在 64 位平台,因为 MongoDB 在 32 位模式运行时支持的最大文件尺寸为 2GB。

1.2 MongoDB 主要特点

1.2.1 文档

MongoDB 中的记录是一个文档,它是由 字段和值组成的键值对 组成的数据结构。

多个键及其关联的值有序地放在一起就构成了文档。

MongoDB 文档类似于 JSON 对象。字段的值可以包括其他文档,数组和文档数组。

img

这个文档只有一个键“greeting”,对应的值为“hello,world”。多数情况下,文档比这个更复杂,它包含多个键/值对。

例如:{“greeting”:“hello,world”,“foo”: 3} 文档中的键/值对是有序的,下面的文档与上面的文档是完全不同的两个文档。

文档中的值不仅可以是双引号中的字符串,也可以是其他的数据类型,例如,整型、布尔型等,也可以是另外一个文档,即文档可以嵌套。文档中的键类型只能是字符串

使用文档的优点是:

  • 文档(即对象)对应于许多编程语言中的本机数据类型
  • 嵌入式文档和数组减少了对昂贵连接的需求
  • 动态模式支持流畅的多态性

1.3.2 集合

集合就是一组文档,类似于关系数据库中的表。

集合是无模式的,集合中的文档可以是各式各样的。例如,{“hello,word”:“Mike”}和{“foo”: 3},它们的键不同,值的类型也不同,但是它们可以存放在同一个集合中,也就是不同模式的文档都可以放在同一个集合中。

既然集合中可以存放任何类型的文档,那么为什么还需要使用多个集合? 这是因为所有文档都放在同一个集合中,无论对于开发者还是管理员,都很难对集合进行管理,而且这种情形下,对集合的查询等操作效率都不高。所以在实际使用中,往往将文档分类存放在不同的集合中。 例如,对于网站的日志记录,可以根据日志的级别进行存储,Info 级别日志存放在 Info 集合中,Debug 级别日志存放在 Debug 集合中,这样既方便了管理,也提供了查询性能。 但是需要注意的是,这种对文档进行划分来分别存储并不是 MongoDB 的强制要求,用户可以灵活选择。

可以使用“.”按照命名空间将集合划分为子集合。 例如,对于一个博客系统,可能包括 blog.user 和 blog.article 两个子集合,这样划分只是让组织结构更好一些,blog 集合和 blog.user、blog.article 没有任何关系。虽然子集合没有任何特殊的地方,但是使用子集合组织数据结构清晰,这也是 MongoDB 推荐的方法。

1.3.3 数据库

MongoDB 中多个文档组成集合,多个集合组成数据库。

一个 MongoDB 实例可以承载多个数据库。它们之间可以看作相互独立,每个数据库都有独立的权限控制。在磁盘上,不同的数据库存放在不同的文件中。

MongoDB 中存在以下系统数据库。

  • Admin 数据库:一个权限数据库,如果创建用户的时候将该用户添加到 admin 数据库中,那么该用户就自动继承了所有数据库的权限。
  • Local 数据库:这个数据库永远不会被复制,可以用来存储本地单台服务器的任意集合。
  • Config 数据库:当 MongoDB 使用分片模式时,config 数据库在内部使用,用于保存分片的信息。

1.3.4 数据模型

一个 MongoDB 实例可以包含一组数据库,一个 DataBase 可以包含一组 Collection(集合),一个集合可以包含一组 Document(文档)。

一个 Document 包含一组 field(字段),每一个字段都是一个 key/value pair

  • key: 必须为字符串类型
  • value:可以包含如下类型
    - 基本类型,例如,string,int,float,timestamp,binary 等类型 - 一个document - 数组类型

1.4 Windows 安装 MongoDB

1.4.1 下载 MongoDB

MongoDB 提供了可用于 32 位系统和 64 位系统的预编译二进制包(新版本没有了 32 位系统的安装文件),你可以进入 MongoDB 官网下载安装,MongoDB 的预编译二进制包的下载地址为:https://www.mongodb.com/download-center/community,打开之后会看到如下图,直接点击Download下载即可,也可以在Version中选择你想要的版本:

img

1.4.2 安装 MongoDB

双击打开文件进行安装,在安装过程中,可以通过点击 “Custom(自定义)” 按钮来设置你的安装目录。

img

这里我选择安装在 E:\MongoDB 这个目录下(安装目录会影响我们后面的配置)

img

这里选择直接 next

img

这里安装 "Install MongoDB Compass"不勾选,否则可能要很长时间都一直在执行安装,MongoDB Compass 是一个图形界面管理工具,这里不安装也是没有问题的,可以自己去下载一个图形界面管理工具,比如 Robo3T。

img

之后稍微等待一会就安装好了。

1.4.3 配置 MongoDB

MongoDB 的安装过程是很简单的,但是配置就比较麻烦了,可能会遇到各种各样的问题,需要你有足够的耐心和仔细。

首先要在 MongoDB 的 data 文件夹里新建一个 db 文件夹和一个 log 文件夹:

然后在 log 文件夹下新建一个 mongo.log:

img

然后将 E:\MongoDB\bin 添加到环境变量 path 中,此时打开 cmd 窗口运行一下 mongo 命令,出现如下情况:

img

这是为什么呢?这是因为我们还没有启动 MongoDB 服务,自然也就连接不上服务了。那要怎么启动呢?在 cmd 窗口中运行如下命令:

java
mongod --dbpath E:\MongoDB\data\db

需要注意的是:如果你没有提前创建 db 文件夹,是无法启动成功的。运行成功之后,我们打开浏览器,输入 127.0.0.1:27017,看到如下图,就说明 MongoDB 服务已经成功启动了。

img

但是如果每次都要这么启动服务的话也太麻烦了吧,这里你可以选择设置成开机自启动,也可以选择用命令 net start mongodb 来手动启动,这里我选择使用后者,具体方法如下。

还是打开 cmd 窗口,不过这次是以管理员身份运行,然后输入如下命令:

java
mongod --dbpath "E:\MongoDB\data\db" --logpath "E:\MongoDB\data\log\mongo.log" -install -serviceName "MongoDB"

如果没有报错的话就说明成功添加到服务里了,可以使用 win+R 然后输入 services.msc 命令进行查看:

img

默认是自动运行的,这里我选择把它改成手动的。然后在 cmd 窗口中运行 net start mongodb:

img

怎么解决呢?两个步骤:

1)运行 sc delete mongodb 删除服务;

2)再运行一次配置服务的命令:

java
mongod --dbpath "E:\MongoDB\data\db" --logpath "E:\MongoDB\data\log\mongo.log" -install -serviceName "MongoDB"

然后再运行 net start mongodb,服务启动成功:

img

可能遇到的问题:

1、mongod 不是内部或外部命令 出现这种问题说明你没有把 bin 目录添加到环境变量之中,重新添加一下即可解决。

2、服务名无效 首先是看你输入的服务名称是否有误,然后再查看本地服务中有没有 MongoDB 服务,如果没有服务,则运行命令添加服务即可。

3、发生服务特定错误:100 删除 db 文件夹下的 mongod.lock 和 storage.bson 两个文件,若删除完之后仍然出现这种问题,用 sc delete mongodb 删除服务,再配置一下服务就能解决了。

1.4.4 安装一个可视化工具

官网下载 RoBo 3T(Robomongo is now Robo 3T) 下载地址:https://robomongo.org/download

img

img

双击安装包安装,修改安装路径,不停下一步,点击安装。

img

打开后,有一个填信息的页面,name、email,暂时不用管,直接 finish。 启动 MongoDB 服务。 点击弹出框中的 create,创建新连接,可以修改连接名 name,连接 IP(下图 IP 为本地 IP),端口(默认)

img

连接成功后,右击 localhost,选择 create Database,创建数据库

img

创建数据库 firstTest,然后右击 firstTest,选择 open Shell,开始进行 shell 命令来创建数据库中的集合和文档。

img

网上的关于 6.0.1 版本的安装:

CSDN 地址:https://blog.csdn.net/weixin_70319460/article/details/126436904?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166299544316800182128116%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=166299544316800182128116&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-3-126436904-null-null.142^v47^control,201^v3^add_ask&utm_term=mongodb6.0.1下载安装教程&spm=1018.2226.3001.4187

运行:在其 bin 目录下,在命令提示符窗口输入mongod,弹出以下内容后继续输入mongosh,弹出以下内容后继续输入show dbs

1.5 Linux 安装 MongoDB

1.5.1 下载 MongoDB

官方下载地址:https://www.mongodb.com/download-center/community

java
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.1.tgz

1.5.2 解压安装

1、解压

java
tar -zxvf mongodb-linux-x86_64-rhel70-4.2.1.tgz

2、创建目录/usr/local/mongo,并将解压完的 mongodb 目录移动到/usr/local/mongo 下

java
mkdir -p /usr/local/mongo
mv mongodb-linux-x86_64-rhel70-4.2.1/* /usr/local/mongo/

3、切到/usr/local/mongo 目录下,创建目录

java
mkdir -p data/db        #数据库目录
mkdir -p logs           #日志目录
mkdir -p conf           #配置文件目录
mkdir -p pids           #进程描述文件目录

创建好的目录如下:

img

4、在 conf 目录,增加配置文件 mongo.conf

java
vi /usr/local/mongo/conf/mongo.conf
java
#数据保存路径
dbpath=/usr/local/mongo/data/db/
#日志保存路径
logpath=/usr/local/mongo/logs/mongo.log
#进程描述文件
pidfilepath=/usr/local/mongo/pids/mongo.pid
#日志追加写入
logappend=true
bind_ip_all=true
#mongo默认端口
port=27017
#操作日志容量
oplogSize=10000
#开启子进程
fork=true

5、通过配置文件启动 mongo 服务端

java
/usr/local/mongo/bin/mongod -f /usr/local/mongo/conf/mongo.conf

启动成功如下:

img

6、启动 mongo 客户端

java
/usr/local/mongo/bin/mongo --host 127.0.0.1 --port 27017

启动成功如下:

img

至此安装完成~

1.6 MongoDB 基本操作及增删改查

1.6.1 基本操作:针对数据库和集合

登陆数据库

java
mongo

查看数据库

java
show databases;

img

选择数据库

use 数据库名

img

如果切换到一个没有的数据库,例如 use admin2,那么会隐式创建这个数据库。(后期当该数据库有数据时,系统自动创建)

java
use admin2

img

查看集合

java
show collections

img

创建集合

java
db.createCollection('集合名')

img

删除集合

java
db.集合名.drop()

img

删除数据库

通过 use 语法选择数据库 通过 db.dropDataBase()删除数据库

img

1.6.2 增加文档

java
db.集合名.insert(JSON数据)

如果集合存在,那么直接插入数据。如果集合不存在,那么会隐式创建。

示例:在 test2 数据库的 c1 集合中插入数据(姓名叫 webopenfather 年龄 18 岁)

java
use test2 db.c1.insert({uname:"webopenfather",age:18})
  • 数据库和集合不存在都隐式创建

  • 对象的键统一不加引号(方便看),但是查看集合数据时系统会自动加

  • mongodb 会给每条数据增加一个全球唯一的_id 键

    img

    • _id 键的组成

      img
    • 自己增加_id 可以,只需要给插入的 JSON 数据增加_id 键即可覆盖(但实战强烈不推荐)db.c1.insert({_id:1, uname:"webopenfather", age:18})

一次性插入多条数据 传递数据,数组中写一个个 JSON 数据即可

java
db.c1.insert([
    {uname:"z3", age:3},
    {uname:"z4", age:4},
    {uname:"w5", age:5}
])

快速插入 10 条数据 由于 mongodb 底层使用 JS 引擎实现的,所以支持部分 js 语法。因此:可以写 for 循环

java
for (var i=1; i<=10; i++) {
    db.c2.insert({uanme: "a"+i, age: i})
}

1.6.3 查询文档

第一种写法:

java
db.集合名.find(条件,{查询的列})
// 查询的列:相当于select的限制字段
// 列:0 就是除了这列都查
// 列:1 就是只查这列

第二种写法:

java
db.集合名.find({
:{运算符:值}
            })
运算符作用
$gt大于
$gte大于等于
$lt小于
$lte小于等于
$ne不等于
$inin
$ninnot in

实例练习

查询所有数据

java
db.c1.find()

img

系统的_id 无论如何都会存在

1、查询 age 大于 5 的数据

java
db.c1.find({age:{$gt:5}})

img

2、查询年龄是 5 岁、8 岁、10 岁的数据

java
db.c2.find({age:{$in:[5,8,10]}})

img

3、只看年龄列,或者年龄以外的列

img

1.6.4 修改文档

方式一:不使用修改器

java
db.集合名.update(条件,新数据,[是否新增,是否修改多条])
//相当于:if(条件成立){
    //原数据 = 新数据
//}

如果不使用修改器,那么会将新数据将直接替换原来的数据,而不是修改其他列!

方式二:使用修改器

java
db.集合名.update(条件,{修改器:{键:值}},[是否新增,是否修改多条])
//修改器就是要进行的操作:不光是数据,而是数据和操作的结合!!!
//相当于:if(条件成立){
    //修改器
//}

修改器:inc 递增/递减、rename 重命名列、set 修改列值、unset 删除列

  • 是否新增 指条件不匹配,则插入这个新数据(true 是插入,false 否不插入默认) db.c3.update({uname:"zs30"},{$set:{age:30}},true)

  • 是否修改多条 指将匹配成功的数据都修改(true 是,false 否默认)

    默认只修改一条

    db.c3.update({uname:"zs2"},{$set:{age:30}},false,true)

实例练习

准备工作

java
use test2;
for(var i = 1; i<= 10; i++){
	db.c3.insert( {"uname":"zs"+i,"age":i} );
}

1、将{uname:"zs1"}改为

java
db.c3.update({uname:"zs1"},{$set:{uname:"zs2"}})

img

2、给{uname:"zs10"}的年龄加 2 岁或减 2 岁

java
db.c3.update({uname:"zs10"},{$inc:{age:2}})

img

递减只需要将 2 改为-2 即可。

综合练习插入数据:

db.c4.insert( {uname:"神龙教主",age:888,who:"男",other:"非国人"});

1.6.5 删除文档

java
db.集合名.remove(条件[,是否删除一条])
  • 是否删除一条 true:是(删除的数据为第一条)

img

false:否

java
db.c3.remove({uname:"zs3"})

img

1.6.6 总结

增 Create

java
db.集合名.insert(JSON数据)

删 Delete

java
db.集合名.remove(条件 ,[是否删除一条true是false否默认])

也就是默认删除多条

改 Update

java
db.集合名.update(条件,新数据,[是否新增],[是否修改多条]) //条件:就是类似{uname:"zs10"}

升级语法:可以指定操作,不一定是用新值覆盖旧值了!
db.集合名.update(条件,{修改器:{键:值}})

查 Read

java
db.集合名.find(条件,[查询的列])

升级语法:写条件更加方便,但是无法指定要查几个字段!
db.集合名.find({键: {运算符:值}})

1.7 MongoDB 存储数据类型

MongoDB 中每条记录称作一个文档,这个文档和我们平时用的 JSON 有点像,但也不完全一样。

JSON 是一种轻量级的数据交换格式。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言,JSON 易于阅

读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率,但是 JSON 也有它的局限性,比如它只

有 null、布尔、数字、字符串、数组和对象这几种数据类型,没有日期类型,只有一种数字类型,无法区

分浮点数和整数,也没法表示正则表达式或者函数

由于这些局限性,BSON 闪亮登场啦,BSON 是一种类 JSON 的二进制形式的存储格式,简称 Binary JSON,它

和 JSON 一样,支持内嵌的文档对象和数组对象,但是BSON 有 JSON 没有的一些数据类型,如 Date 和 BinData

类型,MongoDB 使用 BSON 做为文档数据存储和网络传输格式。

1.7.1 数字

shell 默认使用 64 位浮点型数值,如下:

java
db.sang_collec.insert({x:3.1415926})
db.sang_collec.insert({x:3})

对于整型值,我们可以使用 NumberInt 或者 NumberLong 表示,如下:

java
db.sang_collec.insert({x:NumberInt(10)})
db.sang_collec.insert({x:NumberLong(12)})

1.7.2 字符串

字符串也可以直接存储,如下:

java
db.sang_collec.insert({x:"hello MongoDB!"})

1.7.3 正则表达式

正则表达式主要用在查询里边,查询时我们可以使用正则表达式,语法和 JavaScript 中正则表达式的语法

相同,比如查询所有 key 为 x,value 以 hello 开始的文档且不区分大小写:

java
db.sang_collec.find({x:/^(hello)(.[a-zA-Z0-9])+/i})

1.7.4 数组

数组一样也是被支持的,如下:

java
db.sang_collec.insert({x:[1,2,3,4,new Date()]})

数组中的数据类型可以是多种多样的。

1.7.5 日期

MongoDB 支持 Date 类型的数据,可以直接 new 一个 Date 对象,如下:

java
db.sang_collec.insert({x:new Date()})

1.7.6 内嵌文档

一个文档也可以作为另一个文档的 value,这个其实很好理解,如下:

java
db.sang_collect.insert({name:"三国演义",author:{name:"罗贯中",age:99}});

书有一个属性是作者,作者又有 name,年龄等属性。

也就是对象里面套对象形式的 json 罢了!

1.8 MongoDB 中的索引

1.8.1 索引创建

默认情况下,集合中的_id 字段就是索引,我们可以通过 getIndexes()方法来查看一个集合中的索引:

==其实 mogo 中的索引就是类似于 mysql 中的主键的意思,但是 mogo 中的索引可以有若干个,本质上是为了加快查询的速度!==

java
db.sang_collect.getIndexes()

结果如下:

java
[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "sang.sang_collect"
    }
]

我们看到这里只有一个索引,就是_id。

现在我的集合中有 10000 个文档,我想要查询 x 为 1 的文档,我的查询操作如下:

java
db.sang_collect.find({x:1})

这种查询默认情况下会做全表扫描,我们可以用上篇文章介绍的 explain("executionStats")来查看一下查询计划,如下:

java
db.sang_collect.find({x:1}).explain("executionStats")

结果如下:

java
{
    "queryPlanner" : {
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1,
        "executionTimeMillis" : 15,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 10000,
        "executionStages" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "x" : {
                    "$eq" : 1.0
                }
            },
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 29,
            "works" : 10002,
            "advanced" : 1,
            "needTime" : 10000,
            "needYield" : 0,
            "saveState" : 78,
            "restoreState" : 78,
            "isEOF" : 1,
            "invalidates" : 0,
            "direction" : "forward",
            "docsExamined" : 10000
        }
    },
    "serverInfo" : {
    },
    "ok" : 1.0
}

结果比较长,我摘取了关键的一部分。

我们可以看到查询方式是全表扫描,一共扫描了 10000 个文档才查出来我要的结果。

实际上我要的文档就排第二个,但是系统不知道这个集合中一共有多少个 x 为 1 的文档,所以会把全表扫描

完,这种方式当然很低效,但是如果我加上 limit,如下:

java
db.sang_collect.find({x:1}).limit(1)

此时再看查询计划发现只扫描了两个文档就有结果了,但是如果我要查询 x 为 9999 的记录,那还是得把全表扫描一遍,此时,我们就可以给该字段建立索引,索引建立方式如下:

相当于用索引 x 替换了原来的_id

java
db.sang_collect.ensureIndex({x:1})

1 表示升序,-1 表示降序。当我们给 x 字段建立索引之后,再根据 x 字段去查询,速度就非常快了,我们看下面这个查询操作的执行计划:

java
db.sang_collect.find({x:9999}).explain("executionStats")

这个查询计划过长我就不贴出来了,我们可以重点关注查询要耗费的时间大幅度下降。

此时调用 getIndexes()方法可以看到我们刚刚创建的索引,如下:

java
//注:这里实际上是添加索引,并不是修改索引
[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "sang.sang_collect"
    },
    {
        "v" : 2,
        "key" : {
            "x" : 1.0
        },
        "name" : "x_1",
        "ns" : "sang.sang_collect"
    }
]

我们看到每个索引都有一个名字,默认的索引名字为字段名_排序值,当然我们也可以在创建索引时自定义索引名字,如下:

java
db.sang_collect.ensureIndex({x:1},{name:"myfirstindex"})

此时创建好的索引如下:

java
{
    "v" : 2,
    "key" : {
        "x" : 1.0
    },
    "name" : "myfirstindex",
    "ns" : "sang.sang_collect"
}

当然索引在创建的过程中还有许多其他可选参数,如下:

java
db.sang_collect.ensureIndex(
    {
        x:1
    },
    {
        name:"myfirstindex",
        dropDups:true,
        background:true,
        unique:true,
        sparse:true,
        v:1,
        weights:99999
    }
)

关于这里的参数,我说一下:

1.name表示索引的名称

2.dropDups表示创建唯一性索引时如果出现重复,则将重复的删除,只保留第一个

3.background是否在后台创建索引,在后台创建索引不影响数据库当前的操作,默认为false

4.unique是否创建唯一索引,默认false

5.sparse对文档中不存在的字段是否不起用索引,默认false

6.v表示索引的版本号,默认为2

7.weights表示索引的权重

此时创建好的索引如下:

java
{
    "v" : 1,
    "unique" : true,
    "key" : {
        "x" : 1.0
    },
    "name" : "myfirstindex",
    "ns" : "sang.sang_collect",
    "background" : true,
    "sparse" : true,
    "weights" : 99999.0
}

1.8.2 查看索引

getIndexes()可以用来查看索引,我们还可以通过 totalIndexSize()来查看索引的大小,如下:

java
db.sang_collect.totalIndexSize()

1.8.3 删除索引

我们可以按名称删除索引,如下:

java
db.sang_collect.dropIndex("xIndex")

表示删除一个名为 xIndex 的索引,当然我们也可以删除所有索引,如下:

java
db.sang_collect.dropIndexes()

1.8.4 总结

索引是个好东西,可以有效的提高查询速度,但是索引会降低插入、更新和删除的速度,因为这些操作不仅要更新文档,还要更新索引,MongoDB 限制每个集合上最多有 64 个索引,我们在创建索引时要仔细斟酌索引的字段。

1.8.5 理解 mogo 索引

mogo 的索引相当于是一个文档的标志

相当于是一个集合的主键,创建索引其实就是增加了被索引的字段,而不是替换了原来的索引,也就是说 mogo 里面的主键不止一个!

==策略:可以把要查询的字段创建成为索引,这样查询起来速度会快很多!==

Mogo 相对于 mysql 所做的改变:

Mogo 是让一个集合(相当于 mysql 中的表)拥有多种表(mysql 中的)的结构

而 mysql 中的表(相当于 mogo 中的集合)是所有集合(mogo 中的)的相同的字段部分组成一个表

Mogo 更适合 给一个集合实现包含多种不同数据结构的对象 的场景

1.9 Java 操作 MongoDB

1.9.1 方式一:原生 java

方式一采用的原生 Java 操作 MongoDB

1.9.1.1 前期准备

首先我们需要驱动,MongoDB 的 Java 驱动我们可以直接在 Maven 中央仓库去下载,也可以创建 Maven 工程添加如下依赖:

xml
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>3.5.0</version>
</dependency>

建议通过 Maven 来添加依赖,如果自己下载 jar,需要下载如下三个 jar:

java
1.org.mongodb:bson:jar:3.5.0
2.org.mongodb:mongodb-driver-core:jar:3.5.0
3.org.mongodb:mongodb-driver:jar:3.5.0

另外,在使用 Java 操作 MongoDB 之前,记得启动 MongoDB

1.9.1.2 获取集合

所有准备工作完成之后,我们首先需要一个 MongoClient,如下:

java
MongoClient client = new MongoClient("192.168.248.136", 27017);

然后通过如下方式获取一个数据库,如果要获取的数据库本身就存在,直接获取到,不存在 MongoDB 会自动创建:

java
MongoDatabase sang = client.getDatabase("sang");

然后通过如下方式获取一个名为 c1 的集合,这个集合存在的话就直接获取到,不存在的话 MongoDB 会自动创建出来,如下:

java
MongoCollection<Document> c = sang.getCollection("c1");

有了集合之后,我们就可以向集合中插入数据了。

1、增加操作

和在 shell 中的操作一样,我们可以一条一条的添加数据,也可以批量添加,添加单条数据操作如下:

java
Document d1 = new Document();
d1.append("name", "三国演义").append("author", "罗贯中");
c.insertOne(d1);

添加多条数据的操作如下:

java
List<Document> collections = new ArrayList<Document>();
Document d1 = new Document();
d1.append("name", "三国演义").append("author", "罗贯中");
collections.add(d1);
Document d2 = new Document();
d2.append("name", "红楼梦").append("author", "曹雪芹");
collections.add(d2);
c.insertMany(collections);

当然也可以通过 Robo 3T 查看修改结果:db.集合名.find()

img

2、修改操作

可以修改查到的第一条数据,操作如下:

java
c.updateOne(Filters.eq("author", "罗贯中"), new Document("$set", new Document("name", "三国演义123")));

上例中小伙伴们也看到了修改器要如何使用,不管是 inc,用法都一致,我这里不再一个一个演示。也可以修改查到的所有数据,如下:

java
c.updateMany(Filters.eq("author", "罗贯中"), new Document("$set", new Document("name", "三国演义456")));

3、删除操作

可以删除查到的一条数据,如下:

java
c.deleteOne(Filters.eq("author", "罗贯中"));

也可以删除查到的所有数据:

java
c.deleteMany(Filters.eq("author", "罗贯中"));

Filters 里边还有其他的查询条件,都是见名知意,不赘述。

4、查询操作

可以直接查询所有文档:

java
FindIterable<Document> documents = c.find();
MongoCursor<Document> iterator = documents.iterator();
while (iterator.hasNext()) {

    System.out.println(iterator.next());
}

也可以按照条件查询:

java
FindIterable<Document> documents = c.find(Filters.eq("author", "罗贯中"));
MongoCursor<Document> iterator = documents.iterator();
while (iterator.hasNext()) {

    System.out.println(iterator.next());
}

其他的方法基本都是见名知意,这里不再赘述。

5、验证问题

上面我们演示的获取一个集合是不需要登录 MongoDB 数据库的,如果需要登录,我们获取集合的方式改为下

面这种:

java
ServerAddress serverAddress = new ServerAddress("192.168.248.128", 27017);
List<MongoCredential> credentialsList = new ArrayList<MongoCredential>();
MongoCredential mc = MongoCredential.createScramSha1Credential("readuser","sang","123".toCharArray());
credentialsList.add(mc);
MongoClient client = new MongoClient(serverAddress,credentialsList);
MongoDatabase sang = client.getDatabase("sang");
c = sang.getCollection("c1");

MongoCredential 是一个凭证,第一个参数为用户名,第二个参数是要在哪个数据库中验证,第三个参数是密码的 char 数组,然后将登录地址封装成一个 ServerAddress,最后将两个参数都传入 MongoClient 中实现登录功能。

6、其他配置

在连接数据库的时候也可以设置连接超时等信息,在 MongoClientOptions 中设置即可,设置方式如下:

java
ServerAddress serverAddress = new ServerAddress("192.168.248.128", 27017);
List<MongoCredential> credentialsList = new ArrayList<MongoCredential>();
MongoCredential mc = MongoCredential.createScramSha1Credential("rwuser","sang","123".toCharArray());
credentialsList.add(mc);
MongoClientOptions options = MongoClientOptions.builder()
        //设置连接超时时间为10s
        .connectTimeout(1000*10)
        //设置最长等待时间为10s
        .maxWaitTime(1000*10)
        .build();
MongoClient client = new MongoClient(serverAddress,credentialsList,options);
MongoDatabase sang = client.getDatabase("sang");
c = sang.getCollection("c1");

1.9.2 方式二:springboot 项目

主要讲解 SpringBoot 操作 MongoDB 实现增删改查的功能

1、pom.xml 引入依赖

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

2、创建 application.yml

yml
spring:
  data:
    mongodb:
      host: 192.168.72.129
      database: studentdb

3、创建实体类 创建包 com.changan.mongodb,包下建包 pojo 用于存放实体类,创建实体类

java
package com.changan.mongdb.pojo;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serializable;

@Document(collection = "student")
public class Student implements Serializable {


    @Id
    private Long id;

    private String name;

    private String sex;

    private String age;

    private String introduce;

    public String getSex() {

        return sex;
    }

    public void setSex(String sex) {

        this.sex = sex;
    }

    public String getAge() {

        return age;
    }

    public void setAge(String age) {

        this.age = age;
    }

    public String getIntroduce() {

        return introduce;
    }

    public void setIntroduce(String introduce) {

        this.introduce = introduce;
    }

    public String getName() {

        return name;
    }

    public void setName(String name) {

        this.name = name;
    }

    public Long getId() {

        return id;
    }

    public void setId(Long id) {

        this.id = id;
    }
}

4、创建数据访问接口 com.changan.mongodb 包下创建 dao 包,包下创建接口

java
package com.changan.mongdb.dao;

import com.changan.mongdb.pojo.Student;

import java.util.List;
import java.util.Map;

public interface StudentDao {



    void save(Student student);

    void update(Student student);

    List<Student> findAll();

    void delete(Integer id);
}

5、创建业务逻辑类 com.changan.mongodb 包下创建 impl 包,包下创建类

java
package com.changan.mongdb.dao.impl;

import com.changan.mongdb.dao.StudentDao;
import com.changan.mongdb.pojo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.LookupOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class StudentDaoImpl implements StudentDao {


    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 新增信息
     * @param student
     */
    @Override
    public void save(Student student) {

        mongoTemplate.save(student);
    }

    /**
     * 修改信息
     * @param student
     */
    @Override
    public void update(Student student) {

        //修改的条件
        Query query = new Query(Criteria.where("id").is(student.getId()));

        //修改的内容
        Update update = new Update();
        update.set("name",student.getName());

        mongoTemplate.updateFirst(query,update,Student.class);
    }

    /**
     * 查询所有信息
     * @return
     */
    @Override
    public List<Student> findAll() {

        return mongoTemplate.findAll(Student.class);
    }

    /**
     * 根据id查询所有信息
     * @param id
     */
    @Override
    public void delete(Integer id) {

        Student byId = mongoTemplate.findById(1,Student.class);
        mongoTemplate.remove(byId);
    }

}

6、创建测试类

java
package com.changan.mongdb;

import com.changan.mongdb.dao.StudentDao;
import com.changan.mongdb.pojo.Student;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MongdbApplicationTests {


    @Autowired
    private StudentDao studentDao;

    /**
     * 查询所有信息
     */
    @Test
    public void findAll() {

        List<Student> all = studentDao.findAll();
        System.out.println(all.size());
    }

    /**
     * 新增信息
     */
    @Test
    public void save() {

        Student student = new Student();
        student.setId(6l);
        student.setName("宋人头");
        studentDao.save(student);
    }

    /**
     * 修改信息
     */
    @Test
    public void update() {

        Student student = new Student();
        student.setId(2l);
        student.setName("吴很帅");
        studentDao.update(student);
    }

    /**
     * 删除信息
     */
    @Test
    public void delete() {

        studentDao.delete(3);
    }
}

1.10 MongoDB 之副本集配置

1.10.1 MongoDB 主从复制

主从复制是 MongoDB 最早使用的复制方式, 该复制方式易于配置,并且可以支持任意数量的从节点服务器,与使用单节点模式相比有如下优点:

在从服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。

可配置读写分离,主节点负责写操作,从节点负责读操作,将读写压力分开,提高系统的稳定性。

img

MongoDB 的主从复制至少需要两个服务器或者节点。其中一个是主节点,负责处理客户端请求,其它的都是从节点,负责同步主节点的数据。

主节点记录在其上执行的所有写操作,从节点定期轮询主节点获取这些操作,然后再对自己的数据副本执行这些操作。由于和主节点执行了相同的操作,从节点就能保持与主节点的数据同步。

主节点的操作记录称为 oplog(operation log),它被存储在 MongoDB 的 local 数据库中。oplog 中的每个文档都代表主节点上执行的一个操作。需要重点强调的是 oplog 只记录改变数据库状态的操作。比如,查询操作就不会被存储在 oplog 中。这是因为 oplog 只是作为从节点与主节点保持数据同步的机制。

然而,主从复制并非生产环境下推荐的复制方式,主要原因如下两点:

1、灾备都是完全人工的 如果主节点发生故障失败,管理员必须关闭一个从服务器,然后作为主节点重新启动它。然后应用程序必须重新配置连接新的主节点。 2、数据恢复困难 因为 oplog 只在主节点存在,故障失败需要在新的服务器上创建新的 oplog,这意味着任意存在的节点需要重新从新的主节点同步 oplog。

因此,在新版本的 MongoDB 中已经不再支持使用主从复制这种复制方式了,取而代之的是使用副本集复制方式。

1.10.2 MongoDB 副本集

MongoDB 副本集(Replica Set)其实就是具有自动故障恢复功能的主从集群,和主从复制最大的区别就是在副本集中没有固定的“主节点;整个副本集会选出一个节点作为“主节点”,当其挂掉后,再在剩下的从节点中选举一个节点成为新的“主节点”,在副本集中总有一个主节点(primary)和一个或多个备份节点(secondary)。

除了 primary 和 secondary 之外,副本集中的节点还可以是以下角色:

官方推荐的副本集最小配置需要有三个节点:一个主节点接收和处理所有的写操作,两个备份节点通过复制主节点的操作来对主节点的数据进行同步备份。

img

1.10.2.1 配置副本集

1、环境准备 副本集各节点 IP 如下:

172.16.250.234

172.16.250.239

172.16.250.240

首先,先对三个 MongoDB 节点进行安装。

然后,依次修改各个节点的 mongodb.conf 配置文件,增加副本集相关配置,内容如下:

java
dbpath=/usr/local/mongodb-4.0.2/data
logpath=/usr/local/mongodb-4.0.2/log/mongodb.log
fork=true
logappend=true
bind_ip= # 此处填写服务器的IP
port=27017

# 设置副本集名称,在各个配置文件中,其值必须相同
replSet=rs0

配置完成之后,分别在三个节点上执行如下命令通过加载文件配置来启动 MongoDB 服务:

java
mongod -config /usr/local/mongodb-4.0.2/mongodb.conf
# 或者
mongod -f /usr/local/mongodb-4.0.2/mongodb.conf

至此,3 个 MongoDB 实例都已经以副本集方式启动,但它们彼此之间现在还不会进行通信,仍需要进行一些配置。

2、副本集初始化 通过 Shell 连接到任意一个 MongoDB 实例,执行 rs.initiate()方法对副本集进行初始化。

java
[root@hadoop34 mongodb-4.0.2]# mongo 172.16.250.234:27017
> conf=
    {
    "_id" : "rs0",
    "members" : [
        { "_id" : 0, "host" : "172.16.250.234:27017" },
        { "_id" : 1, "host" : "172.16.250.239:27017" },
        { "_id" : 2, "host" : "172.16.250.240:27017" }
        ]
    }
> rs.initiate(conf)
{
    "ok" : 1,
    "operationTime" : Timestamp(1542247326, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1542247326, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}
rs0:SECONDARY>

如果在执行 rs.initiate()方法时不传入任何参数,MongoDB 将以默认的配置文档对副本集进行初始化,后续可以再通过 rs.add()方法来向副本集中添加成员。

3、副本集更新

java
# 向副本集中添加成员
rs.add("172.16.250.240:27017")

# 从副本集中删除成员
rs.remove("172.16.250.240:27017")

# 向副本集中添加仲裁
rs.addArb("172.16.250.240:27017")

# 向副本集中添加备份节点
rs.add({"_id":3,"host":"172.16.250.240:27017","priority":0,"hidden":true})
java
# 更改副本集配置
rs0:PRIMARY> var conf=rs.conf()
rs0:PRIMARY> conf.members[1].priority = 5

# PRIMARY节点上执行如下命令
rs0:PRIMARY> rs.reconfig(conf)
{
    "ok" : 1,
    "operationTime" : Timestamp(1542248518, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1542248518, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

# SECONDARY节点上执行如下命令,需增加 force 参数
rs0:SECONDARY> rs.reconfig(conf,{force:true})
{
    "ok" : 1,
    "operationTime" : Timestamp(1542248726, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1542248726, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

例如,强制让一个节点成为 Primary,可以将该节点的优先级设置成最高。

java
cfg = rs.conf()
cfg.members[0].priority = 5
cfg.members[1].priority = 1
cfg.members[2].priority = 1
rs.reconfig(cfg)

4、副本集监控

java
# 查看副本集的配置信息
rs0:PRIMARY> rs.conf()
{
    "_id" : "rs0",
    "version" : 104658,
    "protocolVersion" : NumberLong(1),
    "writeConcernMajorityJournalDefault" : true,
    "members" : [
    {
        "_id" : 0,
        "host" : "172.16.250.234:27017",
        "arbiterOnly" : false,
        "buildIndexes" : true,
        "hidden" : false,
        "priority" : 1,
        "tags" : {},
        "slaveDelay" : NumberLong(0),
        "votes" : 1
    },
    {
        "_id" : 1,
        "host" : "172.16.250.239:27017",
        "arbiterOnly" : false,
        "buildIndexes" : true,
        "hidden" : false,
        "priority" : 5,
        "tags" : {},
        "slaveDelay" : NumberLong(0),
        "votes" : 1
    },
    {
        "_id" : 2,
        "host" : "172.16.250.240:27017",
        "arbiterOnly" : false,
        "buildIndexes" : true,
        "hidden" : false,
        "priority" : 1,
        "tags" : {},
        "slaveDelay" : NumberLong(0),
        "votes" : 1
    }],
    "settings" : {
        "chainingAllowed" : true,
        "heartbeatIntervalMillis" : 2000,
        "heartbeatTimeoutSecs" : 10,
        "electionTimeoutMillis" : 10000,
        "catchUpTimeoutMillis" : -1,
        "catchUpTakeoverDelayMillis" : 30000,
        "getLastErrorModes" : {},
        "getLastErrorDefaults" : {
            "w" : 1,
            "wtimeout" : 0
        },
        "replicaSetId" : ObjectId("5becd39e360189766762e057")
    }
}
java
# 查看副本集运行状态
rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2018-11-15T02:46:15.138Z"),
    "myState" : 1,
    "term" : NumberLong(2),
    "syncingTo" : "",
    "syncSourceHost" : "",
    "syncSourceId" : -1,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1542249966, 1),
            "t" : NumberLong(2)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1542249966, 1),
            "t" : NumberLong(2)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1542249966, 1),
            "t" : NumberLong(2)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1542249966, 1),
            "t" : NumberLong(2)
        }
    },
    "lastStableCheckpointTimestamp" : Timestamp(1542249916, 1),
    "members" : [
    {
        "_id" : 0,
        "name" : "172.16.250.234:27017",
        "health" : 1,
        "state" : 2,
        "stateStr" : "SECONDARY",
        "uptime" : 2651,
        "optime" : {
            "ts" : Timestamp(1542249966, 1),
            "t" : NumberLong(2)
        },
        "optimeDurable" : {
            "ts" : Timestamp(1542249966, 1),
            "t" : NumberLong(2)
        },
        "optimeDate" : ISODate("2018-11-15T02:46:06Z"),
        "optimeDurableDate" : ISODate("2018-11-15T02:46:06Z"),
        "lastHeartbeat" : ISODate("2018-11-15T02:46:13.520Z"),
        "lastHeartbeatRecv" : ISODate("2018-11-15T02:46:13.519Z"),
        "pingMs" : NumberLong(0),
        "lastHeartbeatMessage" : "",
        "syncingTo" : "172.16.250.239:27017",
        "syncSourceHost" : "172.16.250.239:27017",
        "syncSourceId" : 1,
        "infoMessage" : "",
        "configVersion" : 104658
        },
    {
        "_id" : 1,
        "name" : "172.16.250.239:27017",
        "health" : 1,
        "state" : 1,
        "stateStr" : "PRIMARY",
        "uptime" : 2799,
        "optime" : {
            "ts" : Timestamp(1542249966, 1),
            "t" : NumberLong(2)
        },
        "optimeDate" : ISODate("2018-11-15T02:46:06Z"),
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "infoMessage" : "",
        "electionTime" : Timestamp(1542248524, 1),
        "electionDate" : ISODate("2018-11-15T02:22:04Z"),
        "configVersion" : 104658,
        "self" : true,
        "lastHeartbeatMessage" : ""
    },
    {
        "_id" : 2,
        "name" : "172.16.250.240:27017",
        "health" : 1,
        "state" : 2,
        "stateStr" : "SECONDARY",
        "uptime" : 1855,
        "optime" : {
            "ts" : Timestamp(1542249966, 1),
            "t" : NumberLong(2)
        },
        "optimeDurable" : {
            "ts" : Timestamp(1542249966, 1),
            "t" : NumberLong(2)
        },
        "optimeDate" : ISODate("2018-11-15T02:46:06Z"),
        "optimeDurableDate" : ISODate("2018-11-15T02:46:06Z"),
        "lastHeartbeat" : ISODate("2018-11-15T02:46:13.520Z"),
        "lastHeartbeatRecv" : ISODate("2018-11-15T02:46:13.520Z"),
        "pingMs" : NumberLong(0),
        "lastHeartbeatMessage" : "",
        "syncingTo" : "172.16.250.239:27017",
        "syncSourceHost" : "172.16.250.239:27017",
        "syncSourceId" : 1,
        "infoMessage" : "",
        "configVersion" : 104658
    }],
    "ok" : 1,
    "operationTime" : Timestamp(1542249966, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1542249966, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}
java
# 查看备份节点的复制信息
rs0:PRIMARY> db.printSlaveReplicationInfo()
source: 172.16.250.234:27017
    syncedTo: Thu Nov 15 2018 11:08:36 GMT+0800 (CST)
    0 secs (0 hrs) behind the primary
source: 172.16.250.240:27017
    syncedTo: Thu Jan 01 1970 08:00:00 GMT+0800 (CST)
    1542251316 secs (428403.14 hrs) behind the primary

1.10.2.2 副本集测试

在 Primary 上插入一万条客户数据:

java
rs0:PRIMARY> for(var i=0;i<10000;i++){db.customer.insert({"name":"user"+i})}
WriteResult({ "nInserted" : 1 })
rs0:PRIMARY> db.customer.count()
10000

在 Secondary 上查看客户数据是否已经同步:

java
rs0:SECONDARY> rs.slaveOk()
rs0:SECONDARY> db.customer.count()
10000

故障转移测试 执行如下命令关闭 Primary 节点,查看其他 2 个节点的情况:

java
mongod --shutdown --dbpath /usr/local/mongodb-4.0.2/data
java
# 查看Primary节点关闭之前的状态
rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2018-11-15T03:36:31.393Z"),
    "myState" : 1,
    "term" : NumberLong(4),
    "syncingTo" : "",
    "syncSourceHost" : "",
    "syncSourceId" : -1,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1542252988, 1),
            "t" : NumberLong(4)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1542252988, 1),
            "t" : NumberLong(4)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1542252988, 1),
            "t" : NumberLong(4)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1542252988, 1),
            "t" : NumberLong(4)
        }
    },
    "lastStableCheckpointTimestamp" : Timestamp(1542252978, 1),
    "members" : [
    {
        "_id" : 0,
        "name" : "172.16.250.234:27017",
        "health" : 1,
        "state" : 2,
        "stateStr" : "SECONDARY",
        "uptime" : 425,
        "optime" : {
            "ts" : Timestamp(1542252988, 1),
            "t" : NumberLong(4)
        },
        "optimeDurable" : {
            "ts" : Timestamp(1542252988, 1),
            "t" : NumberLong(4)
        },
        "optimeDate" : ISODate("2018-11-15T03:36:28Z"),
        "optimeDurableDate" : ISODate("2018-11-15T03:36:28Z"),
        "lastHeartbeat" : ISODate("2018-11-15T03:36:31.243Z"),
        "lastHeartbeatRecv" : ISODate("2018-11-15T03:36:30.233Z"),
        "pingMs" : NumberLong(0),
        "lastHeartbeatMessage" : "",
        "syncingTo" : "172.16.250.239:27017",
        "syncSourceHost" : "172.16.250.239:27017",
        "syncSourceId" : 1,
        "infoMessage" : "",
        "configVersion" : 104666
    },
    {
        "_id" : 1,
        "name" : "172.16.250.239:27017",
        "health" : 1,
        "state" : 1,
        "stateStr" : "PRIMARY",
        "uptime" : 428,
        "optime" : {
            "ts" : Timestamp(1542252988, 1),
            "t" : NumberLong(4)
        },
        "optimeDate" : ISODate("2018-11-15T03:36:28Z"),
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "infoMessage" : "",
        "electionTime" : Timestamp(1542252577, 2),
        "electionDate" : ISODate("2018-11-15T03:29:37Z"),
        "configVersion" : 104666,
        "self" : true,
        "lastHeartbeatMessage" : ""
    },
    {
        "_id" : 2,
        "name" : "172.16.250.240:27017",
        "health" : 1,
        "state" : 2,
        "stateStr" : "SECONDARY",
        "uptime" : 78,
        "optime" : {
            "ts" : Timestamp(1542252988, 1),
            "t" : NumberLong(4)
        },
        "optimeDurable" : {
            "ts" : Timestamp(1542252988, 1),
            "t" : NumberLong(4)
        },
        "optimeDate" : ISODate("2018-11-15T03:36:28Z"),
        "optimeDurableDate" : ISODate("2018-11-15T03:36:28Z"),
        "lastHeartbeat" : ISODate("2018-11-15T03:36:31.376Z"),
        "lastHeartbeatRecv" : ISODate("2018-11-15T03:36:29.597Z"),
        "pingMs" : NumberLong(0),
        "lastHeartbeatMessage" : "",
        "syncingTo" : "172.16.250.239:27017",
        "syncSourceHost" : "172.16.250.239:27017",
        "syncSourceId" : 1,
        "infoMessage" : "",
        "configVersion" : 104666
    }],
    "ok" : 1,
    "operationTime" : Timestamp(1542252988, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1542252988, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}
java
# 在任意其他节点上查看Primary节点关闭之后的状态
> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2018-11-15T03:41:31.213Z"),
    "myState" : 1,
    "term" : NumberLong(5),
    "syncingTo" : "",
    "syncSourceHost" : "",
    "syncSourceId" : -1,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1542253290, 1),
            "t" : NumberLong(5)
        },
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1542253290, 1),
            "t" : NumberLong(5)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1542253290, 1),
            "t" : NumberLong(5)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1542253290, 1),
            "t" : NumberLong(5)
        }
    },
    "lastStableCheckpointTimestamp" : Timestamp(1542253268, 1),
    "members" : [
    {
        "_id" : 0,
        "name" : "172.16.250.234:27017",
        "health" : 1,
        "state" : 1,
        "stateStr" : "PRIMARY",
        "uptime" : 6115,
        "optime" : {
            "ts" : Timestamp(1542253290, 1),
            "t" : NumberLong(5)
        },
        "optimeDate" : ISODate("2018-11-15T03:41:30Z"),
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "infoMessage" : "could not find member to sync from",
        "electionTime" : Timestamp(1542253288, 1),
        "electionDate" : ISODate("2018-11-15T03:41:28Z"),
        "configVersion" : 104666,
        "self" : true,
        "lastHeartbeatMessage" : ""
    },
    {
        "_id" : 1,
        "name" : "172.16.250.239:27017",
        "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-11-15T03:41:30.593Z"),
        "lastHeartbeatRecv" : ISODate("2018-11-15T03:41:18.148Z"),
        "pingMs" : NumberLong(0),
        "lastHeartbeatMessage" : "Error connecting to 172.16.250.239:27017 :: caused by ::     Connection refused",
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "infoMessage" : "",
        "configVersion" : -1
    },
    {
        "_id" : 2,
        "name" : "172.16.250.240:27017",
        "health" : 1,
        "state" : 2,
        "stateStr" : "SECONDARY",
        "uptime" : 372,
        "optime" : {
            "ts" : Timestamp(1542253268, 1),
            "t" : NumberLong(4)
        },
        "optimeDurable" : {
            "ts" : Timestamp(1542253268, 1),
            "t" : NumberLong(4)
        },
        "optimeDate" : ISODate("2018-11-15T03:41:08Z"),
        "optimeDurableDate" : ISODate("2018-11-15T03:41:08Z"),
        "lastHeartbeat" : ISODate("2018-11-15T03:41:30.591Z"),
        "lastHeartbeatRecv" : ISODate("2018-11-15T03:41:31.106Z"),
        "pingMs" : NumberLong(0),
        "lastHeartbeatMessage" : "",
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "infoMessage" : "",
        "configVersion" : 104666
    }],
    "ok" : 1,
    "operationTime" : Timestamp(1542253290, 1),
    "$clusterTime" : {
        "clusterTime" : Timestamp(1542253290, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
}

再次启动 172.16.250.239:27017 节点,由于其选举优先级最高,自动被选举为 Primary。

java
# 待172.16.250.239:27017 节点启动后再次查看副本集状态
> rs.status()
{
	"set" : "rs0",
	"date" : ISODate("2018-11-15T03:44:01.745Z"),
	"myState" : 2,
	"term" : NumberLong(6),
	"syncingTo" : "172.16.250.239:27017",
	"syncSourceHost" : "172.16.250.239:27017",
	"syncSourceId" : 1,
	"heartbeatIntervalMillis" : NumberLong(2000),
	"optimes" : {
		"lastCommittedOpTime" : {
			"ts" : Timestamp(1542253435, 1),
			"t" : NumberLong(6)
		},
		"readConcernMajorityOpTime" : {
			"ts" : Timestamp(1542253435, 1),
			"t" : NumberLong(6)
		},
		"appliedOpTime" : {
			"ts" : Timestamp(1542253435, 1),
			"t" : NumberLong(6)
		},
		"durableOpTime" : {
			"ts" : Timestamp(1542253435, 1),
			"t" : NumberLong(6)
		}
	},
	"lastStableCheckpointTimestamp" : Timestamp(1542253400, 1),
	"members" : [
		{
			"_id" : 0,
			"name" : "172.16.250.234:27017",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 6265,
			"optime" : {
				"ts" : Timestamp(1542253435, 1),
				"t" : NumberLong(6)
			},
			"optimeDate" : ISODate("2018-11-15T03:43:55Z"),
			"syncingTo" : "172.16.250.239:27017",
			"syncSourceHost" : "172.16.250.239:27017",
			"syncSourceId" : 1,
			"infoMessage" : "",
			"configVersion" : 104666,
			"self" : true,
			"lastHeartbeatMessage" : ""
		},
		{
			"_id" : 1,
			"name" : "172.16.250.239:27017",
			"health" : 1,
			"state" : 1,
			"stateStr" : "PRIMARY",
			"uptime" : 23,
			"optime" : {
				"ts" : Timestamp(1542253435, 1),
				"t" : NumberLong(6)
			},
			"optimeDurable" : {
				"ts" : Timestamp(1542253435, 1),
				"t" : NumberLong(6)
			},
			"optimeDate" : ISODate("2018-11-15T03:43:55Z"),
			"optimeDurableDate" : ISODate("2018-11-15T03:43:55Z"),
			"lastHeartbeat" : ISODate("2018-11-15T03:44:01.228Z"),
			"lastHeartbeatRecv" : ISODate("2018-11-15T03:44:00.835Z"),
			"pingMs" : NumberLong(0),
			"lastHeartbeatMessage" : "",
			"syncingTo" : "",
			"syncSourceHost" : "",
			"syncSourceId" : -1,
			"infoMessage" : "",
			"electionTime" : Timestamp(1542253424, 1),
			"electionDate" : ISODate("2018-11-15T03:43:44Z"),
			"configVersion" : 104666
		},
		{
			"_id" : 2,
			"name" : "172.16.250.240:27017",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 522,
			"optime" : {
				"ts" : Timestamp(1542253435, 1),
				"t" : NumberLong(6)
			},
			"optimeDurable" : {
				"ts" : Timestamp(1542253435, 1),
				"t" : NumberLong(6)
			},
			"optimeDate" : ISODate("2018-11-15T03:43:55Z"),
			"optimeDurableDate" : ISODate("2018-11-15T03:43:55Z"),
			"lastHeartbeat" : ISODate("2018-11-15T03:44:01.166Z"),
			"lastHeartbeatRecv" : ISODate("2018-11-15T03:44:01.414Z"),
			"pingMs" : NumberLong(0),
			"lastHeartbeatMessage" : "",
			"syncingTo" : "172.16.250.239:27017",
			"syncSourceHost" : "172.16.250.239:27017",
			"syncSourceId" : 1,
			"infoMessage" : "",
			"configVersion" : 104666
		}
	],
	"ok" : 1,
	"operationTime" : Timestamp(1542253435, 1),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1542253435, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}

1.10.2.3 开启安全认证

创建用户 登录 PRIMARY 节点创建用户,在此我们对 test 库开启安全认证。

java
rs0:PRIMARY> show dbs
admin   0.000GB
config  0.000GB
local   0.002GB
test    0.000GB
rs0:PRIMARY> use admin
switched to db admin
rs0:PRIMARY> db.createUser({user:"root",pwd:"123456",roles:[{role:"userAdminAnyDatabase",db:"admin"}]})
Successfully added user: {
	"user" : "root",
	"roles" : [
		{
			"role" : "userAdminAnyDatabase",
			"db" : "admin"
		}
	]
}
rs0:PRIMARY> use test
switched to db test
rs0:PRIMARY> db.createUser({user:"admin",pwd:"admin",roles:[{role:"readWrite",db:"test"}]})
Successfully added user: {
	"user" : "admin",
	"roles" : [
		{
			"role" : "readWrite",
			"db" : "test"
		}
	]
}

创建 keyFile 文件 先停掉所有 SECONDARY 节点的 MongoDB 服务,然后再停掉 PRIMARY 节点的 MongoDB 服务,并在 PRIMARY 节点所在服务器上创建 keyFile 文件。

java
[root@hadoop39 mongodb-4.0.2]# openssl rand -base64 666 > /usr/local/mongodb-4.0.2/keyfile
[root@hadoop39 mongodb-4.0.2]# chmod 600 /usr/local/mongodb-4.0.2/keyfile

将生成的 keyFile 文件拷贝到其他节点服务器上,并修改文件的操作权限为 600。

java
chmod 600 /usr/local/mongodb-4.0.2/keyfile

更新启动配置文件 修改 PRIMARY 节点的 mongodb.conf 文件,增加如下内容:

java
# Add below Config
auth=true
oplogSize=100
keyFile=/usr/local/mongodb-4.0.2/keyfile

修改 SECONDARY 节点的 mongodb.conf 文件,增加如下内容:

java
# Add below Config
oplogSize=100
keyFile=/usr/local/mongodb-4.0.2/keyfile

启动副本集 先以--auth 方式启动 PRIMARY 节点:

java
[root@hadoop39 mongodb-4.0.2]# mongod -f /usr/local/mongodb-4.0.2/mongodb.conf

再启动 SECONDARY 节点:

java
mongod -f /usr/local/mongodb-4.0.2/mongodb.conf

登录测试

java
[root@hadoop39 mongodb-4.0.2]# mongo -uadmin -padmin 172.16.250.239:27017
MongoDB shell version v4.0.2
connecting to: mongodb://172.16.250.239:27017/test
MongoDB server version: 4.0.2
rs0:PRIMARY> show dbs;
test 0.000GB

admin 用户只能看到 test 库。