目录
MongDb基本
MongDb介绍:
MongoDB是为现代应用程序开发人员和云时代构建的通用的、基于文档的分布式数据库。
MongoDB属于NoSQL数据库中的一种,NoSQL = Not Only SQL ,意即"现代开发不仅仅局限于基于SQL的关系型数据库,还应该有一些别的数据库,比如非关系型的数据库",NoSQL数据库叫非关系型数据库;
MongoDB 使用C++开发;
MongoDB诞生于2007,2009年2月首度推出;
MongoDB现在分为企业版和社区版,社区版免费;
官网:https://www.mongodb.com
与关系型数据库的比较
对比项 MongoDB MySQL、Oracle、SQL Server
数据库 需要有一个数据库, 比如TestDB 需要有一个数据库,比如TestDB
表 集合 Collection 表Table
一条记录 文档document(json串) 一条记录record
表字段 数据key 字段field
字段值 数据value 数据value
主外键 无 PK、FK
扩展性 极高 差
MongDb安装:
下载:
访问官方网站:https://www.mongodb.com 找到下载链接;
Linux:
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.0.tgz
windows: https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2012plus-4.2.0.zip
安装:
1、解压:
tar -zxvf mongodb-linux-x86_64-rhel70-4.2.0.tgz -C /usr/local/
其中-C表示解压到一个指定的目录,解压后即可使用,无需其他操作;
2、在安装主目录下创建一个data目录,用于MongoDB存储数据;
3、MongoDB 在bin 目录下提供了一系列有用的工具(可执行程序);
mongo: 客户端命令行工具,其实也是一个js 解释器,支持js 语法;
mongod: 数据库服务端,每个实例启动一个进程,可以fork 为后台运行;
bsondump: 将bson 格式的文件转储为json 格式的数据;
mongodump/ mongorestore: 数据库备份和恢复工具;
mongoexport/ mongoimport: 数据导出和导入工具;
mongofiles: GridFS 管理工具,可实现二制文件的存取;
mongos: 分片路由,如果使用了sharding 功能,则应用程序需要连接mongos;
MongDb客户端:
命令行客户端:
1、启动MongoDB:
切换到安装目录的bin目录下:
./mongod --dbpath=/usr/local/mongodb-4.2.0/data/
其中 --dbpath是指定MongoDB数据存储的位置,如果不指定默认是/data/db/ 目录下;
2、允许远程连接访问:
MongoDB默认只能本机ip可以访问,其他机器的ip的不能访问,启动时使用
--bind_ip=0.0.0.0
参数指定不限制绑定的ip;
3、后台启动MongoDB:
./mongod --bind_ip=0.0.0.0 --dbpath=/usr/local/mongodb-4.2.0/data/ --logpath=/usr/local/mongodb-4.2.0/logs.log --logappend --fork
通过--fork参数表示后台启动,使用fork参数时,其中logpath参数不能少,这是强制的,logappend表示日志追加 (可以省略);
可以把上面的命令封装到一个Linux的shell脚本中,方便操作;
4、登录MongoDB命令行:
在MongoDB安装目录的bin目录下执行:
./mongo
进入mongodb的命令行,默认进入test数据库;
./mongo 127.0.0.1:27017
进入指定ip端口的mongodb命令行;
./mongo 127.0.0.1:27017/test
进入指定ip端口和数据库test的mongodb命令行;
5、日常操作命令:
查看有多少数据库:show dbs 或者 show databases;
目前处于哪个数据库:db;
当前库中有多少个集合(表):show collections;或者show tables;
6、关闭MongoDB服务:
方式一:
> use admin;
> db.shutdownServer();
方式二:
./mongod --shutdown --dbpath /usr/local/mongodb-4.2.0/data/
方式三:
kill pid进程号
方式四:
如果是前台启动,直接Ctrl+c停止;
其他常用命令:
./mongod --version
./mongod –help
图形界面客户端:
官方 MongoDB Compass: https://www.mongodb.com
下载zip版,直接解压使用,双击MongoDBCompassCommunity.exe启动;
第三方 mongodb manager:https://www.mongodbmanager.com
需要安装一下,并破解;
第三方 studio3t: https://studio3t.com
需要安装一下,并破解;
第三方robo3t:https://robomongo.org/
推荐使用:开源免费
MongDb使用
常用命令操作:
创建数据库:
创建数据库,执行命令:
use mydb;
此时还没有数据库,如果再创建了集合就会有数据库;
创建集合:
创建集合有两种方式,显示创建和隐式创建
显示创建可以使用命令 db.createCollection(“集合名称")
隐式创建可以使用命令 db.集合名称.insert({}),指创建集合并同时向集合中插入数据,例如:
db.customer.insert({name:"cat"});
注意如果需要知道db后面可以使用的方法,可以使用命令 db.help();来查询
插入数据:
向集合添加文档,使用命令 db.集合名称.insert({}),例如:
db.t_user.insert({name:"jack",age:20});
db.collection.insertOne();
向指定集合中插入一条文档数据;
db.collection.insertMany();
向指定集合中插入多条文档数据;
db.users.insertOne({name:'张翠山', age:32, address:'冰火岛'});
db.users.insertMany([{name:'张无忌', age:21, address:'灵蛇岛'}, {name:'谢逊', age:54, address:'灵蛇岛'}]);
js代码循环插入数据:
for (var i=0; i<100; i++) {db.users.insert({name:"张三", age:100});}
MongoDB 支持的数据类型中,_id 是其自有产物,存储在MongoDB 集合中的每个文档(document)都有一个默认的主键_id,这个主键名称是固定的,默认是ObjectId。在关系数据库设计中,主键大多是数值型的,比如常用的int 和long,并且更通常的是主键的取值由数据库自增获得,这种主键数值的有序性有时也表明了某种逻辑。而MongoDB,它在设计之初就定位于分布式存储系统,所以它原生的不支持自增主键。
删除数据:
删除集合中的文档,使用命令 db.集合名称.remove({删除条件}),不加删除条件为删除集合中的所有文档,例如,
db.c1.remove() ;
为删除c1集合中的所有文档;
db.users.remove({name: '张翠山'})
为删除c1集合中name 为 张翠山 的文档;
删除集合:
db.t_user.drop();
其中t_user是集合的名称;
删除数据库:
删除当前数据库,使用命令
db.dropDatabase();
查询数据:
面向文档的NoSQL 数据库主要解决的问题不是高性能的并发读写,而是保证海量数据存储的同时,具有良好的查询性能。
MongoDB 最大的特点是他支持的查询语言非常强大,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。很多项目都考虑用MongoDB 来替代MySQL 等传统数据库来实现不是特别复杂的Web 应用。由于数据量实在太大,所以迁移到了MongoDB 上面,数据查询的速度得到了非常显著的提升。
查询集合中的文档,可以使用命令 db.集合名称.find({条件}),或者使用 db.集合名称.findOne() 查询第一个文档。
MongoDB查询还支持一些额外的参数选项:
db.users.find({}, {age:0});
--返回除age字段外的所有字段;
db.users.find({address: '灵蛇岛'},{_id:0});
--返回地址是'灵蛇岛'不返回_id字段;
db.users.find({address: '灵蛇岛'},{name:1});
--返回地址是'灵蛇岛'的name字段;
如果不明确指定,_id字段始终都会返回;
如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下:
db.users.find({}).pretty()
查询条件:
查询集合中的文档 ,使用条件表达式(<, <=, >, >=, ==, !=)
//大于: field > value
db.collection.find({field:{$gt:value}});
db.users.find({age:{$gt:10}});
//小于: field < value
db.collection.find({field:{$lt:value}});
//大于等于: field >= value
db.collection.find({field:{$gte:value}});
//小于等于: field <= value
db.collection.find({field:{$lte:value}});
//等于:field==value
db.collection.find({field:value});
db.users.find({name:"张无忌"});
//不等于: field != value
db.collection.find({field:{$ne:value}});
统计查询:
统计查询集合中的文档 ,统计(count)
db.collection.count();
db.collection.find().count();
db.collection.find({age:{$lt:5}}).count();
排序查询:
db.users.find().sort({age:1}); 升序
db.users.find().sort({age:-1}); 降序
分页查询:
分页(skip、limit、count)
d
b.users.find().skip(2).limit(3);
db.users.find().sort({age:-1}).skip(2).limit(3);
db.users.find().sort({age:-1}).skip(2).limit(3).count();
db.users.find().sort({age:-1}).skip(2).limit(3).count(0);
db.users.find().sort({age:-1}).skip(2).limit(3).count(1);
skip代表跳过多少条,limit代表查询多少条,count代表计数
查询and关系:
MongoDB AND 条件
MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即 SQL 的 AND 条件。
db.users.find({"name":"张三", "age":98});
查询or关系:
查询集合中的文档 $or,相当于关系型数据库中的OR,表示或者的关系,例如查询name为 张三丰 或者age为21的文档,命令为:
db.users.find({$or:[{name:'张三丰'},{age:21}]});
查询集合中的文档 $nor,表示根据条件过滤掉某些数据,例如查询name不是张三丰,age不是21的文档,命令为:
db.users.find({$nor:[{name:'张三丰'},{age:21}]});
查询包含关系:
查询集合中的文档 $all主要用来查询数组中的包含关系,查询条件中只要有一个不包含就不返回;
db.users.insertOne({name:'张无忌', age:21, address:['冰火岛', '武当山', '绿柳山庄', '光明顶']});
db.users.find({address : {$all:['武当山']} });
查询in关系:
查询集合中的文档 ,$in,类似于关系型数据库中的IN;
查询集合中的文档 ,$nin,与$in相反;
db.users.find({age : {$in:[21, 54, 100]} });
查询是否存在:
查询集合中的文档 $exists,用于查询集合中存在某个键的文档或不存在某个键的文档,例如查询customer集合中存在name键的所有文档,可以使用
db.users.find({name: {$exists:1}});
$exists:1表示真,指存在
$exists:0表示假,指不存在。
游标使用:
var allUsers = db.users.find();
allUsers.hasNext();
allUsers.count();
allUsers.next();
更新数据:
更新集合中的文档,语法如下:
db.collection.update(criteria,objNew,upsert,multi)
参数说明:
criteria: update的查询条件,类似sql update查询内where后面的
objNew: 用于设置更新内容的对象
upsert: 如果记录已经存在,更新它,否则新增一个记录,取值为0或1
multi:如果有多个符合条件的记录,是否全部更新,取值为0或1
注意:默认情况下,只会更新第一个符合条件的记录;
db.users.update({age:99}, {$set: {age:999}}, 1, 1);
更新集合中的文档,使用 $inc 将集合中age为99的age加1,其它键不变, $inc表示使某个键值加减指定的数值;
更新集合中的文档, $set 用来指定一个键的值,如果这个键不存在,则创建它。
给name为user1的文档添加address,可以使用命令:
db.users.update({name:”张三”},{$set:{address:”bj”}}, 0, 1)
将name为user1的文档修改address为tj,其它键值对不变,命令为:
db.c1.update({name:”user1”},{$set:{address:”tj”}},0,1)
更新集合中的文档, $unset 用来删除某个键,例如删除name为user1的文档中的address键,可以使用命令:
db.c1.update({name:”user1”},{$unset:{address:1}},0,1)
索引的使用:
索引就是用来加速查询的,数据库索引与书籍的目录类似,有了目录就不需要翻遍整本书,数据库通过索引查找,使得查找速度能提高几个数量级。在索引中找到条目以后,就可以直接跳转到目标文档的位置。
索引的相关命令:
创建普通索引:
db.users.createIndex({key:1})
查看关于索引的相关信息:
db.users.stats();
查看查询使用索引的情况:
db.collection.find({key:value}).explain();
删除索引:
db.collection.dropIndex({key:1});
删除集合(表),也会将集合中的索引全部删除
创建唯一索引:
db.users.createIndex ({key:1},{unique:true});
固定集合:
固定集合指的是事先创建而且大小固定的集合(表)。
如果空间不足,最早的文档就会被删除,为新的文档腾出空间。
固定集合适用于想要自动淘汰过期属性的场景。
创建固定集合使用命令:
db.createCollection("sms",{capped:true, size:100000, max:3});
capped选项为true,表示为固定集合;
size指定集合大小,单位为KB;
max指定文档的数量;
当指定文档数量上限时,必须同时指定大小,淘汰机制只有在容量还没有满时才会依据文档数量来工作,要是容量满了,淘汰机制会依据容量来工作。
GridFS:
GridFS 用于存储那些超过16M(BSON文件限制)的文件(如:文档、图片、音频、视频等);
GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合(表)中。
GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合(表)中。
GridFS 用两个集合(表)来存储一个文件:fs.files与fs.chunks。
每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性信息等)将会被存在files集合中。
GridFS 添加文件
使用 GridFS 的 put 命令来存储 视频 文件。 调用 MongoDB 安装目录下bin的 mongofiles工具。
打开命令提示符,进入到MongoDB的安装目录的bin目录中,找到mongofiles,并输入下面的代码:
切换到mongodb安装目录的bin目录下,执行:
./mongofiles -d gridfs put 20190920_214804.mp4
其中
-d 指定要存储的mongodb数据库名字,如果不存在该数据库,MongoDB会自动创建;
put表示添加文件;
20190920_214804.mp4是要添加的文件名;
然后进入上面的数据库,可以查询到上传的文件信息:
>db.fs.files.find();
然后我们可以根据这个 _id 获取区块(chunk)的数据:
>db.fs.chunks.find();
>db.fs.chunks.find({files_id:ObjectId('5da46cb3ad81b28f08361d1e')});
fs.files默认内容如下:
{
"_id" : ObjectId("5da423568ba8bb7b48ac5592"),
"length" : NumberLong(57309952),
"chunkSize" : 261120,
"uploadDate" : ISODate("2019-10-14T07:27:28.046Z"),
"filename" : "20190920_214804.mp4",
"metadata" : {}
}
fs.chunks默认内容如下:
{
"_id" : ObjectId("5da423568ba8bb7b48ac5593"),
"files_id" : ObjectId("5da423568ba8bb7b48ac5592"),
"n" : 0,
"data" : { "$binary" : "AAAAIGZ0eXBpc29tAAA........"
}
代码中使用
原生客户端:
<!-- mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.11.0</version>
</dependency>
连接URL格式:
mongodb://[username:password@]host1[:port1][,host2[:port2],…[,hostN[:portN]]][/[database][?options]]
mongodb:// 这是固定的格式,必须要指定。
username:password@ 可选项,如果设置,在连接数据库服务器之后,驱动都会尝试登陆这个数据库;
host1 必须的指定至少一个host, host1 是这个URI唯一要填写的。它指定了要连接服务器的地址。如果要连接复制集,请指定多个主机地址。
portX 可选的指定端口,如果不填,默认为27017;
/database 如果指定username:password@,连接并验证登陆指定数据库。若不指定,默认打开 test 数据库。
?options 是连接选项。如果不使用/database,则前面需要加上/。所有连接选项都是键值对name=value,键值对之间通过&或;(分号)隔开;
spring整合:
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
http://www.springframework.org/schema/data/mongo https://www.springframework.org/schema/data/mongo/spring-mongo.xsd
<!-- Default bean name is 'mongo' -->
<mongo:mongo-client id="mongo" host="192.168.87.129" port="27017" />
<mongo:db-factory id="mongoDbFactory" mongo-ref="mongo" dbname="blog"/>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory" />
</bean>
需要在工厂里面也指定一下数据库名称,否则默认是插入到名叫db的数据库;
MongoDB+GridFS案例:
实际上存储文件,还有更多的选择比如采用分布式文件系统,如FastDFS、HDFS等;
MongoDB也可以存文件,因为mongodb内置的GridFS本身就是一个文件系统,不过大量文件的存储还是建议采用分布式文件系统来实现会更好,分布式文件系统比mangodb存储文件效率更高;
MongoDB在国内有很多大公司都在使用,比如 京东、360、美团、优酷、58同城、视觉中国等,国外也很多公司在使用;
权限认证
1、MogoDB默认没有账号和密码,只要连上了就可以增删改查任何操作;
2、启动mongodb服务时候,首先不要加--auth参数;
3、首先分配一个超级管理员,创建管理员,切换到admin数据库,然后创建;
(默认admin、config、local)
use admin;
db.createUser({user:"root", pwd:"123456", roles:[{role:"root", db:"admin"}]});
(附录:)
1、切换到admin数据库下,执行:
db.system.users.find();
查看目前monggodb已经有哪些用户账号了;
db.system.users.find().pretty();
可以美化数据输出;
2、要删除某个用户,就切换到该用户所属的库中,执行:db.dropUser('cat01'); 其中'cat01'是用户名;
3、一个库可以分配很多个用户;
超级管理员的角色是root;
3、然后用超级管理员给其他数据库分配账号,一般地会给指定的用户对指定的数据库有读权限或者读写权限,
比如现在要给sample库分配一个登录认证账号,切换到sample数据库再操作
use sample;
db.createUser({user:"cat", pwd:"123456", roles:[{role:"readWrite", db:"sample"}]});
或者
db.createUser({user:"cat01", pwd:"123456", roles:[{role:"read", db:"sample"}]});
4、上面用户分配好之后,在启动参数中添加--auth开启登录认证,重启mongodb服务;
5、超级管理员登录 ./mongo -u root -p,超级管理员是root角色,可以操作一切;
6、普通用户登录 ./mongo sample -u cat -p 其中sample 是数据库名,cat是用户名,普通用户登录后只能操作被赋予的角色所对应的权限;
权限角色:
read:允许用户读取指定数据库;
readWrite:允许用户读写指定数据库;
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计;
userAdmin:允许用户向system.users集合写入,可以指定数据库里创建、删除和管理用户;
clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限;
readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限;
readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限;
userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限;
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限;
root:只在admin数据库中可用,超级账号,超级权限;
MongDb集群
MongoDB集群搭建有三种方式:
1、Master-Slave模式
2、Replica-Set方式
3、Sharding方式
在MongoDB 2.x系列的版本,有一个主从复制模式,但是MongoDB官方也不推荐使用,而从MongoDB 3.2开始,主从复制模式已经被标记成过时了,不推荐使用;
从MongoDB 4.0 开始已经完全移除了主从复制模式,推荐使用副本集和分片集群模式;
副本集
副本集就是有自动故障转移功能的主从复制模式
主从复制和副本集最大的区别就是副本集没有固定的“主节点”,整个集群会选出一个“主节点”,当其挂掉后,又在剩下的从节点中选中其他节点为“主节点”,
当原来的主节点修复之后,原来的主节点会再次恢复为主节点,备节点不再是主节点的角色。
副本集的方式还有一个角色,那就是仲裁节点,它不存储数据,他的作用就是当主节点出现故障,选举出备节点作为主节点,继续保证集群可用。客户端连接时只连接主节点或者备节点,不用连接仲裁节点。
副本集至少需要3个节点, 可以3个全部做副本集, 也可以让其中一个做仲裁;
副本集中只有1台主可以进行读和写,其余节点只能读;
副本集之间的复制是通过oplog日志现实的,oplog是节点中local库中的一个集合,备份节点通过查询这个集合就可以知道需要进行复制的操作;
每个节点通过心跳去查看其他节点的状态;
当从节点宕机,不用影响副本集的使用;
当主节点宕机,各个节点通过选举的方式来选择下一个主节点;
若所有的从都宕机只剩下Primary(主),则Primary会变成Secondary(从),不能提供服务;
MongoDB 3.2.0版本前是基于Bully选举算法(霸道选举算法),从3.2.0开始默认使用基于Raft算法的选举策略;
副本集集群搭建
1、以三个节点为例,配置三个副本集的配置文件;
(我们是在一台linux下操作的,端口号不能重复,实际在项目中是在多台linux操作,那么区别就是端口号可以一样)
节点1的配置文件 master.conf:
dbpath=/usr/local/mongodb-4.2.0/data/master
logpath=/usr/local/mongodb-4.2.0/logs/master.log
pidfilepath=/usr/local/mongodb-4.2.0/logs/master.pid
directoryperdb=true
logappend=true
replSet=testrs
bind_ip=0.0.0.0
port=27018
oplogSize=10000
fork=true
节点2的配置文件 slave.conf:
dbpath=/usr/local/mongodb-4.2.0/data/slave
logpath=/usr/local/mongodb-4.2.0/logs/slave.log
pidfilepath=/usr/local/mongodb-4.2.0/logs/slave.pid
directoryperdb=true
logappend=true
replSet=testrs
bind_ip=0.0.0.0
port=27019
oplogSize=10000
fork=true
节点3 的配置文件 arbiter.conf:
dbpath=/usr/local/mongodb-4.2.0/data/arbiter
logpath=/usr/local/mongodb-4.2.0/logs/arbiter.log
pidfilepath=/usr/local/mongodb-4.2.0/logs/arbiter.pid
directoryperdb=true
logappend=true
replSet=testrs
bind_ip=0.0.0.0
port=27020
oplogSize=10000
fork=true
注意要保证上面配置的路径的文件夹都要存在,不存在的话要创建好;
directoryperdb:设置为true,修改数据目录存储模式,每个数据库的文件存储在DBPATH指定目录的不同的文件夹中,使用此选项,可以配置的MongoDB将数据存储在不同的磁盘设备上,以提高写入吞吐量或磁盘容量,默认为false;
注意:在运行一段时间的数据库中开启该参数,会导致原始的数据都会消失(注释参数则会回来),因为数据目录不同了,除非迁移现有的数据文件到directoryperdb产生的数据库目录中,如:
mv test.* test/
把test数据文件迁移到directoryperdb产生的数据库test目录中,所以需要在规划好之后确定是否要开启;
logappend:写日志的模式:设置为true为追加;
replSet:使用此设置来配置复制副本集,指定一个副本集名称作为参数,所有主机都必须有相同的名称作为同一个副本集;
oplogSize:指定的复制操作日志(OPLOG)的最大大小,mongod创建一个OPLOG的大小基于最大可用空间,对于64位系统,OPLOG通常是5%的可用磁盘空间;
一旦mongod第一次创建OPLOG,改变oplogSize将不会影响OPLOG的大小;
2、然后依次启动每个节点,切换到mongodb的bin目录下;
./mongod -f ../conf/master.conf
./mongod -f ../conf/slave.conf
./mongod -f ../conf/arbiter.conf
3、创建集群:
连接登录第一个节点:
./mongo 127.0.0.1:27018
使用admin数据库:
use admin
执行配置:(注意:这里的id要和配置文件中的replSet属性保持一致,这里的host如果是生产环境,要改为真实的ip地址,不然java客户端会提示连接不上)
var cfg={_id:"testrs", members:[
{_id:0,host:"192.168.87.129:27018",priority:2},
{_id:1,host:"192.168.87.129:27019",priority:1},
{_id:2,host:"192.168.87.129:27020",arbiterOnly:true}
]
};
初始化副本集:
rs.initiate(cfg);
如果是重置副本集配置则使用:
rs.reconfig(cfg);
稍等几秒,然后查看集群状态:
rs.status();
重点查看 "stateStr":"PRIMARY", 信息;
4、主节点测试:
可以正常插入数据,可以读,可以写;
从节点连接:
从节点默认情况下不能写,也不能查,比如你查询数据库会出错:not master and slaveOk=false,这个错误是正常的,默认情况下从节点不允许读和写操作,可以执行rs.slaveOk()命令让从机器能进行读操作;
5、故障测试:
主节点挂掉:在备节点上查看集群状态,会发现备节点成为主节点:primary。
这里如果再次将挂掉的master节点启动,那么等一会儿原来的master节点会变为primary主节点,而不是由slave备节点继续充当主节点;
分片集群
(与redis非常相似: 副本集-->redis的哨兵, 分片 -->redis cluster)
100G数据,原来放在一个mongodb上的,现在把它放到3个mongodb上,这就是分片,然后3个mongodb上的数据总和还是100G;
分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程,将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载,其基本思想就是将集合切成小块,这些块分散到若干片里,每个片只负责存储总数据中的一部分,最后通过一个路由均衡器来对各个分片进行路由均衡,通过一个名为mongos的路由进程进行操作,mongos知道数据和片的对应关系(通过配置服务器),大部分使用场景都是解决磁盘空间的问题,查询则尽量避免跨分片查询;
使用分片的时机:
1,机器的磁盘不够用了,使用分片解决磁盘空间的问题;
2,单个mongod已经不能满足写数据的性能要求,通过分片让写压力分散到各个分片上;
3,希望把大量数据放到内存中提高性能,通过分片使用分片服务器来分摊压力;
MongoDB分片sharding集群方式是基于副本集,整个sharding方式的集群中,有三类角色,分别是shard,config,router;
shard:分片服务器,用于存储实际的数据块,实际生产环境中一个shard server 角色可以由几台服务器组成一个Peplica Set 承担,防止主机单点故障;
Config:配置节点,不会存储数据,会存储元数据信息,比如片键的范围;
router:路由节点,是mongo集群与外部客户端连接的入口,提供客户端连接访问,对客户端透明,让客户端感觉使用像是单节点数据库;
1、三台配置服务形成副本集(存储元数据信息,一般冗余3台),
2、三台分片服务形成一个副本集(提供两组或多组分片服务副本集),
3、客户端直接与三台路由服务连接交互,配置服务和分片服务对客户端是透明的;
分片集群搭建 :
3台配置服务器部署(3台服务器执行相同操作)
1、在/usr/local/mongodb-4.2.0/conf/sharding目录创建:
config37018.conf
config37019.conf
config37020.conf
dbpath=/usr/local/mongodb-4.2.0/data/sharding/config37018
logpath=/usr/local/mongodb-4.2.0/logs/sharding/config37018.log
pidfilepath=/usr/local/mongodb-4.2.0/logs/sharding/config37018.pid
port=37018
logappend=true
fork=true
replSet=configs
configsvr=true
bind_ip=0.0.0.0
dbpath=/usr/local/mongodb-4.2.0/data/sharding/config37019
logpath=/usr/local/mongodb-4.2.0/logs/sharding/config37019.log
pidfilepath=/usr/local/mongodb-4.2.0/logs/sharding/config37019.pid
port=37019
logappend=true
fork=true
replSet=configs
configsvr=true
bind_ip=0.0.0.0
dbpath=/usr/local/mongodb-4.2.0/data/sharding/config37020
logpath=/usr/local/mongodb-4.2.0/logs/sharding/config37020.log
pidfilepath=/usr/local/mongodb-4.2.0/logs/sharding/config37020.pid
port=37020
logappend=true
fork=true
replSet=configs
configsvr=true
bind_ip=0.0.0.0
2、配置复制集
分别启动三台服务器的配置服务:
./mongod -f /usr/local/mongodb-4.2.0/conf/sharding/config37018.conf
./mongod -f /usr/local/mongodb-4.2.0/conf/sharding/config37019.conf
./mongod -f /usr/local/mongodb-4.2.0/conf/sharding/config37020.conf
连接mongo,只需在任意一台机器执行即可:
./mongo 192.168.10.128:37018
切换数据库:
use admin
初始化复制集:
rs.initiate(
{
_id: "configs",
members: [{
_id: 0,
host: "192.168.10.128:37018",priority:2
}, {
_id: 1,
host: "192.168.10.128:37019",priority:1
}, {
_id: 2,
host: "192.168.10.128:37020",arbiterOnly:true
}]
}
)
其中_id:"configs"的configs是上面config.conf配置文件里的复制集名称,把三台服务器的配置服务组成复制集。
等一会儿,查看状态:
rs.status()
三台机器的配置服务就已形成复制集,其中1台为PRIMARY,其他2台为SECONDARY。
--------------------------------------
3台分片服务部署(3台服务器执行相同操作)
第一组分片:
1、在/usr/local/mongodb-4.2.0/conf/sharding目录创建:
shard47018.conf
shard47019.conf
shard47020.conf
dbpath=/usr/local/mongodb-4.2.0/data/sharding/shard47018
logpath=/usr/local/mongodb-4.2.0/logs/sharding/shard47018.log
port=47018
logappend=true
fork=true
shardsvr=true
replSet=shard1
bind_ip=0.0.0.0
dbpath=/usr/local/mongodb-4.2.0/data/sharding/shard47019
logpath=/usr/local/mongodb-4.2.0/logs/sharding/shard47019.log
port=47019
logappend=true
fork=true
shardsvr=true
replSet=shard1
bind_ip=0.0.0.0
dbpath=/usr/local/mongodb-4.2.0/data/sharding/shard47020
logpath=/usr/local/mongodb-4.2.0/logs/sharding/shard47020.log
port=47020
logappend=true
fork=true
shardsvr=true
replSet=shard1
bind_ip=0.0.0.0
第二组分片:
1、在/usr/local/mongodb-4.2.0/conf/sharding目录创建:
shard57018.conf
shard57019.conf
shard57020.conf
dbpath=/usr/local/mongodb-4.2.0/data/sharding/shard57018
logpath=/usr/local/mongodb-4.2.0/logs/sharding/shard57018.log
port=57018
logappend=true
fork=true
shardsvr=true
replSet=shard2
bind_ip=0.0.0.0
dbpath=/usr/local/mongodb-4.2.0/data/sharding/shard57019
logpath=/usr/local/mongodb-4.2.0/logs/sharding/shard57019.log
port=57019
logappend=true
fork=true
shardsvr=true
replSet=shard2
bind_ip=0.0.0.0
dbpath=/usr/local/mongodb-4.2.0/data/sharding/shard57020
logpath=/usr/local/mongodb-4.2.0/logs/sharding/shard57020.log
port=57020
logappend=true
fork=true
shardsvr=true
replSet=shard2
bind_ip=0.0.0.0
两组3台机器的形成两个分片的复制集,每一组有3台机器,所以启动6个分片服务:
./mongod -f /usr/local/mongodb-4.2.0/conf/sharding/shard47018.conf
./mongod -f /usr/local/mongodb-4.2.0/conf/sharding/shard47019.conf
./mongod -f /usr/local/mongodb-4.2.0/conf/sharding/shard47020.conf
./mongod -f /usr/local/mongodb-4.2.0/conf/sharding/shard57018.conf
./mongod -f /usr/local/mongodb-4.2.0/conf/sharding/shard57019.conf
./mongod -f /usr/local/mongodb-4.2.0/conf/sharding/shard57020.conf
2、将分片配置为复制集
连接mongo,只需在任意一台机器执行即可:
./mongo 192.168.10.128:47018
切换数据库:
use admin
初始化复制集:
rs.initiate(
{
_id: "shard1",
members: [{
_id: 0,
host: "192.168.10.128:47018",priority:2
}, {
_id: 1,
host: "192.168.10.128:47019",priority:1
}, {
_id: 2,
host: "192.168.10.128:47020",arbiterOnly:true
}]
}
)
以上是基于分片1来操作,同理,其他2个分片也要连到各自的端口来执行一遍上述的操作,让3个分片各自形成1主2从的复制集;
./mongo 192.168.10.128:/57018
这个还需要再执行一遍;
-----------------------------------------------------
路由服务部署(3台服务器执行相同操作)
1、在在/usr/local/mongodb-4.2.0/conf/sharding目录创建:
mongos27018.conf
mongos27019.conf
mongos27020.conf
dbpath=/usr/local/mongodb-4.2.0/data/sharding/mongos27018
logpath=/usr/local/mongodb-4.2.0/logs/sharding/mongos27018.log
pidfilepath=/usr/local/mongodb-4.2.0/logs/sharding/mongos27018.pid
logappend = true
port = 27018
fork = true
configdb = configs/192.168.10.128:37018,192.168.10.128:37019,192.168.10.128:37020
maxConns=20000
bind_ip=0.0.0.0
dbpath=/usr/local/mongodb-4.2.0/data/sharding/mongos27019
logpath=/usr/local/mongodb-4.2.0/logs/sharding/mongos27019.log
pidfilepath=/usr/local/mongodb-4.2.0/logs/sharding/mongos27019.pid
logappend = true
port = 27019
fork = true
configdb = configs/192.168.10.128:37018,192.168.10.128:37019,192.168.10.128:37020
maxConns=20000
bind_ip=0.0.0.0
dbpath=/usr/local/mongodb-4.2.0/data/sharding/mongos27020
logpath=/usr/local/mongodb-4.2.0/logs/sharding/mongos27020.log
pidfilepath=/usr/local/mongodb-4.2.0/logs/sharding/mongos27020.pid
logappend = true
port = 27020
fork = true
configdb = configs/192.168.10.128:37018,192.168.10.128:37019,192.168.10.128:37020
maxConns=20000
bind_ip=0.0.0.0
2、启动mongos
./mongos -f /usr/local/mongodb-4.2.0/conf/mongos27018.conf
./mongos -f /usr/local/mongodb-4.2.0/conf/mongos27019.conf
./mongos -f /usr/local/mongodb-4.2.0/conf/mongos27020.conf
3、启动分片功能
连接mongo:
./mongo 192.168.10.128:27018
切换数据库:
use admin
添加分片,只需在一台机器执行即可:
sh.addShard("shard1/192.168.10.128:47018,192.168.10.128:47019,192.168.10.128:47020")
sh.addShard("shard2/192.168.10.128:57018,192.168.10.128:57018,192.168.10.128:57018")
查看集群状态:
sh.status()
4、实现分片功能
设置分片chunk大小
use config
db.setting.save({"_id":"chunksize","value":1}) #设置块大小为1M是方便实验,不然需要插入海量数据
5、模拟写入数据
use sample
for(i=1;i<=50000;i++){db.user.insert({"id":i,"name":"cat"+i})} #模拟往sample数据库的user表写入5万数据
6、启用数据库分片
sh.enableSharding("sample")
7、创建索引,对表进行分片
db.user.createIndex({"id":1}) #以"id"作为索引
sh.shardCollection(sample.user",{"id":1}) #根据"id"对user表进行分片
sh.status() #查看分片情况
到此MongoDB分布式集群就搭建完毕;
副本集集群添加安全认证
1.要求
(1)通过密钥文件进行身份验证时,副本集中的每个mongo实例都使用密钥文件的内容作为与其他成员进行身份验证的共享密码。
只有拥有正确密钥文件的Mongod实例才能加入副本集。
(2)密钥文件的内容必须在6到1024个字符之间,并且对于副本集的所有成员必须是相同的。
2创建
使用openssl生成用于密钥文件
key的长度必须是6-1024的base64字符,unix下必须相同组权限,windows下不需要。
3更改权限
chmod 600 ./mongodb.key
4 将密钥文件复制到每个副本集成员上
将密钥文件复制到托管副本集成员的每个服务器上,确保运行mongod实例的用户是该文件的所有者,并且可以访问密钥文件;
#生成key(密钥文件)
安装openssl库:
yum install openssl -y
openssl rand -base64 756 > mongodb.key
chmod 600 mongodb.key
# 该key的权限必须是600
将该key放到集群中机器的每一台上,记住必须保持一致,权限设置成600;
在mongodb.conf启动配置文件中增加配置项
#安全认证机制
clusterAuthMode=keyFile
keyFile=/usr/local/mongodb-4.2.0/conf/mongodb.key
主库配置用户
也可后面配置,开启keyfile认证就默认开启了auth认证了,为了保证后面可以登录,在开启认证前先创建用户:
先创建管理员账户(在主库上创建即可,另外两台会复制)
use admin;
db.createUser({user:"root", pwd:"123456", roles:[{role:"root", db:"admin"}]});
重启进入
重新启动mongodb,记住重新启动时候,keyfile的指定如果没有在配置文件中配置,就必须启动时候使用参数keyfile指定,关闭顺序注意下,mongodb集群有自动切换主库功能,如果先关主库,主库就切换到其它上面去了,这里预防主库变更,从库关闭后再关闭主库。
登录时使用: ./mongo -uroot -p --host 192.168.10.128 --port 27018
进入后创建普通用户:(在主库上创建即可,另外两台会复制)
use sample;
db.createUser({user:"cat", pwd:"123456", roles:[{role:"readWrite", db:"sample"}]});
MongDb持久化和内存
持久化
我们知道MySQL的每一次更新操作都会直接写入硬盘,但是MangoDB不会,MangoDB做为内存型NoSQL数据库,数据操作会先写入内存,然后再会持久化到硬盘中,那么MangoDB是如何持久化的?
MangoDB在启动时,会初始化一个线程不断循环,用于在一定时间间隔内从defer队列中获取要持久化的数据并写入磁盘的journal(日志) 和 mongofile(数据) 处,但它不是在用户添加数据时就立刻写到磁盘上的,MangoDB官方为了不造成性能上的损耗,所以当进行增删改操作时,记录(Record类型)都会被放入到defer队列中以供延时批量提交写入磁盘,mongodb默认每隔100毫秒写入磁盘,如果该值更低的话,可能会造成频繁磁盘操作,过高又可能会造成系统宕机时数据丢失;
调整时间间隔通过以下命令来实现:journalCommitInterval,间隔的范围为(2毫秒-500ms);
比如设置MangoDB每隔10毫秒将写入数据写入到日志文件中:
在数据库命令行执行:
db.adminCommand({"setParameter":1,"journalCommitInterval":10});
内存使用
MongoDB占用内存会非常高,这是它为了提升读写性能,MangoDB就是这么设计的。
但是有时候我们的服务器内存并没有那么大,所以我们需要配置MongoDB的内存缓存大小,不然MangoDB会占用内存非常多;
可以通过如下参数配置MongoDB的内存使用大小;
--wiredTigerCacheSizeGB arg
Maximum amount of memory to allocate for cache;
Defaults to 1/2 of physical RAM
比如配置:--wiredTigerCacheSizeGB 10 表明内存大小是10G,默认情况下MongoDB是使用物理内存的二分之一;
MongoDb事务管理
在MongoDB 4.0版本之前是没有事务管理,但是2018年初MongoDB发布了4.0版本,其中最重大的标志就是引入了事务管理。
官方新闻:https://www.zdnet.com/article/mongodb-4-0-will-take-acid/
MongoDB事务必须用在副本集或分片情景下使用,否则会报错:
Transaction numbers are only allowed on a replica set member or mongos.
要求mongodb版本在4.0以上,分片情景下的事务从mongodb4.2版本才开始支持.
注意:副本集事务只对已经存在的mongodb中的集合起作用,如果要进行操作的集合,在mongodb中还没有,必须得先创建该集合,否则当该集合进行插入操作时,会报类似“Cannot create namespace sample.users in multi-document transaction.”的错误
图形界面中使用事务
使用事务步骤:
获取session:session = db.getMongo().startSession();
开启事务:session.startTransaction();
获得collection:users= session.getDatabase("sample").users;
操作:users.insert({"name":"zhangsan"});
回滚事务:session.abortTransaction();
提交事务:session.commitTransaction();
举例说明:
use sample;
session=db.getMongo().startSession();
session.startTransaction();
users = session.getDatabase("sample").users;
users.insert({"name":"zhangsan"});
session.abortTransaction();
session.commitTransaction();
代码中使用事务
客户端使用:
/**
* MongoDB事务测试
*
*/
public class TransactionTest {
public static void main(String[] args) {
//参数, 副本集集群
String uri = "mongodb://cat:123456@192.168.10.128:27018,192.168.10.128:27019/sample?replicaSet=testrs";
final MongoClient client = MongoClients.create(uri);
//开始session会话
ClientSession clientSession = client.startSession();
try {
//开启事务
clientSession.startTransaction();
MongoCollection<Document> coll1 = client.getDatabase("sample").getCollection("person");
MongoCollection<Document> coll2 = client.getDatabase("sample").getCollection("person");
coll1.insertOne(clientSession, new Document("abc", 1));
coll2.insertOne(clientSession, new Document("xyz", 999));
//发生异常
//int a = 10 / 0;
//提交事务
clientSession.commitTransaction();
//回滚事务
//clientSession.abortTransaction();
} catch (Exception e) {
e.printStackTrace();
//发生异常,回滚事务
clientSession.abortTransaction();
} finally {
clientSession.close();
}
}
}
spring中使用:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo
https://www.springframework.org/schema/data/mongo/spring-mongo.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.bjpowernode.mongodb"/>
<context:property-placeholder location="classpath:mongodb.properties"/>
<!-- replica-set 副本集连接 -->
<!-- replica-set格式:ip1:port,ip2:port,... -->
<mongo:mongo-client id="mongoClient" replica-set="192.168.10.128:27018,192.168.10.128:27019" credentials="cat:123456@sample">
<mongo:client-options connections-per-host="100"/>
</mongo:mongo-client>
<mongo:db-factory id="mongoDbFactory" mongo-ref="mongoClient" dbname="${db.name}"/>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory" />
</bean>
<!-- 在配置mongdb的配置文件中,加入事务配置 -->
<bean id="mongoTransactionManager" class="org.springframework.data.mongodb.MongoTransactionManager">
<constructor-arg name="dbFactory" ref="mongoDbFactory"/>
</bean>
<tx:annotation-driven transaction-manager="mongoTransactionManager" proxy-target-class="true"/>
</beans>
使用方式跟spring中对mysql的使用方式一样了,采用@Transction();注解的方式