- 由于某个接口是在太慢了。。。尝试优化(/秃头)
- 使用的spring-data-mongo:2.1.3
- mongo驱动: 3.8.2
- 可以看看我最先的查询语句。
优化思路
分页
- 最原先的分页查询
- 由于数据量有点大,导致这个查询很慢,后来发现根本没有必要使用聚合。使用聚合查询,即使有索引,也要遍历大量无用的数据。并且in查询本身效率也不是很高
- 于是我将这个聚合修改为多次查询,使用findFirst()查询到第一条就直接返回。由于有连接池的存在,查询效率提升了不少。
查询本体
-
首先优化了match。参考博客
- $where和$exists:这两个操作符,完全不能使用索引。
- $ne和$not:通常来说取反和不等于,可以使用索引,但是效率极低,不是很有效,往往也会退化成扫描全表。
- $nin:不包含,这个操作符也总是会全表扫描
- 对于管道中的索引,也很容易出现意外,只有在管道最开始时的match sort可以使用到索引,一旦发生过project投射,group分组,lookup表关联,unwind打散等操作后,就完全无法使用索引。
-
于是先小优化吧数据库中所有的空filed变为""
- db.workflow.update({"parentWorkflowId":{$exists:false}},{$set: {"parentWorkflowId": ""}},{"multi" : true, "upsert" : false})
- 将原先的查询中$or去除,仅剩下parentWorkflowId:"",
-
调整group中的排序
- group中的cond排序修改,将数据量最多的,最容易匹配的放在前方。
-
$in这里暂不做调整 (暂定)
- 由于in中的数据不确定。目前还不确定修改为多次查询是否有效。$in其实就是$or。
- 加上有根据时间排序的需求。如果分成多次可能会导致数据问题。
新建索引
-
语法
- db.collection.createIndex({"xxx":1,"yyy":-1});
-
索引选择
- 这里我先是建立了多个性能可能高的索引
- 使用db.collection.explain().aggregate()。来选择索引。
- explain()中的winningPlan可以看出较优的索引
- 修改后重新查询
稳定性问题
- 在修改后我发现查询稳定性很差。差的快和差的慢会差2-5s的时间。
- 排除连接池的问题:db.serverStatus().connections(admin用户),并且查看日志没有发现新建和断开连接的纪律
使用Profiling排查问题
-
使用admin用户启动profile,设置超过500ms的都是慢操作。进行记录。
- db.setProfilingLevel(1,500)。
-
执行多次后查看数据:
- 同一个查询居然每次调用的索引都一样。。(看完整个人都懵逼了)
- 修改代码,通过hint指定索引。(改的简直要吐血)
Document projectSet = Document.parse("{projectId:1, process:1, startTime:1, parentWorkflowId:1}");
projectSet.put("id", "$_id");
Document matchSet = Document.parse("{parentWorkflowId: '',projectId:{" + "$in: " + listStringToString(projectString) + "}}");
List aggregateList = new ArrayList<>();
aggregateList.add(new Document("$project", projectSet));
aggregateList.add(new Document("$match", matchSet));
aggregateList.add(new Document("$group", group()));
aggregateList.add(new Document("$sort", Document.parse("{maxStartTime:-1}")));
aggregateList.add(new Document("$skip", ((page - 1) * limit)));
aggregateList.add(new Document("$limit", limit));
AggregateIterable
最先的查询
最先的查询分页
[{
"$project": {
"projectId": NumberInt("1"),
"process": NumberInt("1"),
"startTime": NumberInt("1"),
"parentWorkflowId": NumberInt("1"),
"id": "$_id"
}
},
{
"$match": {
"$or": [
{
"parentWorkflowId": {
"$exists": false
}
},
{
"parentWorkflowId": ""
}
],
"projectId": {
"$in": [
"ba8ec7bb4dc19a501f296186706c1e31",
"597a937634f8716cad98c66dbb376792",
"18294187351fc4310470f531ca57450f",
"847d91f66357e3c03dd1608b5ba49532",
"6b4ccbffcb258489789c8b2a4520166e",
"89ba378a355479f0d57fbc4ed7c8f22e",
"0f76097d86bf19458ed5c7147bdd55c8",
"9b2c5fd3a1a76f350fbb642c5b3dee56",
"cfd10e186b177d7d165c3f4f8bc68938",
"8fb8f7aeddd89230f5e62713bf1845a0"
]
}
}
},
{
"$group": {
"_id": "$projectId",
"count":{$sum:1}
}
}
最先的查询本体
[{
"$project": {
"projectId": NumberInt("1"),
"process": NumberInt("1"),
"startTime": NumberInt("1"),
"parentWorkflowId": NumberInt("1"),
"id": "$_id"
}
},
{
"$match": {
"$or": [
{
"parentWorkflowId": {
"$exists": false
}
},
{
"parentWorkflowId": ""
}
]
"projectId": {
"$in": [
"ba8ec7bb4dc19a501f296186706c1e31",
"597a937634f8716cad98c66dbb376792",
"18294187351fc4310470f531ca57450f",
"847d91f66357e3c03dd1608b5ba49532",
"6b4ccbffcb258489789c8b2a4520166e",
"89ba378a355479f0d57fbc4ed7c8f22e",
"0f76097d86bf19458ed5c7147bdd55c8",
"9b2c5fd3a1a76f350fbb642c5b3dee56",
"cfd10e186b177d7d165c3f4f8bc68938",
"8fb8f7aeddd89230f5e62713bf1845a0"
]
}
}
},
{
"$group": {
"_id": "$projectId",
"endNum": {
"$sum": {
"$cond": {
"if": {
"$eq": [
"$process",
NumberInt("5")
]
},
"then": NumberInt("1"),
"else": NumberInt("0")
}
}
},
"stopNum": {
"$sum": {
"$cond": {
"if": {
"$eq": [
"$process",
NumberInt("4")
]
},
"then": NumberInt("1"),
"else": NumberInt("0")
}
}
},
"runningNum": {
"$sum": {
"$cond": {
"if": {
"$eq": [
"$process",
NumberInt("1")
]
},
"then": NumberInt("1"),
"else": NumberInt("0")
}
}
},
"others": {
"$sum": {
"$cond": {
"if": {
"$in": [
"$process",
[
NumberInt("6"),
NumberInt("3"),
NumberInt("2"),
NumberInt("7"),
NumberInt("8")
]
]
},
"then": NumberInt("1"),
"else": NumberInt("0")
}
}
},
"maxStartTime": {
"$max": "$startTime"
},
"allProcess": {
"$sum": NumberInt("1")
}
}
},
{
"$sort": {
"maxStartTime": NumberInt("-1")
}
},
{
"$skip": NumberLong("0")
},
{
"$limit": NumberLong("10")
}]