关于MongoDB谨防索引seek的效率问题详析

发布时间:2019-11-19 16:38 来源:互联网 当前栏目:网站服务器

背景

最近线上的一个工单分析服务一直不大稳定,监控平台时不时发出数据库操作超时的告警。

运维兄弟沟通后,发现在每天凌晨1点都会出现若干次的业务操作失败,而数据库监控上并没有发现明显的异常。

在该分析服务的日志中发现了某个数据库操作产生了 SocketTimeoutException。

开发同学一开始希望通过调整 MongoDB Java Driver 的超时参数来规避这个问题。
但经过详细分析之后,这样是无法根治问题的,而且超时配置应该如何调整也难以评估。

下面是关于对这个问题的分析、调优的过程。

初步分析

从出错的信息上看,是数据库的操作响应超时了,此时客户端配置的 SocketReadTimeout 为 60s。
那么,是什么操作会导致数据库 60s 还没能返回呢?

业务操作

左边的数据库是一个工单数据表(t_work_order),其中记录了每张工单的信息,包括工单编号(oid)、最后修改时间(lastModifiedTime)

分析服务是Java实现的一个应用程序,在每天凌晨1:00 会拉取出前一天修改的工单信息(要求按工单号排序)进行处理。

由于工单表非常大(千万级),所以在处理时会采用分页的做法(每次取1000条),使用按工单号翻页的方式:

第一次拉取

db.t_work_order.find({
  "lastModifiedTime":{
   $gt: new Date("2019-04-09T09:44:57.106Z"),
   $lt: new Date("2019-04-09T10:44:57.106Z")}, 
  "oid": {$exists: true}})
  .sort({"oid":1}).limit(1000)

第二次拉取,以第一次拉取的最后一条记录的工单号作为起点

db.t_work_order.find({
  "lastModifiedTime":{
   $gt: new Date("2019-04-09T09:44:57.106Z"),
   $lt: new Date("2019-04-09T10:44:57.106Z")}, 
  "oid": {$exists: true, $gt: "VXZ190"}})
  .sort({"oid":1}).limit(1000)
..

根据这样的查询,开发人员给数据表使用的索引如下:

db.t_work_order.ensureIndexes({
  "oid" : 1,
  "lastModifiedTime" : -1
})

尽管该索引与查询字段基本是匹配的,但在实际执行时却表现出很低的效率:
第一次拉取时间非常的长,经常超过60s导致报错,而后面的拉取时间则会快一些

为了精确的模拟该场景,我们在测试环境中预置了小部分数据,对拉取记录的SQL执行Explain:

db.t_work_order.find({
  "lastModifiedTime":{
   $gt: new Date("2019-04-09T09:44:57.106Z"),
   $lt: new Date("2019-04-09T10:44:57.106Z")}
  "oid": {$exists: true}})
  .sort({"oid":1}).limit(1000)
  .explain("executionStats")

输出结果如下

"nReturned" : 1000,
"executionTimeMillis" : 589,
"totalKeysExamined" : 136661,
"totalDocsExamined" : 1000,

...

"indexBounds" : {
    "oid" : [
        "[MinKey, MaxKey]"
    ],
    "lastModifiedTime" : [
        "(new Date(1554806697106), new Date(1554803097106))"
    ]
},
"keysExamined" : 136661,
"seeks" : 135662,

在执行过程中发现,检索1000条记录,居然需要扫描 13.6 W条索引项!

  • 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、
  • 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、