言成言成啊 | Kit Chen's Blog

MongoDB

发布于2021-05-17 20:24:51,更新于2023-02-21 14:31:30,标签:java sql  文章会持续修订,转载请注明来源地址:https://meethigher.top/blog

公司远程服务器版本4.2.14,本地版本4.4.6

目前主要使用MongoDb做一些非实时的大量的数据的统计功能。集中在接口日志-接口API通用日志管理统计功能

MongoDb是一个非关系型数据库,主要用来存储非结构化的数据。

Mongo将数据存储成一个文档,数据结构由key、value组成,类似于json,value值可以是一个对象(基本类型、List等)

MongoDb内部是基于V8引擎(2.4版本以后),通过JavaScript代码单线程执行操作。

遇事不决,就看官网,看不明白,不怪官网,自己太菜!

实际使用参考的所有文档

  1. 菜鸟教程
  2. 官方中文文档
  3. MongoDB设置登录账号和密码_西瓜游侠的博客-CSDN博客_mongodb设置用户名和密码
  4. Mongo 创建数据库_太阳上的雨天的博客-CSDN博客_mongo 创建数据库
  5. MongoDB设置登录账号和密码_西瓜游侠的博客-CSDN博客_mongodb设置用户名和密码
  6. mongodb开启权限验证、超级管理员、用户权限管理 - 古墩古墩 - 博客园
  7. User Management Methods — MongoDB Manual

命令行获取帮助

  1. 命令db.help获取db下的所有方法
  2. db.表名.find().help()获取find下的所有方法

一、安装

1.1 windows

官网下载安装以Windows为例。

详细安装教程

1.2 linux

官网选择4.4.11,centos7.0,server,下载rpm包。

1
wget https://repo.mongodb.org/yum/redhat/7/mongodb-org/4.4/x86_64/RPMS/mongodb-org-server-4.4.11-1.el7.x86_64.rpm

进入服务器,关闭防火墙。

1
2
systemctl status firewalld
systemctl stop firewalld

安装mongo

1
rpm -ivh mongodb-org-server-4.4.11-1.el7.x86_64.rpm

提示创建关联mongo.service,表示成功。

修改mongo的配置文件,默认是/etc/mongod.conf,绑定ip127.0.0.1改为0.0.0.0

1
2
3
4
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.

启动mongo,并查看结果

1
2
3
sudo systemctl start mongod
sudo systemctl status mongod
sudo systemctl enable mongod #设置自启动

或者用命令

1
ps -ef|grep mongo

二、基本操作

2.1 图形界面工具

mongoDbCompress

robot3T

2.2 命令行

shell安装

shell有两种,一种是mongo shell,一种是mongosh shell。就官网描述。后者比前者更强大。不过正常使用还是推荐mongo shell,因为轻量。

mongosh shell

如果是windows,在安装server时,就已经带了命令行操作的工具。

但是上面Linux安装时,只是单纯安装了一个server而已,mongoshell还需要单独安装。

Install mongosh — MongoDB Shell

以Centos7为例,创建一个仓库,然后配置地址,以便于方便直接通过yum下载

1
2
3
4
5
6
7
8
9
vim /etc/yum.repos.d/mongodb-org-5.0.repo

# 添加下面内容
[mongodb-org-5.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/5.0/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-5.0.asc

执行命令进行安装

1
sudo yum install -y mongodb-mongosh

进行连接

1
mongosh "mongodb://localhost:27017"

mongo shell

1
2
3
wget https://repo.mongodb.org/yum/redhat/7/mongodb-org/4.4/x86_64/RPMS/mongodb-org-shell-4.4.11-1.el7.x86_64.rpm

rpm -ivh mongodb-org-shell-4.4.11-1.el7.x86_64.rpm

进行连接

1
mongo

连接

以windows为例,执行mongo.exe,或者cmd命令行执行

cmd执行的话,如果没有配置环境变量,需要去mongo的有mongo.exe的bin目录下执行。

连接到mongo数据库

1
2
3
4
mongo 127.0.0.1:27017/comm -u jack -p 123456
# mongo ip:port/库名 -u 用户名 -p 密码
# 如果没有库名、用户密码,可以不写,如下
mongo 127.0.0.1:27017

获取当前的连接

1
db.getMongo();

获取当前版本

1
db.getVersion();

查看下面的所有数据库

1
show databases;

查看下面的所有的集合

1
2
3
show collections;
# 或者
show tables;

查看当前所使用的数据库

1
db;

鉴权

开启鉴权,只需要在mongo配置文件/etc/mongod.conf中追加

1
2
3
# 开启鉴权
security:
authorization: enabled

在开启鉴权之前,首先,要在未开启状态下,创建一个最高级用户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 进入mongo
mongo 127.0.0.1:27017

# 在admin表里创建最高级用户
# 创建一个用户,管理admin库时root权限,在admin下创建的管理其他库时也有root权限
use admin
db.createUser({
user: 'admin',
pwd: 'admin',
roles: [{
role: 'root',
db: 'admin'
}]
})

# 创建完成之后退出
exit

修改mongo配置文件,开启鉴权。上面有记录该操作。

然后重启mongo

1
systemctl restart mongod

以admin用户登录,创建一个test库,并在test库里创建三个用户,只能登录、只能登录+查询、只能登录+查询+修改。

以上三个用户,如果适用于所有表,就使用use admin,在admin表里操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 登录
mongo 127.0.0.1:27017 -u admin -p admin
# 创建一个测试库test,并在测试库test的aaa表里插入一条数据
use test
db.aaa.insert({_id:1})
# 创建只能登录用户
db.createUser({
user: 'loginOnly',
pwd: 'loginOnly',
roles: []
})
# 创建登录+读取用户
db.createUser({
user: 'readOnly',
pwd: 'readOnly',
roles: [{
role: 'read',
db: 'test'
}]
})
# 创建登录+读写用户
db.createUser({
user: 'readWriteOnly',
pwd: 'readWriteOnly',
roles: [{
role: 'readWrite',
db: 'test'
}]
})

如果想要给一个用户,多个表权限,只需要在roles里面,再添加一个对象即可

其他一些操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看当前库下所有用户
show users
# 删除用户
db.dropUser('testadmin')
# 修改用户,后面对象不仅仅可传pwd,其他也可。看自己需求
db.updateUser('admin', {pwd: '654321'})
db.updateUser('admin',{
pwd: 'admin',
roles: [{
role: 'root',
db: 'admin'
}]
})
# 切换到admin用户
db.auth('admin', 'admin')

普通CRUD

更详细的参考

查询所有

1
2
3
4
5
6
// 法一
// 语法:db.表名.find();
db.interfaceCountEveryday.find();
// 法二
// 语法:db.getCollection("表名").find();
db.getCollection("hotProject").find();

以下所有例子都使用法一

插入数据

1
2
// 语法:db.表名.insert(json字符串)
db.ccc.insert({"name":"ccc","_id":"ccc"})

删除表

1
2
// 语法:db.表名.drop();
db.ccc.drop();

删除某条数据

1
2
// 语法:db.表名.remove(json字符串)
db.ccc.remove({"_id":"ccc"});

更新数据

1
2
3
// 法一
db.col.update({'title':'MongoDB'},{$set:{'title':'Mongo'}})
db.col.update({'title':'MongoDB','author':'ccc'},{$set:{'title':'MongoDB'},'author':'sldt'},{multi:true})

根据条件查询

1
2
// 语法:
db.interfaceCountEveryday.find({"_id":"2021-05-09"});

常用的查询关键字

  1. $gt:greater than 大于
  2. $lt:less than 小于
  3. $gte:greater than and equals 大于等于
  4. $lte:less than and equals 小于等于
  5. $ne:no equals 不等于
  6. $in
  7. $nin
  8. $all:$all与$in的区别是,all必须匹配到里面所有,in匹配其中之一即可
  9. $exists:判断元素是否存在
1
2
3
4
5
6
7
8
9
10
11
12
13
// 多条件联合查询
db.track.find({
"bindingId": "渝A18287D",
"saveTime": {
$gt: 1676246400000,
$lt: 1676253600000
},
"spd": {
$gt: 120
}
});
// 表示查询number为2或4或6的所有数据
db.collection.find({"number":{$in: [2,4,6]}});

复杂聚合查询

查询总条数

1
db.ccc.find().count()

查询取第6-10条

1
2
db.ccc.find().skip(5).limit(5);
db.ccc.find().skip(5).limit(5).count(true);//加true表示统计skip、limit里面的内容

查询排序

1
db.ccc.find().sort({"createTimie":-1})// -1表示从大到小降序,1表示从小到大升序

mapReduce

mapreduce分为两个阶段

  1. map:Map阶段并行处理输入数据
  2. reduce:Reduce阶段对Map结果进行汇总

例子:从interfaceLogMongo表中查询出interfaceCallType为2的数据,以interfaceCode分组统计出调用的总次数以及它的类型,并存入test数据表中。

mapreduce两种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//法一
db.interfaceLogMongo.mapReduce(
function() {
emit(this.interfaceCode, {count:1,type:this.interfaceType});
},
function(key, values) {
//统计的时候,如果想要统计出正确的次数,必须要用for循环
//使用total+=values[0].count()不会统计
var total=0;
for(var i in values){
total+=values[i].count;
}
return {count:total,type:values[1].type}
},
{
query: {
interfaceCallType: 2
},
out: "test" //删表重存
}
)
//法二
db.runCommand({
"mapreduce": "interfaceLogMongo",
"map": function () {
emit(this.interfaceCode, {count: 1, type: this.interfaceType});
},
"reduce": function (id, emits) {
var total = 0;
for(var i in emits)
total+=emits[i].count;
var type = emits[1].type;
return {count: total, type: emits[1].type};
},
"query": {
interfaceCallType: 2
},
"out": {merge: "test"}//merge表示追加
})

注意:由于mongo是基于js,单线程,单个节点跑mapreduce效率反而不如aggregate(agg framework使用c++)来的快。所以后续所有使用mapreduce的都换成了aggregate

mongo中MR与agg的比较,以及如何优化MR

aggregate

例子:从interfaceLogMongo中查出interfaceCallType为2的数据,以interfaceCode、interfaceName、interfaceType为主键分组,取他们的最大的interfaceResponseTime,取前5条并按倒序(由大到小)排序

aggregation中参数介绍

  • $match:相当于SQL中的where
  • $group:相当于SQL中的group by
  • $project:相当于SQL中的select
  • $sort:相当于SQL中的order by
  • $limit:相当于SQL中的limit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
db.interfaceLogMongo.aggregate([
{
$match: {
"interfaceCallType": 2
}
},
{
$group: {
_id: {
"code": "$interfaceCode",
"name": "$interfaceName",
"type": "$interfaceType"
},
max: {$max: "$interfaceResponseTime"}
}
}, {
$sort: {
max: -1
}
}, {
$limit: 5
}
]);

索引CRD

以interfaceLogMongo这张表为例

索引值,1为按升序创建索引,-1为按降序创建索引

查询表的索引

1
db.interfaceLogMongo.getIndexes();

创建单字段索引

1
2
// 按照interfaceResponseTime的降序创建索引
db.interfaceLogMongo.createIndex({"interfaceResponseTime":-1})

创建复合索引

1
db.interfaceLogMongo.createIndex({"interfaceResponseTime":-1,"interfaceCallType":1})

数据量多大,创建索引也会比较耗时,可以让索引在后台执行

1
db.interfaceLogMongo.createIndex({"interfaceResponseTime":-1,"interfaceCallType":1},{background:true})

删除索引

1
2
db.interfaceLogMongo.dropIndex("索引名称");// 删除单个
db.interfaceLogMongo.dropIndexes(); //删除所有索引

索引优化

mongodb性能优化

查看执行过程,在语句上添加explain()即可。

1
db.interfaceLogMongo.explain().find().limit(5).sort({"interfaceResponseTime":-1});

stage类型

  • COLLSCAN:全表扫描
  • IXSCAN:索引扫描
  • FETCH:根据索引去检索指定document
  • SHARD_MERGE:将各个分片返回数据进行merge
  • SORT:表明在内存中进行了排序
  • LIMIT:使用limit限制返回数
  • SKIP:使用skip进行跳过
  • IDHACK:针对_id进行查询
  • SHARDING_FILTER:通过mongos对分片数据进行查询
  • COUNT:利用db.coll.explain().count()之类进行count运算
  • COUNTSCAN:count不使用Index进行count时的stage返回
  • COUNT_SCAN:count使用了Index进行count时的stage返回
  • SUBPLA:未使用到索引的$or查询的stage返回
  • TEXT:使用全文索引进行查询时候的stage返回
  • PROJECTION:限定返回字段时候stage的返回

使用索引前

耗时

使用索引后

耗时

注意:并非所有的都可以使用索引来优化。

特别复杂的聚合查询,使用单字段索引会变慢,使用聚合索引效果也不明显,最后是通过分表的方式来处理的。

三、MongoTemplate

3.1 普通CRUD

mongoTemplate中提供的常用方法

  1. find
  2. findOne
  3. findAll
  4. findDistinct:如果是多个字段distinct的话,还是使用aggregate比较好用。
  5. insert:数据重复就会抛异常
  6. save:数据重复则覆盖旧的数据
  7. upsert:与save类似,有重复数据就会进行修改
  8. remove

3.2 mapReduce

语法:mongoTemplate.mapReduce(Query对象,表名或者实体类的字节码,map的js语句,reduce的js语法,返回实体的字节码)

以下面这个为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
db.interfaceLogMongo.mapReduce(
function() {
emit(this.interfaceCode, {count:1,type:this.interfaceType});
},
function(key, values) {
//统计的时候,如果想要统计出正确的次数,必须要用for循环
//使用total+=values[0].count()不会统计
var total=0;
for(var i in values){
total+=values[i].count;
}
return {count:total,type:values[1].type}
},
{
query: {
interfaceCallType: 2
},
out: "test" //删表重存
}
)

java代码

1
2
3
4
5
6
7
8
9
10
//法一
mongoTemplate.mapReduce(Query查询对象, 表名或者实体类的字节码,
"function () {emit(this.interfaceCode, {count:1,type:this.interfaceType});}",
"function (id, emits) {var total=0;for(var i in values){total+=values[i].count;}return {count:total,type:values[1].type}}",
返回实体的字节码);
//法二
mongoTemplate.mapReduce(Query查询对象, 表名或者实体类的字节码,
"classpath:map.js",
"classpath:reduce.js",
返回实体的字节码);

3.3 aggregate

语法:mongoTemplate.aggregate(aggregation对象,表的名称或者表的字节码,返回对象的字节码)

获取aggregation对象:Aggregation.newAggregation(一串可变数组,可以是match、project、group、sort、limit等等)

以下面这个为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
db.interfaceLogMongo.aggregate([
{
$group: {
_id: {
"code":"$interfaceCode",
"type":"$interfaceType",
"name":"$interfaceName"
},
count: {
$sum: 1
}
}
}, {
$sort: {
count: -1// 1表示由小到大,-1表示由大到小
}
}
]);

java代码

1
2
3
4
5
6
Aggregation agg = Aggregation.newAggregation(
Aggregation.project("interfaceCode","interfaceType","interfaceName"),
Aggregation.group("interfaceCode","interfaceType","interfaceName").count().as("count"),
Aggregation.project("count"),
Aggregation.sort(Sort.by("count").descending()));
mongoTemplate.aggregate(agg,表的名称或者表的字节码,返回对象的字节码)
发布:2021-05-17 20:24:51
修改:2023-02-21 14:31:30
链接:https://meethigher.top/blog/2021/mongodb/
标签:java sql 
付款码 打赏 分享
Shift+Ctrl+1 可控制工具栏