Mongodb 索引優化建議
阿新 • • 發佈:2018-12-25
業務背景
偶然的一次機會,聽到我們專案的API 呼叫方說請求超時,server 返回500,經查驗說是交易的collection資料量太大(超過100GB),而且每天通過spring batch新增80w資料量,目前表的資料量已經過億,有同事分析了查詢說是通過索引會掃描大量的無效資料(此處預設通過id 排序分頁),然後只能強制刪除了大部分資料(目前是30GB)。
索引優化
原先的查詢語句如下(涉及保密資料用xxxx代替):
db.Trade.find( {_id:{$lte:ObjectId("xxxxxxxx")},"startDate":{"$lte":ISODate("2018-12-08T23:59:59.000Z")},"endDate":{"$not":{"$lte":ISODate("2018-12-08T23:59:59.000Z")}},"source":"XXXX"}).hint('_id_1_startDate_1_endDate_1').sort({ '_id': -1 }).limit(100000)
db.Trade.find( {_id:{$lte:ObjectId("XXXXXXXX")}, "startDate":{"$lte":ISODate("2018-12-08T23:59:59.000Z")},"endDate":{"$not":{"$lte":ISODate("2018-12-08T23:59:59.000Z")}}, markingSystems: { $in: ['XXX'] }}).sort({ '_id': -1 }).limit(100000)
經分析建立如下索引:
db.Trade.createIndex({'source':1,'_id':-1,startDate:1,endDate:1},{background:true}) db.Trade.createIndex({'markingSystem':1,'_id':-1,startDate:1,endDate:1},{background:true})
優化後的performance : from 100sec to 2sec
原因:
原先的索引'_id_1_startDate_1_endDate_1'有明顯的缺點,它是先根據_id排序再過濾的,所以Iindexscan的時候會過濾大量的無效資料,而新的索引{'markingSystem':1,'_id':-1,startDate:1,endDate:1} or {'source':1,'_id':-1,startDate:1,endDate:1}會根據markingSystem or source過濾掉大量無效資料,接下來再根據其他過濾條件來過濾的範圍會小很多。
優化建議
組合索引第一個欄位應該是查詢頻率較高的欄位,第二個是你要排序的欄位,其餘放後面。
第一個欄位需要特別注意,最好放等值查詢的欄位,不要放範圍查詢的欄位,如 $gt, $gte, $lt等。
ps:如果你還有更好的建議,歡迎留言一起探討