日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:52003
  • 待審:43
  • 小程序:12
  • 文章:1047590
  • 會員:762

作者 | 吳守陽

審校 | 重樓

背景

線上某個頁面的響應速度異常緩慢,達到了16秒,嚴重影響了業務的正常運行。經過與研發的溝通得知,該頁面調用的數據集合只會保留7天的數據,集合有6000萬條記錄。針對過期數據的處理,使用了根據 create_time 字段創建的過期索引,以自動使數據失效。此外,數據集合還通過 company_id 字段進行了哈希分片。

問題排查

慢語句分析

在后臺拿到了慢查詢語句,如下:

db.visitor.find({
 "company_id": 13272,
 "create_time": {
 "$gte": ISODate("2024-04-11T00:00:00.000+0800"),
 "$lte": ISODate("2024-04-11T23:59:59.000+0800")
 }
});

db.visitor.find({
 "company_id": 13272,
 "create_time": {
 "$gte": ISODate("2024-04-12T00:00:00.000+0800"),
 "$lte": ISODate("2024-04-18T23:59:59.000+0800")
 }
});

很簡單的一個查詢,語句上沒有再優化的必要了。如果索引都在不應該出現這種十多秒的耗時,接下來開始分析索引。

索引分析

索引如下:

db.getCollection("visitor").createIndex({
 "company_id": "hashed"
}, {
 name: "company_id_hashed"
});

db.getCollection("visitor").createIndex({
 "company_id": NumberInt("1")
}, {
 name: "company_id_1"
});

db.getCollection("visitor").createIndex({
 "create_time": NumberInt("1")
}, {
 name: "create_time_1",
 expireAfterSeconds: NumberInt("604800")
});

其中:

  • company_id_hashed:創建集合分片使用的hash索引
  • company_id_1:普通查詢的索引
  • create_time_1:過期時間的索引
    就這點數據量,按理說會用到索引的,不應該執行耗時16s,接下來執行計劃分析。

ExplAIn執行計劃

winningPlan

"stage": "SHARDING_FILTER",
 "inputStage": {
 "stage": "FETCH",
 "filter": {
 "$and": [
 {
 "company_id": {
 "$eq": 13272
 }
 },
 {
 "create_time": {
 "$lte": ISODate("2024-04-17T15:59:59.000Z")
 }
 },
 {
 "create_time": {
 "$gte": ISODate("2024-04-10T16:00:00.000Z")
 }
 }
 ]
 },
 "inputStage": {
 "stage": "IXSCAN",
 "keyPattern": {
 "company_id": "hashed"
 },
 "indexName": "company_id_hashed",
 "isMultiKey": false,
 "isUnique": false,
 "isSparse": false,
 "isPartial": false,
 "indexVersion": NumberInt("2"),
 "direction": "forward",
 "indexBounds": {
 "company_id": [
 "[7977521071453068053, 7977521071453068053]"

這部分顯示只用到了company_id_hashed索引,沒有用到create_time_1索引。

rejectedPlans

"stage": "SHARDING_FILTER",
 "inputStage": {
 "stage": "FETCH",
 "filter": {
 "company_id": {
 "$eq": 13272
 }
 },
 "inputStage": {
 "stage": "IXSCAN",
 "keyPattern": {
 "create_time": 1
 },
 "indexName": "create_time_1",
 "isMultiKey": false,
 "multiKeyPaths": {
 "create_time": [ ]
 },
 "isUnique": false,
 "isSparse": false,
 "isPartial": false,
 "indexVersion": NumberInt("2"),
 "direction": "forward",
 "indexBounds": {
 "create_time": [
 "[new Date(1712764800000), new Date(1713369599000)]"
 ]
 }
 }
 }
 },
 {
 "stage": "SHARDING_FILTER",
 "inputStage": {
 "stage": "FETCH",
 "filter": {
 "$and": [
 {
 "create_time": {
 "$lte": ISODate("2024-04-17T15:59:59.000Z")
 }
 },
 {
 "create_time": {
 "$gte": ISODate("2024-04-10T16:00:00.000Z")
 }
 }
 ]
 },
 "inputStage": {
 "stage": "IXSCAN",
 "keyPattern": {
 "company_id": 1
 },
 "indexName": "company_id_1",
 "isMultiKey": false,
 "multiKeyPaths": {
 "company_id": [ ]
 },

這部分顯示的是被拒絕的執行計劃列表,不會用到company_id_1、create_time_1索引。

executionStats

"nReturned": NumberInt("229707"),
 "executionTimeMillis": NumberInt("15668"),
 "totalKeysExamined": NumberInt("238012"),
 "totalDocsExamined": NumberInt("238012"),
 "executionStages": {
 "stage": "SINGLE_SHARD",
 "nReturned": NumberInt("229707"),
 "executionTimeMillis": NumberInt("15668"),
 "totalKeysExamined": NumberInt("238012"),
 "totalDocsExamined": NumberInt("238012"),
 "totalChildMillis": NumberLong("15667"),
 "shards": [
 {
 "shardName": "d-m5eee03fdeaeaee4",
 "executionSuccess": true,
 "executionStages": {
 "stage": "SHARDING_FILTER",
 "nReturned": NumberInt("229707"),
 "executionTimeMillisEstimate": NumberInt("14996"),
 "works": NumberInt("238013"),
 "advanced": NumberInt("229707"),
 "needTime": NumberInt("8305"),
 "needYield": NumberInt("0"),
 "saveState": NumberInt("1980"),
 "restoreState": NumberInt("1980"),
 "iseoF": NumberInt("1"),
 "chunkSkips": NumberInt("0"),
 "inputStage": {
 "stage": "FETCH",
 "filter": {
 "$and": [
 {
 "company_id": {
 "$eq": 13272
 }
 },
 {
 "create_time": {
 "$lte": ISODate("2024-04-17T15:59:59.000Z")
 }
 },
 {
 "create_time": {
 "$gte": ISODate("2024-04-10T16:00:00.000Z")
 }
 }
 ]
 },
 "nReturned": NumberInt("229707"),
 "executionTimeMillisEstimate": NumberInt("14595"),
 "works": NumberInt("238013"),
 "advanced": NumberInt("229707"),
 "needTime": NumberInt("8305"),
 "needYield": NumberInt("0"),
 "saveState": NumberInt("1980"),
 "restoreState": NumberInt("1980"),
 "isEOF": NumberInt("1"),
 "docsExamined": NumberInt("238012"),
 "alreadyHasObj": NumberInt("0"),
 "inputStage": {
 "stage": "IXSCAN",
 "nReturned": NumberInt("238012"),
 "executionTimeMillisEstimate": NumberInt("251"),
 "works": NumberInt("238013"),
 "advanced": NumberInt("238012"),
 "needTime": NumberInt("0"),
 "needYield": NumberInt("0"),
 "saveState": NumberInt("1980"),
 "restoreState": NumberInt("1980"),
 "isEOF": NumberInt("1"),
 "keyPattern": {
 "company_id": "hashed"
 },
 "indexName": "company_id_hashed",
 "isMultiKey": false,
 "isUnique": false,
 "isSparse": false,
 "isPartial": false,
 "indexVersion": NumberInt("2"),
 "direction": "forward",
 "indexBounds": {
 "company_id": [
 "[7977521071453068053, 7977521071453068053]"
 ]
 },
 "keysExamined": NumberInt("238012"),
 "seeks": NumberInt("1"),
 "dupsTested": NumberInt("0"),
 "dupsDropped": NumberInt("0")

這部分顯示的是查詢的執行統計信息。

索引分析

通過explain的執行計劃,可以看出索引的使用上存在問題。按理說company_id、create_time都已創建索引,為什么沒有使用上?是什么原因導致它失效,沒有用上create_time索引?

下面列舉了失效的情況:

  • 索引選擇性不高:由于查詢條件是一個范圍查詢,create_time 字段可能有許多不同的值滿足條件。因此,單鍵索引 create_time_1 的選擇性(即索引中不同值的比例)可能不高,這使得使用該索引無法有效地減少需要檢索的文檔數量。
  • 查詢需要跨越多個索引鍵值:查詢涉及到了兩個字段 company_id 和 create_time。雖然索引 create_time_1 可以幫助過濾 create_time 符合條件的文檔,但在執行查詢時,還需要考慮 company_id 的匹配條件。因此,MongoDB 需要在兩個索引之間進行查找和合并,而不是簡單地使用單個索引來解決查詢。
  • 額外的查找和合并成本:在涉及多個條件的查詢中,MongoDB 會嘗試使用覆蓋索引(Covered Index)來盡可能地減少在磁盤上的文檔檢索。然而,在這種情況下,create_time_1 索引不能單獨滿足查詢條件,因此 MongoDB 還需要查找和合并從 company_id_1 索引中過濾出來的文檔。這種額外的查找和合并過程會增加查詢的成本,并且降低性能。
    因此,針對給定的查詢語句,MongoDB 不會使用 create_time_1 索引來優化查詢,而是會選擇其他更適合的索引,如 company_id_hashed 和 company_id_1。

問題原因

造成執行耗時過長的主要原因是索引失效的問題,在涉及多個條件的查詢中,MongoDB 會嘗試使用覆蓋索引(Covered Index)來盡可能地減少在磁盤上的文檔檢索。然而,在這種情況下,create_time_1 索引不能單獨滿足查詢條件,因此 MongoDB 還需要查找和合并從 company_id_1 索引中過濾出來的文檔。這種額外的查找和合并過程會增加查詢的成本,并且降低性能。

優化方案

創建新的復合索引company_id_create_time,讓其走company_id_hashed到company_id_create_time的鏈路。添加新的索引后,相同的語句執行時間只需要400ms,能滿足業務的需求。

結論

要多關注索引在什么情況下會失效?復合索引的先后順序,不是每個條件字段都建個單個普通索引,查詢語句都會使用上,不要存在這種誤區,有時候復合索引才是最完美的組合。

執行計劃詳解

1、queryPlanner:包含了MongoDB查詢的執行計劃。

  • mongosPlannerVersion:MongoDB計劃版本。
  • winningPlan:勝出的執行計劃,即MongoDB選擇的最佳執行計劃。
  • shards: 分片的詳細信息,包括分片名稱、連接字符串、服務器信息等。2、winningPlan: 勝出的執行計劃。
  • stage: 執行階段,這里是SINGLE_SHARD,表示單分片操作。
  • shardName: 執行操作的分片名稱。
  • plannerVersion: 計劃版本。
  • namespace: 查詢的命名空間。
  • indexFilterSet: 是否設置了索引過濾器。
  • parsedQuery: 解析后的查詢條件。
  • winningPlan: 勝出的執行計劃的詳細信息,這里是SHARDING_FILTER。3、rejectedPlans: 被拒絕的執行計劃列表,即非勝出的備選計劃。
    每個被拒絕的執行計劃包含了其詳細信息,包括執行階段、過濾器、索引掃描等。
    4、executionStats: 查詢的執行統計信息。
  • nReturned: 返回的文檔數量。
  • executionTimeMillis: 查詢執行時間(毫秒)。
  • totalKeysExamined: 總共檢查的鍵數量。
  • totalDocsExamined: 總共檢查的文檔數量。
  • executionStages: 執行階段的詳細統計信息。

作者介紹

吳守陽,51CTO社區編輯,擁有8年DBA工作經驗,熟練管理MySQL、redis、MongoDB等開源數據庫。精通性能優化、備份恢復和高可用性架構設計。善于故障排除和自動化運維,保障系統穩定可靠。具備良好的團隊合作和溝通能力,致力于為企業提供高效可靠的數據庫解決方案。

分享到:
標簽:MongoDB
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 52003

    網站

  • 12

    小程序

  • 1047590

    文章

  • 762

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定