##mongo学习,最近项目中要存储一些非结构化的数据,因此需要使用到MongoDB。非结构化数据如:物联网项目中的设备模型(属性、服务、事件)每个设备或者每种设备都完全不一样,使用mysql估计需要6-7张表,为了存储、查询方便,我们采用MongoDB。
一、 MongoDB优势
MongoDB优势:
1: 副本集Replication(类似主从),但可以自动容灾,容灾是自动选举一个新的节点做主节点(primary、secondary)。主写,记录日志oplog,从库从oplog读取信息,写入从库。
2:分片sharding:当一台机器,难以存储海量数据,可以通过分片,把数据分散到不同的机器上面。副本、分片,让MongoDB天生可靠、负载更简单,其他数据库需要额外负载的中间件才能完成。
3:不需要定义表结构,直接存储。表设计,可以使用 关系型数据库设计完成。
4:表查询有join 或者 复杂查询 或者 强事物的 不建议使用MongoDB。
5: 支持TB、PB级别的数据,有索引的字段不必mysql、没有索引的全面胜出mysql
MongoDB缺点:
1: 占用空间大
2: 开发 运维使用工具少; Navicat for mongo, 建议先创建一个新的db 进行操作,不要用内置库。
- 默认端口 27017, MongoDB经常遭到黑客入侵,我们该端口47071
- 自带数据库 local、config、admin, 一般不要使用默认自带数据库。比如使用local, 如果是副本集的情况,故障local数据里面会丢失。
参考: [1、mongodb中的admin数据库和local数据库(https://www.cnblogs.com/MyOnlyBook/articles/6201391.html)
参考:[2、什么是MongoDB ]
参考:Mongodb集群搭建的三种方式
1. MongoDB简单命令
1: MongoDB默认有三个内置库,我们自己创建新库使用
2:查询库中的数据
3:添加数据库
> mongo -version; #4.x 目前使用MongoDB4.x
> mongo # 如: mongo --port=47071 使用MongoDB直接进入MongoDB;mongoDB 没有root用户,只有能管理用户的用户userAdminAnyDatabase; 如果有授权,必须执行下面自己授权。 如果有其他设置,请 mongo --help ,加上指定参数。
> use admin; #必须切换admin, 然后 show dbs;发现没有任何表,说明,开启密码,必须登录。
> db.auth('admin','admin@123xX.`1];'); #如果开启密码,这里必须授权登录。 前面是账号、后面是密码; 返回1,登录成功;
#db.auth('iotMongoUserX','.123aB.iotMongoUserXpWD');
> show dbs; #查看所有数据库,登录之后就可以查看所有db了。
> show users; #查看当前库中MongoDB的所有用户。普通账号,查看会报错。
# mongo 创建用户:
use admin;
db.createUser(
{
user: "admin",
pwd: "admin@123xX.`1];",
roles: [ { role: "userAdminAnyDatabase", db: "admin" }, "readWriteAnyDatabase" ]
}
)
)
# 指定数据库,重新登录之后,就只能看到自己的两个数据库,其他配置库和系统库都看不到的。
db.createUser(
{
user: "iotMongoUserX",
pwd: ".123aB.iotMongoUserXpWD",
roles: [ { role: "readWrite", db: "iot_gateway_mongo,iot_platform_mongo" },
{ role: "read", db: "testMongoDb" } ]
}
);
# 修改用户角色、密码
db.updateUser(
"<username>",
{
customData : { <any information> },
roles : [
{ role: "<role>", db: "<database>" } | "<role>",
...
],
pwd: "<cleartext password>"
},
writeConcern: { <write concern> }
)
#删除用户:
db.dropUser('mongoIotuser');
# 查看所有用户,先切换到admin
show users;
#创建数据库 & 插入测试数据 和 查看
> use testdb; #如果不存在,则会自动创建数据库。要显示需要插入数据;《testdb》
> db.createCollection("students"); #创建集合students,当然也可以不创建,不存在会生成。
> show collections; #查看集合
> db.students.insert({"name":"张三"}); #创建一个文档《students》
> db.students.find().pretty(); #查询文档《students》中数据
> db.students.drop(); #删除整个集合;db.getCollection('RunLog-mw-airconditioning-hailin').drop();#如果有特殊字符,就这样处理
>
MongoDB用户和密码登录
MongoDB3.4开启用户验证、设置远程连接及更改数据存储路径
Mongodb 开启用户名密码访问控制
2 centos7 手工 安装使用
Centos7下yum安装mongodb - 自动yum 安装,手工下载官网太慢了
# yum 安装源,上面博文MongoDB版本是3.x,我们使用4.x; 别的全看上面的博客《Centos7下yum安装mongodb》
#vim /etc/yum.repos.d/mongodb-org-4.0.repo
[mngodb-org]
name=MongoDB Repository
baseurl=http://mirrors.aliyun.com/mongodb/yum/redhat/7Server/mongodb-org/4.0/x86_64/
gpgcheck=0
enabled=1
centos 查看MongoDB位置:whereis mongo 或者 find / -name "*mongo*"
centos 重启MongoDB: systemctl restart mongod.service
centos 查看MongoDB状态: systemctl status mongod.service
目录1-命令目录:/usr/bin/mongo
目录2-数据目录:/var/lib/mongo 最好换一个目录,防止数据库目录把磁盘占满。eg: /usr/local2/mongo-data
目录3-备份目录,我们自己创建一个目录:/usr/local/mongodbbake/
目录4-配置目录:/etc/mongod.conf
#mongod.conf配置文件,我们对默认配置进行修改
# where to write logging data.
systemLog:
logAppend: false #默认true,每次启动是追加日志;false: 是创建新日志文件。
port: 47071 #我们从27017改成47017,防止默认端口导致漏洞或者被黑客攻击
bindIp: 0.0.0.0 # 默认是:127.0.0.1 只有本机可以访问。 Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.; 默认绑定:127.0.0.1 就只能本地用户可以访问。0.0.0.0 允许所有人访问。
#security:
# authorization: enabled # 注释就是没密码,不注释就是开启密码
# MongoDB默认没有密码,我们一定要创建密码; 修改配置之后必须重启
systemctl restart mongod.service
3. MongoDB 复制集
复制集类似主从,但是比主从要方便灵活,因此平时主要用的是复制集。MongoDB自带功能。
4. MongoDB 切片
MongoDB切片是基于复制集的,每个片都是一个复制集。因此搭建有两个分片MongoDB,就要4台服务器。
5. rebo 3t 客户端工具
免费的MongoDB客户端工具;当然Navicat premium最新版也支持MongoDB
6. mongodb 数据库连接池
Spring Boot 配置 Mongo DB 数据库连接池
# MongoDB 配置,指定数据库连接池;
# 连接池第三方的
### mongodb 配置,这里 MongoDB 没有设置账号和密码
data:
mongodb:
# uri: mongodb://39.98.173.142 #无账号密码,禁止
uri: mongodb://iotMongoUserX:.123aB.iotMongoUserXpWD@39.98.173.142:27017
database: iot_platform_mongo #指定数据库
port: 27017
option: # 每个主机的最小和最大连接数
min-connection-per-host: 10
max-connection-per-host: 150
Spring Boot 配置 Mongo DB 数据库连接池
mongodb 查看连接数和最大连接数
关于配置mongoDB连接池
7 MongoDB 定时备份
mongodb 定时备份 完美,文档非常好、定时备份、恢复都很详细。
# 目前都是通过服务器,没有账号密码恢复的。mongorestore 直接是shell命令; 不用进入MongoDB里面
# 说明:mongorestore 直接是一个shell命令,不用进入到 MongoDB shell控制台
# 这里没有密码注意,可能需要账号和密码; mongorestore --help可以查看
# 备份恢复示例1
# -h: 指定MongoDB数据库地址和端口; 默认本地
# –drop: 指定先删除表,后恢复。避免出现重复。可选
# -d : 指定恢复到那个一个数据库
# -u、-p同;默认无账号、密码
# --authenticationDatabase ,指定授权db: --authenticationDatabase=<database-name>
mongorestore -h localhost:47071 -d iot_platform_mongo -u iotMongoUserX -p .123aB.iotMongoUserXpWD /usr/local2/mongobake/iot_gateway_mongo/ --authenticationDatabase admin
# 备份恢复示例2
mongorestore -h localhost:47071 -d iot_platform_mongo -u iotMongoUserX -p .123aB.iotMongoUserXpWD /usr/local2/mongobake/iot_platform_mongo/ --authenticationDatabase admin
# --------------------------------------
# 备份导出示例1
# - h 指定主机端口,不指定默认,默认:27017
# -d 指定备份哪一个数据库,不指定备份所有数据库
# -u 指定账号
# -p 指定密码
# -o 指定导出位置
mongodump -h localhost:47071 -d iot_platform_mongo -u iotMongoUserX -p .123aB.iotMongoUserXpWD --authenticationDatabase admin -o /usr/local2/mongobake/baketest/
8 MongoDB 连接数
业界流传千万级别一下系统用mysql, 千万级别以上用MongoDB, 亿级别用hadoop。我们对其连接数看一下:
数据库名称 | 默认连接数 | 推荐连接数 | 查询命令 | 查询结果 | 参考 |
---|---|---|---|---|---|
MongoDB | 5万 | 5万 | 查询命令:db.serverStatus().connections; | { “current” : 86, “available” : 51114, “totalCreated” : 253, “active” : 1 } | MongoDB查看当前连接数、 |
MySQL | 150 | 1000, 最大可连接数为16384 | show variables like '%max_connection%'; | ‘max_connections’, ‘151’ | Mysql查看连接数(连接总数、活跃数、最大并发数)、MySQL max_connections 总是 214 。不能设大了? max_connections = 214 |
9 定期清理日志数据
MongoDB数据定期清理,和删除指定月份数据。
MongoDB可以直接执行js文件,使用js很方便。
#!/bin/sh
#clear-mongo.sh
# 删除MongoDB日志表
# 参考:MongoDB 定时删除数据 https://blog.csdn.net/CL_YD/article/details/79461528
# 参考:shell脚本传递参数给mongo命令 https://blog.csdn.net/fangfu123/article/details/104748183
# 里面课加载js,什么东西都可以写了。非常棒
mongo 10.105.0.6:47071<<EOF
use admin;
db.auth("iotMongoUserX",".123aB.iotMongoUserXpWD");
show dbs;
db.getCollection('RunLog-mw-airconditioning-hailin').drop();
db.getCollection('RunLog-mw-automobile-baidu').drop();
db.getCollection('RunLog-mw-bus-wanfeng').drop();
db.getCollection('RunLog-mw-carpark-jieshun').drop();
db.getCollection('RunLog-mw-doorcontrol-jieshun').drop();
db.getCollection('RunLog-mw-elevator-taichuan').drop();
db.getCollection('RunLog-mw-face-wanfeng').drop();
db.getCollection('RunLog-mw-informationpublish-chinashine').drop();
db.getCollection('RunLog-mw-internetbehavior-sangfor').drop();
db.getCollection('RunLog-mw-light-kuaisicong').drop();
db.getCollection('RunLog-mw-soilirrigation-splant').drop();
db.getCollection('RunLog-mw-video-haikang').drop();
db.getCollection('RunLog-mw-visualintercom-taichuan').drop();
db.getCollection('RunLog-mw-wifi-huawei').drop();
db.getCollection('Services-mw-bus-wanfeng-feederBusBooking').find({"createTime": {lt:'2020-08-12 11:35:35'}}).forEach(function(item){db.getCollection('Services-mw-bus-wanfeng-feederBusBooking').remove({"_id":item._id})});
load("clear-mongo.js")
exit;
EOF
#下面操作动态变量的MongoDB命令
# echo `date -d "-1 days ago" '+%Y-%m-%d %H:%M:%S'` #获取一天前的时间
#MIN_DATE=`date -d "-10 days ago" '+%Y-%m-%d %H:%M:%S'`; #'2020-08-12 11:35:35';定义变量=和取值之间不能有空格;
//clear-mongo.js
//mongo 使用js写法清理或者删除部分数据
var mongo=new Mongo("10.105.0.6:47071");
var db=mongo.getDB("iot_gateway_mongo");
// 演示打印
printjson('---------开始打印-------');
printjson("数据条数:" + db.getCollection('Services-mw-carpark-jieshun-carout').count());
Date.prototype.Format = function (fmt) { // author: meizz
var o = {
"M+": this.getMonth() + 1, // 月份
"d+": this.getDate(), // 日
"h+": this.getHours(), // 小时
"m+": this.getMinutes(), // 分
"s+": this.getSeconds(), // 秒
"q+": Math.floor((this.getMonth() + 3) / 3), // 季度
"S": this.getMilliseconds() // 毫秒
};
if (/(y+)/.test(fmt))
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
};
Date.prototype.addDays = function(number) {
var adjustDate = new Date(this.getTime() + 24 * 60 * 60 * 1000 * number);
return adjustDate;
};
//获取30天前的时间 2020-08-12 11:35:35
var today30Ago = new Date().addDays(10).Format("yyyy-MM-dd hh:mm:ss");
printjson("当前时间: " + today30Ago);
printjson("清理01-");
db.getCollection('Services-mw-bus-wanfeng-feederBusBooking').find({"createTime": {$lt: ''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-bus-wanfeng-feederBusBooking').remove({"_id":item._id})});
printjson("清理02-");
db.getCollection('Services-mw-bus-wanfeng-feederBusTicket').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-bus-wanfeng-feederBusTicket').remove({"_id":item._id})});
printjson("清理03-");
db.getCollection('Services-mw-carpark-jieshun-carin').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-carpark-jieshun-carin').remove({"_id":item._id})});
printjson("清理04-");
db.getCollection('Services-mw-doorcontrol-jieshun-doorrecord').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-doorcontrol-jieshun-doorrecord').remove({"_id":item._id})});
printjson("清理05-");
db.getCollection('Services-mw-face-wanfeng-getReturnNumber').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-face-wanfeng-getReturnNumber').remove({"_id":item._id})});
printjson("清理06-");
db.getCollection('Services-mw-face-wanfeng-mqCousumer').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-face-wanfeng-mqCousumer').remove({"_id":item._id})});
printjson("清理07-");
db.getCollection('Services-mw-face-wanfeng-returnNumber').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-face-wanfeng-returnNumber').remove({"_id":item._id})});
printjson("清理08-");
db.getCollection('Services-mw-internetbehavior-sangfor-appRank').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-internetbehavior-sangfor-appRank').remove({"_id":item._id})});
printjson("清理09-");
db.getCollection('Services-mw-internetbehavior-sangfor-userRank').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-internetbehavior-sangfor-userRank').remove({"_id":item._id})});
printjson("清理10-");
db.getCollection('Services-mw-soilirrigation-splant-returnNumber').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-soilirrigation-splant-returnNumber').remove({"_id":item._id})});
printjson("清理11-");
db.getCollection('Services-mw-video-haikang-videoCatchcar').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-video-haikang-videoCatchcar').remove({"_id":item._id})});
printjson("清理12-");
db.getCollection('Services-mw-video-haikang-videoCatchface').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-video-haikang-videoCatchface').remove({"_id":item._id})});
printjson("清理13-");
db.getCollection('Services-mw-visualintercom-taichuan-returnNumber').find({"createTime": {$lt:''+ today30Ago +''}}).forEach(function(item){db.getCollection('Services-mw-visualintercom-taichuan-returnNumber').remove({"_id":item._id})});
10 定期拆分和清理日志
MongoDB日志默认在 /var/log/mongodb, 但是一直是追加,经常非常大,甚至上G,我们对日志进行分割 。
#!/bin/bash
#mongo-logRotate.sh
#Rotate the MongoDB logs to prevent a single logfile from consuming too much disk space.
#拆分MongoDB日志,避免日志文件过大
mongo 10.105.0.6:47071<<EOF
use admin;
db.auth("iotMongoUserX",".123aB.iotMongoUserXpWD");
db.runCommand({logRotate:1});
show dbs;
exit;
EOF
编辑定时cron: crontab -e
,填入内容:0 5 * * * /usr/local/sh/mongo-logRotate.sh > /var/null #MongoDB logRotate定期拆分
,保存。
11 mongo定时任务列举
# crontab -e 编辑MongoDB的相关定时任务
0 4 * * * /usr/local/sh/mongod_bak.sh > /var/null #mongo的的备份
0 5 * * * /usr/local/sh/mongo-logRotate.sh > /var/null #MongoDB日志拆分
*/1 * * * * /usr/local/sh/mongo-auto-restart.sh > /var/null #mongodb 自动重启
12 mongo 原始方式启动
参考 - 原始方式安装:MongoDB的启动与停止
参考-启动报错 搭建Mongodb过程中报错:child process failed, exited with error number 48、1、100
#后台启动
mongod --fork
# 启动命令
mongod -f /etc/mongod.conf
# 查看服务
ps aux|grep mongo
# MongoDB禁止使用kill -9关闭
二、Mongodb 使用dokcer 安装
2.1 docker 安装单机版
dockerhub里面的MongoDB安装资料,特别详细,直接看docker hub 官网资料。
2.1.1 Start a mongo server instance
# 这里安装一个MongoDB,指定名称叫做mongo;映射外部端口:27017(前外网);指定账号、密码、指定数据库目录;
docker run -d --name mongo-some \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=mongoadmin \
-e MONGO_INITDB_ROOT_PASSWORD=secret9527 \
-v /usr/local/mongodb/datadir:/data/db \
mongo
2.1.2 Creating database dumps
# docker中对MongoDB进行定期导出,导出成归档模式,然后进行gzip压缩
# 指定导出数据库、指定账号密码、指定压缩方式
# 直接把容器内文件,导出到容器外的主机上面。
docker exec mongo-some sh -c 'exec mongodump -d admin -u mongoadmin -p secret9527 --archive --gzip ' > /usr/local/mongodb/mongobake/all-collections.archive.gzip
#恢复,只能把文件copy到容器内部,在容器内部执行了
mongorestore -h 127.0.0.1:27017 --gzip --archive=/***/***/Desktop/test.archive
三、Mongodb 查询
MongoDB项目中使用的时候,常见操作有增删改查、分页、分组、聚合等。
3.1 简单列表查询find、count
# 最简单查询示例:
mongoTemplate.find(mongoQuery, xxxxxInstanceVo.class);//查询列表
mongoTemplate.count(mongoQuery, xxxxxInstanceVo.class);//查询总条数
mongoTemplate.insert(iotEquitpmentInstance);//插入数据,默认是实体名称
mongoTemplate.insert(o, "Services-collectionName-type");//插入数据&指定MongoDB的document名称; 新增是insert
mongoTemplate.save(dests);//save是update
//更新,也可以是:
Update update = Update.update("status", status);
mongoTemplate.updateMulti(query, update, IotBusLinkageTriggerVo.class);
3.2 Criteria 使用
Criteria.where("crossTime").gte(beginDate).lte(endDate);
Criteria.where("personName").is("张三")
.and("sex").is(o.get("female"))
.and("age").in(1,2,3)
.and("birthday").gte("2000-01-01 00:00:00").lte("2020-12-31 23:59:59")
.and("hometown1").regex(Pattern.compile("^.*" + "陕西省" , Pattern.CASE_INSENSITIVE))
.and("hometown2").regex(Pattern.compile("陕西省" + ".*$", Pattern.CASE_INSENSITIVE))
.and("hometown3").regex(Pattern.compile("^.*" + "陕西省" + ".*$", Pattern.CASE_INSENSITIVE))
;
3.3 分组 group 使用
MongoDB的聚合操作,可以同时对多个字段进行聚合
public List<IotEquitpmentInstanceVo> groupByFloorId(Map<String, Object> params, List<String> fieldsObject, String... groupByName) {
//1: 第一步分组(多个字段分组)获取到所有的分组的设备的集合
Criteria criteria = MongoQuery.toCriteria(params);
StringBuilder reduceFunctionStr = new StringBuilder("id:doc._id,name:doc.name,");
for (int i = 0; fieldsObject != null && i < fieldsObject.size(); ++i) {
reduceFunctionStr.append(fieldsObject.get(i)).append(":").append("doc.").append(fieldsObject.get(i));
if (i != fieldsObject.size() - 1) {
reduceFunctionStr.append(",");
}
}
# 分组,可以支持多个字段
GroupBy groupBy = GroupBy.key(groupByName).initialDocument("{ collectionFirst: [], total: 0 }").reduceFunction("function (doc,prev){prev.total+=1, prev.total <= 1 ? prev.collectionFirst.push({" + reduceFunctionStr.toString() +"}) : '';}");
String documentName = StringUtils.uncapitalize(IotEquitpmentInstanceVo.class.getSimpleName());
GroupByResults<IotEiGroupByFloorIdANdSiIdVo> groupByResults = mongoTemplate.group(criteria, documentName, groupBy, IotEiGroupByFloorIdANdSiIdVo.class);
//2: 把每组的第一个设备封装到集合里面
List<IotEquitpmentInstanceVo> instanceVos = new ArrayList<>();
for (IotEiGroupByFloorIdANdSiIdVo groupByResult : groupByResults) {
instanceVos.addAll(groupByResult.getCollectionFirst());
}
return instanceVos;
}
//IotEiGroupByFloorIdANdSiIdVo.java
@Getter
@Setter
@ApiModel(description = "eiid 设备的聚合查询vo")
public class IotEiGroupByFloorIdANdSiIdVo {
@ApiModelProperty("设备位置-楼层")
private String floorId;
@ApiModelProperty("别称:systemInstanceId = siId, 系统id")
private String sysModelInstancId;
@ApiModelProperty("设备集合的第一个设备")
private List<IotEquitpmentInstanceVo> collectionFirst;
@ApiModelProperty("该分组的中的设备总数")
private int total;
}
3.4 聚合操作Aggregation.newAggregation
mongo里面可以group之后进行sum、avg这种
参考:mongoTemplate.aggregate()聚合查询
public List<StatisticalEntity> statistics(String beginDate, String endDate, int interceptions, Long tenantId) {
//1: 查询条件
Criteria criteria = Criteria.where("tenantId").is(tenantId + "");
Aggregation aggregation5 =
Aggregation.newAggregation(
//1: match是查询条件
//2: project是增加或许删除参数
//3: group、count、as:分组 求和 起别名
Aggregation.match(Criteria.where("crossTime").gte(beginDate).lte(endDate)),
Aggregation.match(criteria),
Aggregation.project()
.and("crossTime").substring(0, interceptions).as("day"),
Aggregation.group("day")
.count().as("num"),
Aggregation.sort(new Sort(new Sort.Order(Sort.Direction.ASC, "_id"))));
AggregationResults<StatisticalEntity> outputTypeCount5 =
mongoTemplate.aggregate(aggregation5, "Services-mw-doorcontrol-jieshun-doorrecord", StatisticalEntity.class);
return outputTypeCount5.getMappedResults();
}