腾讯云数据库MongoDB人造反对高可用、分布式、高性能、高压缩、schema free、欠缺的客户端拜访平衡策略等性能。云上某重点用户基于MongoDB这些劣势,选用MongoDB作为主存储服务,该用户业务场景如下:
· 存储电商业务外围数据
· 查问条件多变、查问不固定,查问较简单,查问组合泛滥
· 对性能要求较高
· 对存储老本有要求
· 流量占比:insert较少、update较多、find较多、峰值流量较高
· 高峰期读写流量数千/秒
通过和业务沟通,理解业务应用场景和业务述求后,通过一系列的索引优化,最终完满解决读写性能瓶颈问题。本文重点剖析该外围业务索引优化过程,通过本文能够学习到以下知识点:
· 如何确定无用索引?
· 如何确定反复索引?
· 如何创立最优索引?
· 对索引的一些错误认识?
· 索引优化收益(节俭90%以上CPU资源、85%磁盘IO资源、20%存储老本)
问题剖析过程
收到用户集群性能瓶颈反馈后,通过集群监控信息及服务器监控信息能够看出集群存在如下景象:
· Mongod节点CPU耗费过高,CPU时不时耗费靠近90%,甚至100%
· 磁盘IO耗费过高,单节点IO资源耗费占整服务器60%
· 大量慢日志(次要集中在find和update),高峰期每秒数千条慢日志
· 慢日志类型各不相同,查问条件泛滥
· 所有慢查问都有匹配到索引
登录服务器对应节点后盾,获取慢日志信息,发现mongod.log中蕴含大量不同类型find和update的慢日志,慢日志都有走索引,任意提取一条慢日志其内容如下:
Mon Aug 2 10:34:24.928 I COMMAND [conn10480929] command xxx.xxx command: find { find: "xxx", filter: { $and: [ { alxxxId: "xxx" }, { state: 0 }, { itemTagList: { $in: [ xx ] } }, { persxxal: 0 } ] }, limit: 3, maxTimeMS: 10000 } planSummary: IXSCAN { alxxxId: 1.0, itemTagList: 1.0 } keysExamined:1650 docsExamined:1650 hasSortStage:0 cursorExhausted:1 keyUpdates:0 writeConflicts:0 numYields:15 nreturned:3 reslen:8129 locks:{ Global: { acquireCount: { r: 32 } }, Database: { acquireCount: { r: 16 } }, Collection: { acquireCount: { r: 16 } } } protocol:op_command 227ms Mon Aug 2 10:34:22.965 I COMMAND [conn10301893] command xx.txxx command: find { find: "txxitem", filter: { $and: [ { itxxxId: "xxxx" }, { state: 0 }, { itemTagList: { $in: [ xxx ] } }, { persxxal: 0 } ] }, limit: 3, maxTimeMS: 10000 } planSummary: IXSCAN { alxxxId: 1.0, itemTagList: 1.0 } keysExamined:1498 docsExamined:1498 hasSortStage:0 cursorExhausted:1 keyUpdates:0 writeConflicts:0 numYields:12 nreturned:3 reslen:8039 locks:{ Global: { acquireCount: { r: 26 } }, Database: { acquireCount: { r: 13 } }, Collection: { acquireCount: { r: 13 } } } protocol:op_command 158ms
从下面的日志打印能够看出,查问都有走 { alxxxId: 1.0, itemTagList: 1.0 } 索引,走该索引扫描的keysExamined为1498行,扫描的docsExamined为1498行,然而返回的doc文档数却只有nreturned=3行。
通过下面的日志外围信息能够看出,满足条件的数据只有3条,然而却扫描了1498行数据和索引,阐明查问有走索引,然而不是最优所有。
获取用户SQL查问模型及已有索引信息
下面的剖析能够确定问题呈现在索引不是最优,大量查问找了很多无用数据。
3.1. 和用户接触,理解用户SQL模型
通过和用户沟通,收集到用户查问、更新次要波及以下SQL类型:
· 罕用查问、更新类SQL
基于AlxxxId(用户ID)+itxxxId(单个或多个)
基于AlxxxId查问count
基于AlxxxId通过工夫范畴(createTime)进行分页查问,局部查问会拼接state及其他字段
基于AlxxxId,ParentAlxxxId,parentItxxxId,state组合查问
基于ItxxxId(单个或多个)查问数据
基于AlxxxId, state, updateTime组合查问
基于AlxxxId, state,createTime, totalStock(库存数量)组合查问
基于AlxxxId(用户ID)+itxxxId(单个或多个)+任意其余字段组合
基于AlxxxId, digitalxxxrmarkId(水印ID), state进行查问
基于AlxxxId, itemTagList(标签ID),state等进行查问
基于AlxxxId+itxxxId(单个或多个) +其余任意字段进行查问
其余查问
· 统计类count查问SQL
AlxxxId,state, persxxal 组合
AlxxxId, state,itemType 组合
AlxxxId(用户ID)+itxxxId(单个或多个)+任意其余字段组合
3.2. 获取集群已有索引
通过db.xxx.getindex({})获取到该表的索引信息如下,总计30个索引:
{ "alxxxId" : 1, "state" : -1, "updateTime" : -1, "itxxxId" : -1, "persxxal" : 1, "srcItxxxId" : -1 } { "alxxxId" : 1, "image" : 1 } { "itexxxList.vidxxCheck" : 1, "itemType" : 1, "state" : 1 } { "alxxxId" : 1, "state" : -1, "newsendTime" : -1, "itxxxId" : 1, "persxxal" : 1 } { "_id" : 1 } { "alxxxId" : 1, "createTime" : -1, "checkStatus" : 1 } { "alxxxId" : 1, "parentItxxxId" : -1, "state" : -1, "updateTime" : -1, "persxxal" : 1, "srcItxxxId" : -1 } { "alxxxId" : 1, "state" : -1, "parentItxxxId" : 1, "updateTime" : -1, "persxxal" : -1 } { "srcItxxxId" : 1 } { "createTime" : 1 } { "itexxxList.boyunState" : -1, "itexxxList.wozhituUploadServerId": -1, "itexxxList.photoQiniuUrl" : 1, "itexxxList.sourceType" : 1 } { "alxxxId" : 1, "state" : 1, "digitalxxxrmarkId" : 1, "updateTime" : -1 } { "itxxxId" : -1 } { "alxxxId" : 1, "parentItxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } { "alxxxId" : 1, "videoCover" : 1 } { "alxxxId" : 1, "itemType" : 1 } { "alxxxId" : 1, "state" : -1, "itemType" : 1, "persxxal" : 1, "updateTime" : 1 } { "alxxxId" : 1, "itxxxId" : 1 } { "itxxxId" : 1, "alxxxId" : 1 } { "alxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } { "alxxxId" : 1, "itemTagList" : 1 } { "itexxxList.photoQiniuUrl" : 1, "itexxxList.boyunState" : -1, "itexxxList.sourceType" : 1, "itexxxList.wozhituUploadServerId" : -1 } { "alxxxId" : 1, "parentItxxxId" : 1, "state" : 1 } { "alxxxId" : 1, "parentItxxxId" : 1, "updateTime" : 1 } { "updateTime" : 1 } { "itemPhoxxIdList" : -1 } { "alxxxId" : 1, "state" : -1, "isTop" : 1 } { "alxxxId" : 1, "state" : 1, "itemResxxxIdList" : 1, "updateTime" : -1 } { "alxxxId" : 1, "state" : -1, "itexxxList.photoQiniuUrl" : 1 } { "itexxxList.qiniuStatus" : 1, "itexxxList.photoNetUrl" : 1, "itexxxList.photoQiniuUrl" : 1 } { "itemResxxxIdList" : 1 }
索引优化过程
从上一节能够看出,该集群查问简单,同时索引泛滥,通过剖析已有索引和用户数据模型,对已有索引做如下优化,最终无效索引缩小到8个。
4.1. 第一轮优化:删除无用索引
MongoDB默认提供有索引统计命令来获取各个索引命中的次数,该命令如下:
> db.xxxxx.aggregate({"$indexStats":{}}) { "name" : "alxxxId_1_parentItxxxId_1_parentAlxxxId_1", "key" : { "alxxxId" : 1, "parentItxxxId" : 1, "parentAlxxxId" : 1 },"host" : "TENCENT64.site:7014", "accesses" : { "ops" : NumberLong(11236765),"since" : ISODate("2020-08-17T06:39:43.840Z") } }
该聚合输入中的几个外围指标信息如下表:
字段内容
阐明
name
索引名,代表是针对那个索引的统计。
ops
索引命中次数,也就是所有查问中采纳本索引作为查问索引的次数。
上表中的ops代表命中次数,如果命中次数为0或者很小,阐明该索引很少被选为最优索引应用,因而能够工作是无用索引,能够间接删除。
获取用户外围表索引统计信息,如下:
db.xxx.aggregate({"$indexStats":{}}) { "alxxxId" : 1, "state" : -1, "updateTime" : -1, "itxxxId" : -1, "persxxal" : 1, "srcItxxxId" : -1 } "ops" : NumberLong(88518502) { "alxxxId" : 1, "image" : 1 } "ops" : NumberLong(293104) { "itexxxList.vidxxCheck" : 1, "itemType" : 1, "state" : 1 } "ops" : NumberLong(0) { "alxxxId" : 1, "state" : -1, "newsendTime" : -1, "itxxxId" : -1, "persxxal" : 1 } "ops" : NumberLong(33361216) { "_id" : 1 } "ops" : NumberLong(3987) { "alxxxId" : 1, "createTime" : 1, "checkStatus" : 1 } "ops" : NumberLong(20042796) { "alxxxId" : 1, "parentItxxxId" : -1, "state" : -1, "updateTime" : -1, "persxxal" : 1, "srcItxxxId" : -1 } "ops" : NumberLong(43042796){ "alxxxId" : 1, "state" : -1, "parentItxxxId" : 1, "updateTime" : -1, "persxxal" : -1 } "ops" : NumberLong(3042796){ "itxxxId" : -1 } "ops" : NumberLong(38854593){ "srcItxxxId" : -1 } "ops" : NumberLong(0) { "createTime" : 1 } "ops" : NumberLong(62) { "itexxxList.boyunState" : -1, "itexxxList.wozhituUploadServerId" : -1, "itexxxList.photoQiniuUrl" : 1, "itexxxList.sourceType" : 1 } "ops" : NumberLong(0) { "alxxxId" : 1, "state" : 1, "digitalxxxrmarkId" : 1, "updateTime" : -1 } "ops" : NumberLong(140238342) { "itxxxId" : -1 } "ops" : NumberLong(38854593) { "alxxxId" : 1, "parentItxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } "ops" : NumberLong(132237254) { "alxxxId" : 1, "videoCover" : 1 } { "ops" : NumberLong(2921857) { "alxxxId" : 1, "itemType" : 1 } { "ops" : NumberLong(457) { "alxxxId" : 1, "state" : -1, "itemType" : 1, "persxxal" : 1, " itxxxId " : 1 } "ops" : NumberLong(68730734) { "alxxxId" : 1, "itxxxId" : 1 } "ops" : NumberLong(232360252) { "itxxxId" : 1, "alxxxId" : 1 } "ops" : NumberLong(145640252) { "alxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } "ops" : NumberLong(689891) { "alxxxId" : 1, "itemTagList" : 1 } "ops" : NumberLong(2898693682) { "itexxxList.photoQiniuUrl" : 1, "itexxxList.boyunState" : 1, "itexxxList.sourceType" : 1, "itexxxList.wozhituUploadServerId" : 1 } "ops" : NumberLong(511303207) { "alxxxId" : 1, "parentItxxxId" : 1, "state" : 1 } "ops" : NumberLong(0) { "alxxxId" : 1, "parentItxxxId" : 1, "updateTime" : 1 } "ops" : NumberLong(0) { "updateTime" : 1 } "ops" : NumberLong(1397) { "itemPhoxxIdList" : -1 } "ops" : NumberLong(0) { "alxxxId" : 1, "state" : -1, "isTop" : 1 } "ops" : NumberLong(213305) { "alxxxId" : 1, "state" : 1, "itemResxxxIdList" : 1, "updateTime" : 1 } "ops" : NumberLong(2591780) { "alxxxId" : 1, "state" : 1, "itexxxList.photoQiniuUrl" : 1} "ops" : NumberLong(23505){ "itexxxList.qiniuStatus" : 1, "itexxxList.photoNetUrl" : 1, "itexxxList.photoQiniuUrl" : 1 } "ops" : NumberLong(0) { "itemResxxxIdList" : 1 } "ops" : NumberLong(7)
该业务曾经运行一段时间,首先把ops小于10000的索引删除,满足该条件的索引如下面的红色局部索引。
通过该轮删除11个无用索引后,残余有用索引30-11=19个索引。
4.2. 第二轮优化:删除反复索引
反复索引次要包含以下几类:
· 查问程序引起的索引反复
例如该业务不同开发写了如下两种查问:
db.xxxx.find({{ "alxxxId" : xxx, "itxxxId" : xxx }}) db.xxxx.find({{ " itxxxId " : xxx, " alxxxId " : xxx }})
为了应答这两种查问SQL,DBA创立了两个索引:
{ alxxxId :1, itxxxId:1 }和{ itxxxId :1, alxxxId:1}
这两个SQL实际上创立任何一个索引即可,无需两个索引,因而这就是无用索引。
· 最左准则匹配引起的索引反复
例如上面的三个索引存在反复索引:
{ itxxxId:1, alxxxId:1 }和{ itxxxId :1}这两个索引,{ itxxxId :1}即为反复索引。
· 蕴含关系引起的索引反复
例如下面的以下两个索引为反复索引:
{ "alxxxId" : 1, "parentItxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } { "alxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } { "alxxxId" : 1, " state " : 1 }
和用户确认,用户创立这三个索引,是因为有如下三个查问:
Db.xxx.find({ "alxxxId" : xxx, "parentItxxxId" : xx, "parentAlxxxId" : xxx, "state" : xxx }) Db.xxx.find({ "alxxxId" : xxx, " parentAlxxxId " : xx, " state " : xxx }) Db.xxx.find({ "alxxxId" : xxx, " state " : xxx })
这几个查问都蕴含公共字段,因而能够合并为一个索引来满足这两类SQL的查问,合并后的索引如下:
{ "alxxxId" : 1, " state " : 1, " parentAlxxxId " : 1, parentItxxxId :1}
通过以上几个索引准则优化后,以下几个索引能够优化,优化前索引:
{ itxxxId:1, alxxxId:1 } { alxxxId:1, itxxxId:1 } {itxxxId:1 } { "alxxxId" : 1, "parentItxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } { "alxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } { "alxxxId" : 1, " state " : 1 }
这6个索引能够合并优化为如下2个索引:
{ itxxxId:1, alxxxId:1 } { "alxxxId" : 1, "parentItxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 }
4.3. 第三轮优化:获取数据模型,剔除惟一索引引起的无用索引
通过剖析表中数据各个字段模块组合,发现alxxxId和itxxxId字段为高频字段,通过剖析字段schema信息,随机抽取一部分数据,发现这两个字段组合是惟一的。于是和用户确认,用户反馈这两个字段的任意组合都代表一条惟一的数据。
如果{alxxxId:1, itxxxId:1}索引能够确定唯一性,则这两个字段和任何字段的组合都是惟一的。因而上面的几个索引能够合并为一个索引:
{ "alxxxId" : 1, "state" : -1, "updateTime" : -1, "itxxxId" : 1, "persxxal" : 1, "srcItxxxId" : -1 } { "alxxxId" : 1, "state" : -1, "itemType" : 1, "persxxal" : 1, " itxxxId " : 1 } { "alxxxId" : 1, "state" : -1, "newsendTime" : -1, "itxxxId" : 1, "persxxal" : 1 } { "alxxxId" : 1, "state" : 1, "itxxxId" : 1, "updateTime" : -1 } { itxxxId:1, alxxxId:1 }
下面的5个索引能够去重合并为以下一个索引:{ itxxxId:1, alxxxId:1 }
4.4. 第四轮优化:非等值查问引起的无用反复索引优化
从后面的30个索引能够看出,索引中有局部为工夫类型字段,如createTime、updateTime,这类字段个别用于范畴查问,通过和用户确认,这些字段的确用于各种范畴查问。因为范畴查问属于非等值查问,如果范畴查问字段呈现在索引字段后面,则前面字段无奈走索引,例如如下查问及索引:
db.collection.find({{ "alxxxId" : xx, "parentItxxxId" : xx, "state" : xx, "updateTime" : {$gt: xxxxx}, "persxxal" : xxx, "srcItxxxId" : xxx } }) db.collection.find({{ "alxxxId" : xx, "state" : xx, "parentItxxxId" : xx, "updateTime" : {$lt: xxxxx}, "persxxal" : xxx} })
用户为这两个查问减少了以下两个索引:
第一个索引如下:{ "alxxxId" : 1, "parentItxxxId" : -1, "state" : -1, "updateTime" : -1, "persxxal" : 1, "srcItxxxId" : -1 } 第二个索引如下:{ "alxxxId" : 1, "state" : -1, "parentItxxxId" : 1, "updateTime" : -1, "persxxal" : -1 }
因为这两个查问都蕴含updateTime字段,并进行范畴查问。因为除了updateTime字段以外的字段都是等值查问,因而下面两个查问实际上updateTime左边的字段无奈走索引。也就是下面的第一个索引persxxal和srcItxxxId字段无奈匹配索引,第二个索引persxxal字段无奈匹配索引。
同时因为这两个索引字段基本相同,为了更好保障更多字段走索引,因而能够合并优化为如下一个索引,确保更多字段可能走索引:
{ "alxxxId" : 1, "state" : -1, "parentItxxxId" : 1, "persxxal" : -1, "updateTime" : -1 }
4.5. 第五轮优化:去除查问频率较低的字段对应索引
第一轮优化删除无用索引的时候,过滤掉了命中率低于10000次以下的索引。然而,还有一部分索引相比高频命中次数(数十亿次)命中次数也绝对较低(命中次数只有几十万),这部分较低频命中次数的索引如下:
{ "alxxxId" : 1, "image" : 1 } "ops" : NumberLong(293104) { "alxxxId" : 1, "videoCover" : 1 } "ops" : NumberLong(292857)
调低慢日志时延阈值,剖析这两个查问对应日志,如下:
Mon Aug 2 10:56:46.533 I COMMAND [conn5491176] command xxxx.tbxxxxx command: count { count: "xxxxx", query: { alxxxId: "xxxxxx", itxxxId: "xxxxx", image: "http:/xxxxxxxxxxx/xxxxx.jpg" }, limit: 1 } planSummary: IXSCAN { itxxxId: 1.0,alxxxId:1.0 } keyUpdates:0 writeConflicts:0 numYields:1 reslen:62 locks:{ Global: { acquireCount: { r: 4 } }, Database: { acquireCount: { r: 2 } }, Collection: { acquireCount: { r: 2 } } } protocol:op_query 4ms Mon Aug 2 10:47:53.262 I COMMAND [conn10428265] command xxxx.tbxxxxx command: find { find: "xxxxx", filter: { $and: [ { alxxxId: "xxxxxxx" }, { state: 0 }, { itemTagList: { $size: 0 } } ] }, limit: 1, singleBatch: true } planSummary: IXSCAN { alxxxId: 1, videoCover: 1 } keysExamined:128 docsExamined:128 cursorExhausted:1 keyUpdates:0 writeConflicts:0 numYields:22 nreturned:0 reslen:108 locks:{ Global:{ acquireCount: { r: 46 } }, Database: { acquireCount: { r: 23 } }, Collection: { acquireCount: { r: 23 } } } protocol:op_command 148ms
剖析日志发现用户申请中的image都是和alxxxId,itxxxId进行组合查问,后面提到alxxxId,itxxxId是惟一的,从查问打算也能够看出,image字段齐全没有走索引。因而{ "alxxxId" : 1, "ixxxge" : 1 }索引能够删除。
同理,剖析日志发现用户查问条件中没有携带videoCover,只是局部查问走了{ alxxxId: 1, videoCover: 1 } 索引,并且keysExamined、docsExamined与nreturned不雷同,所以能够确认理论只匹配了alxxxId索引字段。因而,该索引{ alxxxId: 1, videoCover: 1 } 能够删除。
4.6. 第六轮优化:剖析日志高频查问,增加高频查问最优索引
调低日志阈值,通过mtools工具剖析一段时间的查问,获取到如下热点查问信息:
这部分高频热点查问简直占用了99%以上的查问,因而务必确保这部分查问须要所有字段能走索引。剖析该类查问对应日志,失去如下信息:
Mon Aug 2 10:47:58.015 I COMMAND [conn4352017] command xxxx.xxx command: find { find: "xxxxx", filter: { $and: [ { alxxxId:"xxxxx" }, { state: 0 }, { itemTagList: { $in: [ xxxxx ] } }, { persxxal: 0 } ] }, projection: { $sortKey: { $meta: "sortKey" } }, sort: { updateTime: 1 }, limit: 3, maxTimeMS: 10000 } planSummary: IXSCAN { alxxxId: 1.0, itexxagList: 1.0 } keysExamined:1327 docsExamined:1327 hasSortStage:1 cursorExhausted:1 keyUpdates:0 writeConflicts:0 numYields:23 nreturned:3 reslen:12036 locks:{ Global: { acquireCount: { r: 48 } }, Database: { acquireCount: { r: 24 } }, Collection: { acquireCount: { r: 24 } } } protocol:op_command 151ms
下面日志能够看出,该高频查问扫描数据行数和最终返回的数据行数差距很大,扫描了1327行,最终只获取到了3条数据,走的是 { alxxxId: 1.0, itexxagList: 1.0 }
索引,该索引不是最优索引。该高频查问是四字段的等值查问,只有两个字段走了索引,能够把该索引优化为如下索引:{ alxxxId: 1.0, itexxagList: 1.0 , persxxal:1.0, stat:1.0}
此外,从日志能够看出,该高频查问实际上还有个sort排序和limit限度,整个查问原始SQL如下:
db.xxx.find({ $and: [ { alxxxId:"xxxx" }, { state: 0 }, { itexxagList: { $in: [ xxxx ] } },{ persxxal: 0 } ] }).sort({updateTime:1}).limit(3)
该查问模型为一般多字段等值查问 + sort排序类查问 + limit限度。该类查问最优索引可能是上面两个索引中的一个:
· 索引1:一般多字段等值查问对应索引
对应查问中的如下SQL查问条件:
{ $and: [ { alxxxId:"xxx" }, { state: 0 }, { itexxagList: { $in: [ xxxx ] } }, { persxxal: 0 } ] }
该SQL四个字段都为等值查问,依照散列度创立最优索引,取值越散列的字段放最右边,能够失去如下最优索引:
{ alxxxId: 1.0, itexxagList: 1.0 , persxxal:1.0, stat:1.0}
如果抉择该索引作为最优索引,则整个一般多字段等值查问 + sort排序类查问 + limit限度查问执行流程如下:
- 通过{ alxxxId: 1.0, itexxagList: 1.0 , persxxal:1.0, stat:1.0}索引找出满足{ $and: [ { alxxxId:"xxxx" }, { state: 0 }, { itexxagList: { $in: [ xxxx ] } }, { persxxal: 0 } ] }条件的所有数据。
- 对这些满足条件的数据进行内存排序
- 取排序好的前三条数据
· 索引2:Sort排序对应最优索引
因为查问中带有limit,因而有可能间接走{updateTime:1}排序索引,通过该索引找出三条满足以下查问条件的数据:
{ $and: [ { alxxxId:"xxxx" }, { state: 0 }, { itexxagList: { $in: [ xxxx ] } }, { persxxal: 0 } ] }
整个一般多字段等值查问 + sort排序类查问 + limit限度查问对应索引抉择索引1和索引2和数据分布有较大的关系,因为该查问为超高频查问,因而倡议这类SQL增加2个索引,由MongoDB内核依据理论查问条件和数据分布本人决定抉择那个索引作为最优索引,该高频查问对应索引如下:
{ alxxxId: 1.0, itexxagList: 1.0 , persxxal:1.0, stat:1.0} {updateTime:1}
4.7. 小结
通过后面六轮优化后,最终只保留如下8个索引:
{ "itxxxId" : 1, "alxxxId" : 1 } { "alxxxId" : 1, "state" : 1, "digitalxxxrmarkId" : 1, "updateTime" : 1 } { "alxxxId" : 1, "state" : -1, "parentItxxxId" : 1, "persxxal" : -1, "updateTime" : 1 } { "alxxxId" : 1, "itexxxList.photoQiniuUrl" : 1, } { "alxxxId" : 1, "parentAlxxxId" : 1, "state" : 1"parentItxxxId" : 1} { alxxxId: 1.0, itexxagList: 1.0 , persxxal:1.0, stat:1.0} {updateTime:1} { "alxxxId" : 1,"createTime" : -1}
索引优化收益
通过一系列索引优化后,最终索引缩小到8个,整体收益十分明细,次要收益如下:
· 节俭90% 以上CPU资源
峰值CPU耗费从之前的90%多降到优化后的10%以内
· 节俭85% 左右磁盘IO资源
磁盘IO耗费从之前的60%-70%升高到10%以内
· 节俭20%磁盘存储老本
因为每个索引都对应一个磁盘索引文件,索引从30个缩小到8个,数据+索引最终实在磁盘耗费缩小20%左右。
· 慢日志缩小99%
索引优化前慢日志每秒数千条,优化后慢日志条数升高到数十条。优化由慢日志次要有求count引起,满足条件数据太多,这是失常景象。
最初,索引对MongoDB数据库查问性能起着至关重要的作用,用起码索引满足用户查问需要会极大晋升数据库性能,并缩小存储老本。腾讯云DBbrain for MongoDB基于SQL分类+索引规定+代价计算完满实现索引智能举荐,下期将为大家带来腾讯云索引举荐计划及实现细节的分享。
作者:腾讯云MongoDB团队
腾讯云MongoDB以后服务于游戏、电商、社交、教育、新闻资讯、金融、物联网、软件服务等多个行业;MongoDB团队(简称CMongo)致力于对开源MongoDb内核进行深度钻研及持续性优化(如百万库表、物理备份、免密、审计等),为用户提供高性能、低成本、高可用性的平安数据库存储服务。后续继续分享MongoDb在腾讯外部及内部的典型利用场景、踩坑案例、性能优化、内核模块化剖析。