MongoDB
概述MongoDB
是一个基于分布式文件存储的数据库。由C++
语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB
是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
它支持的数据结构非常松散,是类似json
的bson
格式,因此可以存储比较复杂的数据类型。Mongo
最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
MongoDB
服务端可运行在Linux
、Windows
平台,支持32
位和64
位应用,默认端口为27017
。
推荐运行在64
位平台,因为MongoD
B在32
位模式运行时支持的最大文件尺寸为2
GB。
MongoDB
主要特点MongoDB
中的记录是一个文档,它是由字段和值对组成的数据结构。
多个键及其关联的值有序地放在一起就构成了文档。
MongoDB
文档类似于JSON
对象。字段的值可以包括其他文档,数组和文档数组。
{“greeting”:“hello,world”}
这个文档只有一个键“greeting”
,对应的值为“hello,world”
。多数情况下,文档比这个更复杂,它包含多个键/值对。
例如:{“greeting”:“hello,world”,“foo”: 3}
文档中的键/值对是有序的,下面的文档与上面的文档是完全不同的两个文档。{“foo”: 3 ,“greeting”:“hello,world”}
文档中的值不仅可以是双引号中的字符串,也可以是其他的数据类型,例如,整型、布尔型等,也可以是另外一个文档,即文档可以嵌套。文档中的键类型只能是字符串。
使用文档的优点是:
集合就是一组文档,类似于关系数据库中的表。
集合是无模式的,集合中的文档可以是各式各样的。例如,{“hello,word”:“Mike”}
和{“foo”: 3}
,它们的键不同,值的类型也不同,但是它们可以存放在同一个集合中,也就是不同模式的文档都可以放在同一个集合中。
既然集合中可以存放任何类型的文档,那么为什么还需要使用多个集合?
这是因为所有文档都放在同一个集合中,无论对于开发者还是管理员,都很难对集合进行管理,而且这种情形下,对集合的查询等操作效率都不高。所以在实际使用中,往往将文档分类存放在不同的集合中。
例如,对于网站的日志记录,可以根据日志的级别进行存储,Info
级别日志存放在Info
集合中,Debug
级别日志存放在Debug
集合中,这样既方便了管理,也提供了查询性能。
但是需要注意的是,这种对文档进行划分来分别存储并不是MongoDB
的强制要求,用户可以灵活选择。
可以使用“.”
按照命名空间将集合划分为子集合。
例如,对于一个博客系统,可能包括blog.user
和blog.article
两个子集合,这样划分只是让组织结构更好一些,blog
集合和blog.user
、blog.article
没有任何关系。虽然子集合没有任何特殊的地方,但是使用子集合组织数据结构清晰,这也是MongoDB
推荐的方法。
MongoDB
中多个文档组成集合,多个集合组成数据库。
一个MongoDB
实例可以承载多个数据库。它们之间可以看作相互独立,每个数据库都有独立的权限控制。在磁盘上,不同的数据库存放在不同的文件中。
MongoDB
中存在以下系统数据库。
Admin
数据库:一个权限数据库,如果创建用户的时候将该用户添加到admin
数据库中,那么该用户就自动继承了所有数据库的权限。Local
数据库:这个数据库永远不会被复制,可以用来存储本地单台服务器的任意集合。Config
数据库:当MongoDB
使用分片模式时,config
数据库在内部使用,用于保存分片的信息。一个MongoDB
实例可以包含一组数据库,一个DataBase
可以包含一组Collection
(集合),一个集合可以包含一组Document
(文档)。
一个Document
包含一组field
(字段),每一个字段都是一个key/value pair
key
: 必须为字符串类型value
:可以包含如下类型
string,int,float,timestamp,binary
等类型document
Windows
安装MongoDB
MongoDB
MongoDB
提供了可用于32
位系统和64
位系统的预编译二进制包(新版本没有了32
位系统的安装文件),你可以进入MongoDB
官网下载安装,MongoDB
的预编译二进制包的下载地址为:https://www.mongodb.com/download-center/community
,打开之后会看到如下图,直接点击Download
下载即可,也可以在Version
中选择你想要的版本:
MongoDB
双击打开文件进行安装,在安装过程中,可以通过点击 “Custom
(自定义)” 按钮来设置你的安装目录。
这里我选择安装在E:\MongoDB
这个目录下(安装目录会影响我们后面的配置)
这里选择直接next
这里安装 "Install MongoDB Compass"
不勾选,否则可能要很长时间都一直在执行安装,MongoDB Compass
是一个图形界面管理工具,这里不安装也是没有问题的,可以自己去下载一个图形界面管理工具,比如Robo3T
。
之后稍微等待一会就安装好了。
MongoDB
MongoDB
的安装过程是很简单的,但是配置就比较麻烦了,可能会遇到各种各样的问题,需要你有足够的耐心和仔细。
首先要在MongoDB
的data
文件夹里新建一个db
文件夹和一个log
文件夹:
然后在log文件夹下新建一个mongo.log:
然后将E:\MongoDB\bin
添加到环境变量path
中,此时打开cmd
窗口运行一下mongo
命令,出现如下情况:
这是为什么呢?这是因为我们还没有启动MongoDB
服务,自然也就连接不上服务了。那要怎么启动呢?在cmd
窗口中运行如下命令:
mongod --dbpath E:\MongoDB\data\db
需要注意的是:如果你没有提前创建db
文件夹,是无法启动成功的。运行成功之后,我们打开浏览器,输入127.0.0.1:27017
,看到如下图,就说明MongoDB
服务已经成功启动了。
但是如果每次都要这么启动服务的话也太麻烦了吧,这里你可以选择设置成开机自启动,也可以选择用命令net start mongodb
来手动启动,这里我选择使用后者,具体方法如下。
还是打开cmd
窗口,不过这次是以管理员身份运行,然后输入如下命令:
mongod --dbpath "E:\MongoDB\data\db" --logpath "E:\MongoDB\data\log\mongo.log" -install -serviceName "MongoDB"
如果没有报错的话就说明成功添加到服务里了,可以使用win+R
然后输入services.msc
命令进行查看:
默认是自动运行的,这里我选择把它改成手动的。然后在cmd
窗口中运行net start mongodb
:
怎么解决呢?两个步骤:
1)运行sc delete mongodb
删除服务;
2)再运行一次配置服务的命令:
mongod --dbpath "E:\MongoDB\data\db" --logpath "E:\MongoDB\data\log\mongo.log" -install -serviceName "MongoDB"
然后再运行net start mongodb
,服务启动成功:
可能遇到的问题:
1、mongod
不是内部或外部命令
出现这种问题说明你没有把bin
目录添加到环境变量之中,重新添加一下即可解决。
2、服务名无效
首先是看你输入的服务名称是否有误,然后再查看本地服务中有没有MongoDB
服务,如果没有服务,则运行命令添加服务即可。
3、发生服务特定错误:100
删除db
文件夹下的mongod.lock
和storage.bson
两个文件,若删除完之后仍然出现这种问题,用sc delete mongodb
删除服务,再配置一下服务就能解决了。
官网下载 RoBo 3T(Robomongo is now Robo 3T)
下载地址:https://robomongo.org/download
双击安装包安装,修改安装路径,不停下一步,点击安装。
打开后,有一个填信息的页面,name
、email
,暂时不用管,直接finish
。
启动MongoDB
服务。
点击弹出框中的create
,创建新连接,可以修改连接名name
,连接IP
(下图IP
为本地IP
),端口(默认)
连接成功后,右击localhost
,选择create Database
,创建数据库
创建数据库firstTest
,然后右击firstTest
,选择open Shell
,开始进行shell
命令来创建数据库中的集合和文档。
Linux
安装MongoDB
MongoDB
官方下载地址:https://www.mongodb.com/download-center/community
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.1.tgz
1、解压
tar -zxvf mongodb-linux-x86_64-rhel70-4.2.1.tgz
2、创建目录/usr/local/mongo
,并将解压完的mongodb
目录移动到/usr/local/mongo
下
mkdir -p /usr/local/mongo
mv mongodb-linux-x86_64-rhel70-4.2.1/* /usr/local/mongo/
3、切到/usr/local/mongo
目录下,创建目录
mkdir -p data/db #数据库目录
mkdir -p logs #日志目录
mkdir -p conf #配置文件目录
mkdir -p pids #进程描述文件目录
创建好的目录如下:
4、在conf
目录,增加配置文件mongo.conf
vi /usr/local/mongo/conf/mongo.conf
#数据保存路径
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
服务端
/usr/local/mongo/bin/mongod -f /usr/local/mongo/conf/mongo.conf
/usr/local/mongo/bin/mongo --host 127.0.0.1 --port 27017
MongoDB
基本操作及增删改查登陆数据库
mongo
查看数据库
show databases;
选择数据库
use
数据库名
如果切换到一个没有的数据库,例如use admin2
,那么会隐式创建这个数据库。(后期当该数据库有数据时,系统自动创建)
use admin2
show collections
db.createCollection('集合名')
`db.集合名.drop()`
删除数据库
通过use
语法选择数据
通过db.dropDataBase()
删除数据库
db.集合名.insert(JSON数据)
如果集合存在,那么直接插入数据。如果集合不存在,那么会隐式创建。
示例:在test2
数据库的c1
集合中插入数据(姓名叫webopenfather
年龄18
岁)
use test2 db.c1.insert({uname:"webopenfather",age:18})
一次性插入多条数据
传递数据,数组中写一个个JSON
数据即可
db.c1.insert([ {uname:"z3", age:3}, {uname:"z4", age:4}, {uname:"w5", age:5} ])
快速插入10
条数据
由于mongodb
底层使用JS
引擎实现的,所以支持部分js
语法。因此:可以写for
循环
for (var i=1; i<=10; i++) { db.c2.insert({uanme: "a"+i, age: i}) }
查询文档
db.集合名.find(条件[,查询的列])
条件 | 写法 |
---|---|
查询所有的数据 | {}或者不写 |
查询age=6的数据 | {age:6} |
既要age=6又要性别=男 | {age:6,sex:‘男’} |
查询的列(可选参数) | 写法 |
---|---|
查询全部列(字段) | 不写 |
只显示age列(字段) | {age:1} |
除了age列(字段)都显示 | {age:0} |
其他语法
db.集合名.find({
键:{运算符:值}
})
运算符 | 作用 |
---|---|
$gt | 大于 |
$gte | 大于等于 |
$lt | 小于 |
$lte | 小于等于 |
$ne | 不等于 |
$in | in |
$nin | not in |
实例练习
查询所有数据
db.c1.find()
1、查询age
大于5
的数据
db.c1.find({age:{$gt:5}})
db.c2.find({age:{$in:[5,8,10]}})
db.集合名.update(条件,新数据[是否新增,是否修改多条,])
新数据此数据需要使用修改器,如果不使用,那么会将新数据替换原来的数据。1db.集合名.update(条件,{修改器:{键:值}}[是否新增,是否修改多条,])
修改器作用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)
实例练习
准备工作
use test2;
for(var i = 1; i<= 10; i++){
db.c3.insert( {"uname":"zs"+i,"age":i} );
}
1、将{uname:"zs1"}
改为{uname:"zs2"}
db.c3.update({uname:"zs1"},{$set:{uname:"zs2"}})
db.c3.update({uname:"zs10"},{$inc:{age:2}})
综合练习插入数据:
db.c4.insert( {uname:"神龙教主",age:888,who:"男",other:"非国人"})
;
db.集合名.remove(条件[,是否删除一条])
true
:是(删除的数据为第一条)false
:否
db.c3.remove({uname:"zs3"})
高级开发攻城狮统称:所有数据库都需要增删改查CURD
标识
MongoDB
删除语法:remove
增Create
db.集合名.insert(JSON数据)
删Delete
db.集合名.remove(条件 [,是否删除一条true是false否默认])
也就是默认删除多条
改Update
db.集合名.update(条件, 新数据 [,是否新增,是否修改多条])
升级语法db.集合名.update(条件,{修改器:{键:值}})
查Read
db.集合名.find(条件 [,查询的列])
MongoDB
存储数据类型MongoDB
中每条记录称作一个文档,这个文档和我们平时用的JSON
有点像,但也不完全一样。JSON
是一种轻量级的数据交换格式。简洁和清晰的层次结构使得JSON
成为理想的数据交换语言,JSON
易于阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率,但是JSON
也有它的局限性,比如它只有null
、布尔、数字、字符串、数组和对象这几种数据类型,没有日期类型,只有一种数字类型,无法区分浮点数和整数,也没法表示正则表达式或者函数。由于这些局限性,BSON
闪亮登场啦,BSON
是一种类JSON
的二进制形式的存储格式,简称Binary JSON
,它和JSON
一样,支持内嵌的文档对象和数组对象,但是BSON
有JSON
没有的一些数据类型,如Date
和BinData
类型,MongoDB
使用BSON
做为文档数据存储和网络传输格式。
shell
默认使用64
位浮点型数值,如下:
db.sang_collec.insert({x:3.1415926})
db.sang_collec.insert({x:3})
对于整型值,我们可以使用NumberInt
或者NumberLong
表示,如下:
db.sang_collec.insert({x:NumberInt(10)})
db.sang_collec.insert({x:NumberLong(12)})
字符串也可以直接存储,如下:
db.sang_collec.insert({x:"hello MongoDB!"})
正则表达式主要用在查询里边,查询时我们可以使用正则表达式,语法和JavaScript
中正则表达式的语法相同,比如查询所有key
为x
,value
以hello
开始的文档且不区分大小写:
db.sang_collec.find({x:/^(hello)(.[a-zA-Z0-9])+/i})
数组一样也是被支持的,如下:
db.sang_collec.insert({x:[1,2,3,4,new Date()]})
数组中的数据类型可以是多种多样的。
MongoDB
支持Date
类型的数据,可以直接new
一个Date
对象,如下:
db.sang_collec.insert({x:new Date()})
一个文档也可以作为另一个文档的value
,这个其实很好理解,如下:
db.sang_collect.insert({name:"三国演义",author:{name:"罗贯中",age:99}});
书有一个属性是作者,作者又有name
,年龄等属性。
MongoDB
中的索引默认情况下,集合中的_id
字段就是索引,我们可以通过getIndexes()
方法来查看一个集合中的索引:
db.sang_collect.getIndexes()
结果如下:
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "sang.sang_collect"
}
]
我们看到这里只有一个索引,就是_id
。
现在我的集合中有10000
个文档,我想要查询x
为1
的文档,我的查询操作如下:
db.sang_collect.find({x:1})
这种查询默认情况下会做全表扫描,我们可以用上篇文章介绍的explain()
来查看一下查询计划,如下:
db.sang_collect.find({x:1}).explain("executionStats")
结果如下:
{
"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
,如下:
db.sang_collect.find({x:1}).limit(1)
此时再看查询计划发现只扫描了两个文档就有结果了,但是如果我要查询x
为9999
的记录,那还是得把全表扫描一遍,此时,我们就可以给该字段建立索引,索引建立方式如下:
db.sang_collect.ensureIndex({x:1})
1表示升序,-1表示降序。当我们给x字段建立索引之后,再根据x字段去查询,速度就非常快了,我们看下面这个查询操作的执行计划:
db.sang_collect.find({x:9999}).explain("executionStats")
这个查询计划过长我就不贴出来了,我们可以重点关注查询要耗费的时间大幅度下降。
此时调用getIndexes()
方法可以看到我们刚刚创建的索引,如下:
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "sang.sang_collect"
},
{
"v" : 2,
"key" : {
"x" : 1.0
},
"name" : "x_1",
"ns" : "sang.sang_collect"
}
]
我们看到每个索引都有一个名字,默认的索引名字为字段名_排序值,当然我们也可以在创建索引时自定义索引名字,如下:
db.sang_collect.ensureIndex({x:1},{name:"myfirstindex"})
此时创建好的索引如下:
{
"v" : 2,
"key" : {
"x" : 1.0
},
"name" : "myfirstindex",
"ns" : "sang.sang_collect"
}
当然索引在创建的过程中还有许多其他可选参数,如下:
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
表示索引的权重
此时创建好的索引如下:
{
"v" : 1,
"unique" : true,
"key" : {
"x" : 1.0
},
"name" : "myfirstindex",
"ns" : "sang.sang_collect",
"background" : true,
"sparse" : true,
"weights" : 99999.0
}
getIndexes()
可以用来查看索引,我们还可以通过totalIndexSize()
来查看索引的大小,如下:
db.sang_collect.totalIndexSize()
我们可以按名称删除索引,如下:
db.sang_collect.dropIndex("xIndex")
表示删除一个名为xIndex
的索引,当然我们也可以删除所有索引,如下:
db.sang_collect.dropIndexes()
索引是个好东西,可以有效的提高查询速度,但是索引会降低插入、更新和删除的速度,因为这些操作不仅要更新文档,还要更新索引,MongoDB
限制每个集合上最多有64
个索引,我们在创建索引时要仔细斟酌索引的字段。
Java
操作MongoDB
方式一采用的原生Java
操作MongoDB
首先我们需要驱动,MongoDB
的Java
驱动我们可以直接在Maven
中央仓库去下载,也可以创建Maven
工程添加如下依赖:
org.mongodb
mongodb-driver
3.5.0
建议通过Maven
来添加依赖,如果自己下载jar,需要下载如下三个jar:
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
所有准备工作完成之后,我们首先需要一个MongoClient,如下:
MongoClient client = new MongoClient("192.168.248.136", 27017);
然后通过如下方式获取一个数据库,如果要获取的数据库本身就存在,直接获取到,不存在MongoDB
会自动创建:
MongoDatabase sang = client.getDatabase("sang");
然后通过如下方式获取一个名为c1的集合,这个集合存在的话就直接获取到,不存在的话MongoDB
会自动创建出来,如下:
MongoCollection<Document> c = sang.getCollection("c1");
有了集合之后,我们就可以向集合中插入数据了。
1、增加操作
和在shell
中的操作一样,我们可以一条一条的添加数据,也可以批量添加,添加单条数据操作如下:
Document d1 = new Document();
d1.append("name", "三国演义").append("author", "罗贯中");
c.insertOne(d1);
添加多条数据的操作如下:
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()
2、修改操作
可以修改查到的第一条数据,操作如下:
c.updateOne(Filters.eq("author", "罗贯中"), new Document("$set", new Document("name", "三国演义123")));
上例中小伙伴们也看到了修改器要如何使用,不管是inc
,用法都一致,我这里不再一个一个演示。也可以修改查到的所有数据,如下:
c.updateMany(Filters.eq("author", "罗贯中"), new Document("$set", new Document("name", "三国演义456")));
3、删除操作
可以删除查到的一条数据,如下:
c.deleteOne(Filters.eq("author", "罗贯中"));
也可以删除查到的所有数据:
c.deleteMany(Filters.eq("author", "罗贯中"));
Filters
里边还有其他的查询条件,都是见名知意,不赘述。
4、 查询操作
可以直接查询所有文档:
FindIterable<Document> documents = c.find();
MongoCursor<Document> iterator = documents.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
也可以按照条件查询:
FindIterable<Document> documents = c.find(Filters.eq("author", "罗贯中"));
MongoCursor<Document> iterator = documents.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
其他的方法基本都是见名知意,这里不再赘述。
5、验证问题
上面我们演示的获取一个集合是不需要登录MongoDB
数据库的,如果需要登录,我们获取集合的方式改为下面这种:
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
中设置即可,设置方式如下:
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");
主要讲解SpringBoot
操作MongoDB
实现增删改查的功能
1、pom.xml
引入依赖
org.springframework.boot
spring-boot-starter-data-mongodb
2、创建application.yml
spring:
data:
mongodb:
host: 192.168.72.129
database: studentdb
3、创建实体类
创建包com.changan.mongodb
,包下建包pojo
用于存放实体类,创建实体类
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
包,包下创建接口
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
包,包下创建类
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、创建测试类
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);
}
}
MongoDB
之副本集配置MongoDB
主从复制主从复制是MongoDB
最早使用的复制方式, 该复制方式易于配置,并且可以支持任意数量的从节点服务器,与使用单节点模式相比有如下优点:
在从服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。
可配置读写分离,主节点负责写操作,从节点负责读操作,将读写压力分开,提高系统的稳定性。
MongoDB
的主从复制至少需要两个服务器或者节点。其中一个是主节点,负责处理客户端请求,其它的都是从节点,负责同步主节点的数据。
主节点记录在其上执行的所有写操作,从节点定期轮询主节点获取这些操作,然后再对自己的数据副本执行这些操作。由于和主节点执行了相同的操作,从节点就能保持与主节点的数据同步。
主节点的操作记录称为oplog(operation log)
,它被存储在MongoDB
的 local
数据库中。oplog
中的每个文档都代表主节点上执行的一个操作。需要重点强调的是oplog
只记录改变数据库状态的操作。比如,查询操作就不会被存储在oplog
中。这是因为oplog
只是作为从节点与主节点保持数据同步的机制。
然而,主从复制并非生产环境下推荐的复制方式,主要原因如下两点:
1、灾备都是完全人工的 如果主节点发生故障失败,管理员必须关闭一个从服务器,然后作为主节点重新启动它。然后应用程序必须重新配置连接新的主节点。
2、数据恢复困难 因为oplog
只在主节点存在,故障失败需要在新的服务器上创建新的oplog
,这意味着任意存在的节点需要重新从新的主节点同步oplog
。
因此,在新版本的MongoDB
中已经不再支持使用主从复制这种复制方式了,取而代之的是使用副本集复制方式。
MongoDB
副本集MongoDB
副本集(Replica Set)
其实就是具有自动故障恢复功能的主从集群,和主从复制最大的区别就是在副本集中没有固定的“主节点;整个副本集会选出一个节点作为“主节点”,当其挂掉后,再在剩下的从节点中选举一个节点成为新的“主节点”,在副本集中总有一个主节点(primary)
和一个或多个备份节点(secondary)
。
除了primary
和secondary
之外,副本集中的节点还可以是以下角色:
成为primary | 对客户端可见 | 参与投票 | 延迟同步 | 复制数据 | |
---|---|---|---|---|---|
Default | √ | √ | √ | ∕ | √ |
Secondary-Only | ∕ | √ | √ | ∕ | √ |
Hidden | ∕ | ∕ | √ | ∕ | √ |
Delayed∕ | √ | √ | √ | √ | |
Arbiters | ∕ | ∕ | √ | ∕ | ∕ |
Non-Voting | √ | √ | ∕ | ∕ | √ |
官方推荐的副本集最小配置需要有三个节点:一个主节点接收和处理所有的写操作,两个备份节点通过复制主节点的操作来对主节点的数据进行同步备份。
1、环境准备
副本集各节点IP
如下:
172.16.250.234
172.16.250.239
172.16.250.240
首先,先对三个MongoDB
节点进行安装。
然后,依次修改各个节点的 mongodb.conf
配置文件,增加副本集相关配置,内容如下:
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
服务:
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()
方法对副本集进行初始化。
[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、副本集更新
# 向副本集中添加成员
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})
# 更改副本集配置
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
,可以将该节点的优先级设置成最高。
cfg = rs.conf()
cfg.members[0].priority = 5
cfg.members[1].priority = 1
cfg.members[2].priority = 1
rs.reconfig(cfg)
4、副本集监控
# 查看副本集的配置信息
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")
}
}
# 查看副本集运行状态
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)
}
}
}
# 查看备份节点的复制信息
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
在Primary
上插入一万条客户数据:
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
上查看客户数据是否已经同步:
rs0:SECONDARY> rs.slaveOk()
rs0:SECONDARY> db.customer.count()
10000
故障转移测试
执行如下命令关闭Primary
节点,查看其他2
个节点的情况:
mongod --shutdown --dbpath /usr/local/mongodb-4.0.2/data
# 查看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)
}
}
}
# 在任意其他节点上查看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
。
# 待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)
}
}
}
创建用户
登录 PRIMARY
节点创建用户,在此我们对 test
库开启安全认证。
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
文件。
[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
。
chmod 600 /usr/local/mongodb-4.0.2/keyfile
更新启动配置文件
修改PRIMARY
节点的 mongodb.conf
文件,增加如下内容:
# Add below Config
auth=true
oplogSize=100
keyFile=/usr/local/mongodb-4.0.2/keyfile
修改SECONDARY
节点的 mongodb.conf
文件,增加如下内容:
# Add below Config
oplogSize=100
keyFile=/usr/local/mongodb-4.0.2/keyfile
启动副本集
先以--auth
方式启动PRIMARY
节点:
[root@hadoop39 mongodb-4.0.2]# mongod -f /usr/local/mongodb-4.0.2/mongodb.conf
再启动SECONDARY
节点:
mongod -f /usr/local/mongodb-4.0.2/mongodb.conf
登录测试
[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
库。