记一次mongo优化--查询稳定性排查

  • 由于某个接口是在太慢了。。。尝试优化(/秃头)
  • 使用的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 cursors = mongoTemplate
        .getCollection(MongoCollection.WORK_FLOW)
        .aggregate(aggregateList, Map.class)
        //指定索引,索引不存在会报错
        .hint(Document.parse("{ parentWorkflowId: 1, projectId: 1 }"));
//                .allowDiskUse(true); 速度慢一倍;数量没到sort分不出来别开。

最先的查询

最先的查询分页

 [{
    "$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")
}]

你可能感兴趣的:(记一次mongo优化--查询稳定性排查)