「生产事故」MongoDB复合索引引发的灾难

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前情提要","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"11月末","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"我司商品服务","attrs":{}}],"attrs":{}},{"type":"text","text":"的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MongoDB主库","attrs":{}}],"attrs":{}},{"type":"text","text":"曾出现过严重抖动、频繁锁库等情况。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"由于诸多业务存在插入","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MongoDB","attrs":{}}],"attrs":{}},{"type":"text","text":"、然后立即查询等逻辑,因此项目并未开启读写分离。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"最终定位问题是由于:服务器自身磁盘 + 大量","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"慢查询","attrs":{}}],"attrs":{}},{"type":"text","text":"导致","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"基于上述情况,运维同学后续着重增强了对","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MongoDB慢查询","attrs":{}}],"attrs":{}},{"type":"text","text":"的监控和告警","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"幸运的一点","attrs":{}},{"type":"text","text":":在出事故之前刚好完成了缓存过期时间的升级且过期时间为一个月,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"C端查询","attrs":{}}],"attrs":{}},{"type":"text","text":"都落在缓存上,因此没有造成","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"P0级","attrs":{}}],"attrs":{}},{"type":"text","text":"事故,仅仅阻塞了部分","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"B端逻辑","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"事故回放","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我司的各种监控做的比较到位,当天突然收到了数据库服务器负载较高的告警通知,于是我和同事们就赶紧登录了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Zabbix监控","attrs":{}}],"attrs":{}},{"type":"text","text":",如下图所示,截图的时候是正常状态,当时事故期间忘记留图了,可以想象当时的数据曲线反正是该高的很低,该低的很高就是了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Zabbix 分布式监控系统官网:https://www.zabbix.com/","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e6/e6f2212c69deae3fdfd8353d05689736.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"开始分析","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们研发是没有操控服务器权限的,因此委托运维同学帮助我们抓取了部分查询记录,如下所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"---------------------------------------------------------------------------------------------------------------------------+\nOp | Duration | Query ---------------------------------------------------------------------------------------------------------------------------+\nquery | 5 s | {\"filter\": {\"orgCode\": 350119, \"fixedStatus\": {\"$in\": [1, 2]}}, \"sort\": {\"_id\": -1}, \"find\": \"sku_main\"} \nquery | 5 s | {\"filter\": {\"orgCode\": 350119, \"fixedStatus\": {\"$in\": [1, 2]}}, \"sort\": {\"_id\": -1}, \"find\": \"sku_main\"} query | 4 s | {\"filter\": {\"orgCode\": 346814, \"fixedStatus\": {\"$in\": [1, 2]}}, \"sort\": {\"_id\": -1}, \"find\": \"sku_main\"} query | 4 s | {\"filter\": {\"orgCode\": 346814, \"fixedStatus\": {\"$in\": [1, 2]}}, \"sort\": {\"_id\": -1}, \"find\": \"sku_main\"} query | 4 s | {\"filter\": {\"orgCode\": 346814, \"fixedStatus\": {\"$in\": [1, 2]}}, \"sort\": {\"_id\": -1}, \"find\": \"sku_main\"}\n...","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查询很慢的话所有研发应该第一时间想到的就是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"索引","attrs":{}}],"attrs":{}},{"type":"text","text":"的使用问题,所以立即检查了一遍索引,如下所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"### 当时的索引\n\ndb.sku_main.ensureIndex({\"orgCode\": 1, \"_id\": -1},{background:true});\ndb.sku_main.ensureIndex({\"orgCode\": 1, \"upcCode\": 1},{background:true});\n....","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我屏蔽了干扰项,反正能很明显的看出来,这个查询是完全可以命中索引的,所以就需要直面第一个问题:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"上述查询记录中排首位的慢查询到底是不是出问题的根源?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我的判断是:它应该不是数据库整体缓慢的根源,因为第一它的查询条件足够简单暴力,完全命中索引,在索引之上有一点其他的查询条件而已,第二在查询记录中也存在相同结构不同条件的查询,耗时非常短。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在运维同学继续排查查询日志时,发现了另一个比较惊爆的查询,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"### 当时场景日志\n\nquery: { $query: { shopCategories.0: { $exists: false }, orgCode: 337451, fixedStatus: { $in: [ 1, 2 ] }, _id: { $lt: 2038092587 } }, $orderby: { _id: -1 } } planSummary: IXSCAN { _id: 1 } ntoreturn:1000 ntoskip:0 keysExamined:37567133 docsExamined:37567133 cursorExhausted:1 keyUpdates:0 writeConflicts:0 numYields:293501 nreturned:659 reslen:2469894 locks:{ Global: { acquireCount: { r: 587004 } }, Database: { acquireCount: { r: 293502 } }, Collection: { acquireCount: { r: 293502 } } } \n\n# 耗时\n179530ms","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"耗时180秒且基于查询的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"执行计划","attrs":{}}],"attrs":{}},{"type":"text","text":"可以看出,它走的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"id","attrs":{}}],"attrs":{}},{"type":"text","text":"索引,进行了全表扫描,扫描的数据总量为:37567133,不慢才怪。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"迅速解决","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定位到问题后,没办法立即修改,第一要务是:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"止损","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结合当时的时间也比较晚了,因此我们发了公告,禁止了上述查询的功能并短暂暂停了部分业务,,过了一会之后进行了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"主从切换","attrs":{}}],"attrs":{}},{"type":"text","text":",再去看","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Zabbix监控","attrs":{}}],"attrs":{}},{"type":"text","text":"就一切安好了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"分析根源","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们回顾一下查询的语句和我们预期的索引,如下所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"### 原始Query\ndb.getCollection(\"sku_main\").find({ \n \"orgCode\" : NumberLong(337451), \n \"fixedStatus\" : { \n \"$in\" : [\n 1.0, \n 2.0\n ]\n }, \n \"shopCategories\" : { \n \"$exists\" : false\n }, \n \"_id\" : { \n \"$lt\" : NumberLong(2038092587)\n }\n }\n).sort(\n { \n \"_id\" : -1.0\n }\n).skip(1000).limit(1000);\n\n### 期望的索引\ndb.sku_main.ensureIndex({\"orgCode\": 1, \"_id\": -1},{background:true});","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"乍一看,好像一切都很Nice啊,字段","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orgCode","attrs":{}}],"attrs":{}},{"type":"text","text":"等值查询,字段","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"_id","attrs":{}}],"attrs":{}},{"type":"text","text":"按照创建索引的方向进行倒序排序,为啥会这么慢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,关键的一点就在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"$lt","attrs":{}}],"attrs":{}},{"type":"text","text":" 上","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"知识点一:索引、方向及排序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在MongoDB中,排序操作可以通过从索引中按照索引的顺序获取文档的方式,来保证结果的有序性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果MongoDB的查询计划器没法从索引中得到排序顺序,那么它就需要在内存中对结果排序。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注意","attrs":{}},{"type":"text","text":":不用索引的排序操作,会在内存超过32MB时终止,也就是说MongoDB只能支持32MB以内的非索引排序","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"知识点二:单列索引不在乎方向","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"无论是MongoDB还是MySQL都是用的树结构作为索引,如果","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"排序方向","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"索引方向","attrs":{}}],"attrs":{}},{"type":"text","text":"相反,只需要从另一头开始遍历即可,如下所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"# 索引\ndb.records.createIndex({a:1}); \n\n# 查询\ndb.records.find().sort({a:-1});\n\n# 索引为升序,但是我查询要按降序,我只需要从右端开始遍历即可满足需求,反之亦然\nMIN 0 1 2 3 4 5 6 7 MAX","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"MongoDB的复合索引结构","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"官方介绍:MongoDB supports ","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"compound indexes","attrs":{}},{"type":"text","text":", where a single index structure holds references to multiple fields within a collection’s documents. ","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"复合索引结构示意图如下所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fa/fae4f0f93651fb89389924012a0b230c.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"该索引刚好和我们讨论的是一样的,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"userid顺序","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"score倒序","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们需要直面第二个问题:","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"复合索引在使用时需不需要在乎方向?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假设两个查询条件:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"# 查询 一\ndb.getCollection(\"records\").find({ \n \"userid\" : \"ca2\"\n}).sort({\"score\" : -1.0});\n\n\n# 查询 二\ndb.getCollection(\"records\").find({ \n \"userid\" : \"ca2\"\n}).sort({\"score\" : 1.0});","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述的查询没有任何问题,因为受到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"score","attrs":{}}],"attrs":{}},{"type":"text","text":"字段排序的影响,只是数据从左侧还是从右侧遍历的问题,那么下面的一个查询呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"# 错误示范\ndb.getCollection(\"records\").find({ \n \"userid\" : \"ca2\",\n \"score\" : { \n \"$lt\" : NumberLong(2038092587)\n }\n}).sort({\"score\" : -1.0});","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"错误原因如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"由于score字段按照倒序排序,因此为了使用该索引,所以需要从左侧开始遍历","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"从倒序顺序中找小于某个值的数据,势必会扫描很多无用数据,然后丢弃,当前场景下找大于某个值才是最佳方案","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"所以MongoDB为了更多场景考虑,在该种情况下,放弃了复合索引,选用其他的索引,如 score 的单列索引","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"针对性修改","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"仔细阅读了根源之后,再回顾线上的查询语句,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"### 原始Query\ndb.getCollection(\"sku_main\").find({ \n \"orgCode\" : NumberLong(337451), \n \"fixedStatus\" : { \n \"$in\" : [\n 1.0, \n 2.0\n ]\n }, \n \"shopCategories\" : { \n \"$exists\" : false\n }, \n \"_id\" : { \n \"$lt\" : NumberLong(2038092587)\n }\n }\n).sort(\n { \n \"_id\" : -1.0\n }\n).skip(1000).limit(1000);\n\n### 期望的索引\ndb.sku_main.ensureIndex({\"orgCode\": 1, \"_id\": -1},{background:true});","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"犯的错误一模一样,所以","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MongoDB","attrs":{}}],"attrs":{}},{"type":"text","text":"放弃了复合索引的使用,该为单列索引,因此进行针对性修改,把 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"$lt","attrs":{}}],"attrs":{}},{"type":"text","text":" 条件改为 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"$gt","attrs":{}}],"attrs":{}},{"type":"text","text":" 观察优化结果:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"# 原始查询\n[TEMP INDEX] => lt: {\"limit\":1000,\"queryObject\":{\"_id\":{\"$lt\":2039180008},\"categoryId\":23372,\"orgCode\":351414,\"fixedStatus\":{\"$in\":[1,2]}},\"restrictedTypes\":[],\"skip\":0,\"sortObject\":{\"_id\":-1}}\n\n# 原始耗时\n[TEMP LT] => 超时 (超时时间10s)\n\n# 优化后查询\n[TEMP INDEX] => gt: {\"limit\":1000,\"queryObject\":{\"_id\":{\"$gt\":2039180008},\"categoryId\":23372,\"orgCode\":351414,\"fixedStatus\":{\"$in\":[1,2]}},\"restrictedTypes\":[],\"skip\":0,\"sortObject\":{\"_id\":-1}}\n\n# 优化后耗时\n[TEMP GT] => 耗时: 383ms , List Size: 999","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"总结","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分析了小2000字,其实改动就是两个字符而已,当然真正的改动需要考虑业务的需要,但是问题既然已经定位,修改什么的就不难了,回顾上述内容总结如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"学习数据库知识的时候可以用类比的方式,但是需要额外注意其不同的地方(MySQL、MongoDB索引、索引的方向)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MongoDB数据库单列索引可以不在乎方向,如对无索引字段排序需要控制数据量级(32M)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"MongoDB数据库复合索引在使用中一定要注意其方向","attrs":{}}],"attrs":{}},{"type":"text","text":",要完全理解其逻辑,避免索引失效","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"最后","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你觉得这篇内容对你挺有帮助的话:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":" 当然要点赞支持一下啦~","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":" 搜索并关注公众号「","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"是Kerwin啊","attrs":{}},{"type":"text","text":"」,一起唠唠嗑~","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":" 再来看看最近几篇的「","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"查漏补缺","attrs":{}},{"type":"text","text":"」系列吧,该系列会持续输出~","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - ","attrs":{}},{"type":"link","attrs":{"href":"https://juejin.cn/post/6870264679063617550","title":""},"content":[{"type":"text","text":"「查缺补漏」巩固你的Nginx知识体系","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - ","attrs":{}},{"type":"link","attrs":{"href":"https://juejin.cn/post/6862865811137101837","title":""},"content":[{"type":"text","text":"「查缺补漏」巩固你的RocketMQ知识体系","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - ","attrs":{}},{"type":"link","attrs":{"href":"https://juejin.cn/post/6857667542652190728","title":""},"content":[{"type":"text","text":"「查缺补漏」巩固你的Redis知识体系(笑)","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - ","attrs":{}},{"type":"link","attrs":{"href":"https://juejin.cn/post/6860262796618268680","title":""},"content":[{"type":"text","text":"悄咪咪提高团队幸福感 & Surprise!","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章