言成言成啊 | Kit Chen's Blog

MongoDB

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

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

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

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

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

实际使用参考的所有文档

  1. 菜鸟教程
  2. 官方中文文档

命令行获取帮助

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

一、安装

官网下载安装以Windows为例。

详细安装教程

二、基本操作

2.1 图形界面工具

mongoDbCompress

robot3T

2.2 命令行

连接

以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
show collections;

查看当前所使用的数据库

1
db;

普通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<number<20
db.collection.find({
"number": {
$gt: "10",
$lt: "20"
}
});
// 表示查询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
修改:2021-06-02 20:59:19
链接:https://meethigher.top/blog/2021/mongodb/
标签:java 
付款码 打赏 分享