1.慢查询问题介绍
慢查询常常是开发者经常会遇到的问题之一,影响数据库的性能及查询效率,对于大型网站的管理者而言,必须成为处理问题的一员。在分布式系统中,MongoDB是最常用的NoSQL数据库中一种。在使用MongoDB的处理过程中,慢查询问题是一个我们要经常面对的问题。本文将介绍一个线上Mongo慢查询问题排查处理过程。
2.排查慢查询问题
2.1 定位问题
MongoDB慢查询是指查询耗费的时间过长,超过了指定的时间阈值。如何精确定位问题的发生原因是至关重要的。从业务层面优化查询逻辑、加索引、数据分库分表、合理数据模型设计都是解决慢查询的手段之一。
2.2.技术分析
在技术层面,Nosql有很多的优化技巧来避免慢查询:使用副本集和分片等方式,使得MongoDb可以支持海量的数据量;优化数据库查询以减少延迟时间,就是在一次查询中,最小化需要扫描的数据量以达到更快的查询。这个过程包含对查询条件和查询语句的优化。对于已经发生的慢查询的问题,我们可以通过如下方式来进行定位(以Mongodb为例):
2.2.1.查看慢查询日志
在Mongodb中,可以设置slowms参数,这个参数是慢查询的阈值,比如设置了slowms=100,则是大于100ms的查询都会被记录到日志中,并标记为慢查询日志,因此我们可以通过查询日志文件来查找慢查询的问题。
db.setProfilingLevel(1,50)
该命令将开启慢查询分析,慢查询的阈值为50毫秒,会将慢查询日志记录在mongod.log中。通过examining mongod.log,我们可以看到如下的慢查询日志:
{
"op" : "query",
"ns" : "local.oplog.rs",
"query" : {
"$query" : {
"ts" : {
"$gte" : Timestamp(1602913824, 1)
}
},
"$readPreference" : {
"mode" : "secondaryPreferred"
}
},
"planSummary" : "EOF",
"keysExamined" : 0,
"docsExamined" : 0,
"cursorExhausted" : true,
"numYield" : 0,
"lockStats" : {
"timeLockedMicros" : {},
"timeAcquiringMicros" : {
"r" : NumberLong(1)
}
},
"responseLength" : 334,
"protocol" : "op_query",
"millis" : 379,
"client" : "10.5.19.18:41552",
"allUsers" : [ ],
"user" : ""
}
通过慢查询日志信息,我们可以看到查询所在的集合、查询方案、查询所花费的时间、筛选的条件、数据扫描数、返回数据大小等详细的信息。
2.2.2 使用MongoDB内建工具
利用MongoDB内建工具,我们可以更加精准的定位查询存在的问题。例子中,我们使用explain()命令,该命令可以显示使用的索引、执行的查询方式等信息。
db.collection.find({“name”:“xxx”}).explain("executionStats")
对于以上例子,将会返回执行结果,它也包含了很多的信息,如索引类型,扫描次数,查询所需的时间等。
2.2.3 使用Diagnostics存储库处理Issue
对于ABB Struxureware Monitoring系统中出现的问题,可以通过diag package来进行分析问题。
db.runCommand({"diagnostics":1})
该命令会返回当前instance的的所有the most expensive operations和consuming operations。由此可以判定,有些操作需要优化,以提升系统的运行效率,从而避免慢查询的问题。
2.3 分析设计
2.3.1.规划集合的使用
MongoDB的设计中,要结合业务数据特点,设置合适的索引,要避免频繁的扫描,同时查询的数据应该尽量与同一分片上的MongoDB节点聚合。对于多应用场景的选择,需要尽量使用更经济的聚合模式。如应用较少的时候使用副本,但是当流量上来的时候,及时切换到分片模式以达到更好的性能和稳定性。
2.3.2.加速查询速度
在分片的场景中,大部分的集合都设计了复合索引,每个集合的复合索引都会包括一个UID (unique ID),以及与应用级别用户界面相匹配的hostname、时间戳和其他类别特定的字段。了解了复合索引的设计,接下来,我们可以在应用级别的查询中有效地使用这些索引。例如,在该实时汽车车队应用程序的查询中,我们将尽量缩小我们的查询,以使用唯一键,过滤器和索引,并使用合适的查询运算符和前缀,来优化查询语句。我们在运行非常超时的查询时,有一个优化技巧。如果查询的过程默认扫描超过1万条数据,可以直接跳过该查询。我们使用limit()和sort()建立索引,即在查询中首先使用排序和限制,以限制只返回30个结果。
2.3.3.分析而非查找
对于大量数据的查询,需要考虑到查询过程中生成的所有新文件。我们使用MongoDB MapReduce作为一种替代选择。相反,MapReduce使用了一种从原始数据生成密钥和值对的矢量函数的方法,对产生的数据文件中的记录进行分类,并提取每个组的统计数据,从而生成新的可查询的集合。这个新的可查询集合我们可以通过在查询或聚合期间进行连接来对计算进行优化。 在使用MapReduce时,最好使用较小的数据集和索引键。
2.3.4.数据不需要常规化
当适用于多个集合时,建议将集合设计冗余以加速查询。从业务的角度来看,每个设备通常只处于一个生产线,只与一个设备进行连接。因此,我们可以将所有设备信息添加到设备集合中,而不是将其存储在各个生产线记录中。
3.结论
我们需要最大化或者优化查询性能,尤其是在遇到较大数据集时,一定更要考虑到查询速度的提高。上述方法可以在提高查询速度的同时,减少扫描次数,从而提高应用程序性能,更快地处理数据请求。