一個MongoDB索引走偏的案例及探究分析

来源:https://www.cnblogs.com/xuliuzai/archive/2019/08/13/11347028.html
-Advertisement-
Play Games

接業務需求,有一個MongoDB的簡單查詢,太耗時了,執行了 70S 左右,嚴重影響用戶的體驗。。 查詢代碼主要如下: 此集合在欄位OPTime上有索引idx_OPTime;在"Tags"數組中的內嵌欄位"SN"有索引idx_TSN;兩者都是獨立的索引。此集合存放的是執行Log,相對Size較大。 ...


接業務需求,有一個MongoDB的簡單查詢,太耗時了,執行了 70S 左右,嚴重影響用戶的體驗。。

查詢代碼主要如下:

db.duoduologmodel.find({"Tags.SN": "QZ435698245"})
 .projection({})
 .sort({OPTime: -1})
.limit(20)

此集合在欄位OPTime上有索引idx_OPTime;在"Tags"數組中的內嵌欄位"SN"有索引idx_TSN;兩者都是獨立的索引。此集合存放的是執行Log,相對Size較大。

查看此查詢對應的慢查詢日誌,如下:

2019-08-13T15:07:16.767+0800 I COMMAND [conn536359] command shqq_zp.duoduologmodel command: find { find: "duoduologmodel", filter: { Tags.SN: "QZ435698245" }, sort: { OPTime: -1 }, projection: {}, limit: 20, $db: "shqq_zp", $clusterTime: { clusterTime: Timestamp(1565679737, 71), signature: { hash: BinData(0, E7B0A887E83BAD0AA0A72016A39C677B53ABDBE2), keyId: 6658116868433248258 } }, lsid: { id: UUID("0f22409b-f122-41e9-a094-46ccf04e44c7") } } planSummary: IXSCAN { OPTime: 1 } cursorid:61603998663 keysExamined:12145431 docsExamined:12145431 fromMultiPlanner:1 replanned:1 numYields:97720 nreturned:14 reslen:16772198 locks:{ Global: { acquireCount: { r: 97721 } }, Database: { acquireCount: { r: 97721 } }, Collection: { acquireCount: { r: 97721 } } }  protocol:op_msg 692537ms

查看此查詢的執行計劃,執行代碼

db.duoduologmodel.find({"Tags.SN": "QZ435698245"})
   .projection({})
   .sort({OPTime: -1})
   .explain()
   

主要反饋信息

    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "shqq_zp.duoduologmodel",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "Tags.SN" : {
                "$eq" : "QZ435698245"
            }
        },
        "winningPlan" : {
            "stage" : "SORT",
            "sortPattern" : {
                "OPTime" : -1
            },
            "inputStage" : {
                "stage" : "SORT_KEY_GENERATOR",
                "inputStage" : {
                    "stage" : "FETCH",
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "Tags.SN" : 1
                        },
                        "indexName" : "idx_TSN",
                        "isMultiKey" : true,
                        "multiKeyPaths" : {
                            "Tags.SN" : [
                                "Tags"
                            ]
                        },
                        "isUnique" : false,
                        "isSparse" : true,
                        "isPartial" : false,
                        "indexVersion" : 2,
                        "direction" : "forward",
                        "indexBounds" : {
                            "Tags.SN" : [
                                "[\"QZ435698245\", \"QZ435698245\"]"
                            ]
                        }
                    }
                }
            }
        },
        "rejectedPlans" : [
            {
                "stage" : "FETCH",
                "filter" : {
                    "Tags.SN" : {
                        "$eq" : "QZ435698245"
                    }
                },
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "OPTime" : 1
                    },
                    "indexName" : "idx_OPTime",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "OPTime" : [ ]
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "backward",
                    "indexBounds" : {
                        "OPTime" : [
                            "[MaxKey, MinKey]"
                        ]
                    }
                }
            }
        ]
    }

假如不用排序,刪除 .sort({OperationTime: -1}),其執行計劃

"queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "shqq_zp.duoduologmodel",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "Tags.SN" : {
                "$eq" : "QZ435698245"
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "Tags.SN" : 1
                },
                "indexName" : "idx_TSN",
                "isMultiKey" : true,
                "multiKeyPaths" : {
                    "Tags.SN" : [
                        "Tags"
                    ]
                },
                "isUnique" : false,
                "isSparse" : true,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "Tags.SN" : [
                        "[\"QZ435698245\", \"QZ435698245\"]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    }

此時,執行查詢確實變快了很多,在2S以內執行完畢。

 

刪除OPTime欄位索引後的執行計劃

    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "shqq_zp.duoduologmodel",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "Tags.SN" : {
                "$eq" : "QZ435698245"
            }
        },
        "winningPlan" : {
            "stage" : "SORT",
            "sortPattern" : {
                "OPTime" : -1
            },
            "inputStage" : {
                "stage" : "SORT_KEY_GENERATOR",
                "inputStage" : {
                    "stage" : "FETCH",
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "Tags.SN" : 1
                        },
                        "indexName" : "idx_TSN",
                        "isMultiKey" : true,
                        "multiKeyPaths" : {
                            "Tags.SN" : [
                                "Tags"
                            ]
                        },
                        "isUnique" : false,
                        "isSparse" : true,
                        "isPartial" : false,
                        "indexVersion" : 2,
                        "direction" : "forward",
                        "indexBounds" : {
                            "Tags.SN" : [
                                "[\"QZ435698245\", \"QZ435698245\"]"
                            ]
                        }
                    }
                }
            }
        },
        "rejectedPlans" : [ ]
    }

 

刪除這個索引後,查看報錯

 {
    "message" : "Executor error during find command :: caused by :: Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit.",
    "OPTime" : "Timestamp(1565681389, 1)",
    "ok" : 0,
    "code" : 96,
    "codeName" : "OperationFailed",
    "$clusterTime" : {
        "clusterTime" : "Timestamp(1565681389, 1)",
        "signature" : {
            "hash" : "vODWe0BCzyihrRVM08wPSFIMvo0=",
            "keyId" : "6658116868433248258"
        }
    },
    "name" : "MongoError"
}

原因比較明確:Sort operation used more than the maximum 33554432 bytes of RAM.,33554432 bytes算下來正好是32Mb,而Mongodb的sort操作是把數據拿到記憶體中再進行排序的,為了節約記憶體,預設給sort操作限制了最大記憶體為32Mb,當數據量越來越大直到超過32Mb的時候就自然拋出異常了!

因這個查看功能執行不多,併發不高。將系統排序記憶體由預設的32M調整到64M。

(此操作需謹慎,一般不建議修改,需要結合業務的使用情況,比如併發,數據量的大小;應優先考慮通過調整索引或集合的設計、甚至前端的設計來實現優化。)

db.adminCommand({setParameter:1, internalQueryExecMaxBlockingSortBytes:64554432})

(註意;這個設置在重啟MongoDB服務就會失效,重新變成預設的32M了)。

 

再次執行查詢查看,不再報錯。並且快速返回結果(2S)

因為還有其他需求,會根據OPTime欄位查看,重新添加這個索引。

再次執行,也可以比較迅速的返回結果(2S)。

 

從以上分析我們可以推斷;

(1)explain()查看的執行計劃,有時候還是有偏差的。

(2)Sort排序情況會影響索引的選擇。即當internalQueryExecMaxBlockingSortBytes不足以支持先查詢(by tag.sn)後排序(by optime)時,系統自動選擇了一個已排序好的索引(by optime),進行查看。

 

知識補充:

    queryPlanner.namespace:該值返回的是該query所查詢的表;

    queryPlanner.indexFilterSet:針對該query是否有indexfilter;

    queryPlanner.winningPlan:查詢優化器針對該query所返回的最優執行計劃的詳細內容;

    queryPlanner.rejectedPlans:其他執行計劃(非最優而被查詢優化器reject的)的詳細返回。

 

 

本文版權歸作者所有,未經作者同意不得轉載,謝謝配合!!!

本文版權歸作者所有,未經作者同意不得轉載,謝謝配合!!!

本文版權歸作者所有,未經作者同意不得轉載,謝謝配合!!!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有時候,當電腦有兩個網卡時;一個網卡 連接免費網路,一個網卡連接收費網路。這樣當你想使用免費網路與遠程伺服器建立連接,使用諸如scp命令或者 ssh 隧道之類傳輸大文件。這時候你需要指定特定的特定的網卡來建立連接了。 ssh 中 有一個選項可以綁定特定的interface 我們使用 man ssh ...
  • 1、Centos7下載 http://isoredirect.centos.org/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1810.iso 2、推薦設置VM NAT模式 3、VM安裝Centos7,適用推薦安裝即可。 4、安裝完畢後首先進行一些常用 ...
  • 在有大量圖片的頁面中,為了避免頁面載入完圖片還未載入完成,我們通常會使用js的圖片預載入。 這是一個預載入的demo: 首先把圖片放入到一個類名為imgSrcArr的變數當中: var imgSrcArr = [ ‘./imgs/01.png’, ‘./imgs/02.png’, ‘./imgs/0 ...
  • 最近,一臺虛擬機是從外網下載的,然後導入本地測試環境使用。 發現一個奇怪的問題:修改了 /etc/sysconfig/network-scripts/ifcfg-eth0 保存後, 重啟網路服務( systemctl restart network)是有效的。但是重啟系統之後就失效了。 ifcfg- ...
  • iotop的簡介: iotop是一款開源、免費的用來監控磁碟I/O使用狀況的類似top命令的工具,iotop可以監控進程的I/O信息。它是Python語言編寫的,與iostat工具比較,iostat是系統級別的IO監控,而iotop是進程級別IO監控。目前最新的版本為iotop 0.6。其官方網址h... ...
  • CPU執行的也不只是一條指令,一般一個程式包含很多條指令 因為有if…else、for這樣的條件和迴圈存在,這些指令也不會一路平直執行下去。 一個電腦程式是怎麼被分解成一條條指令來執行的呢 1 CPU如何執行指令 CPU里差不多幾百億個晶體管 實際上,一條條電腦指令執行起來非常複雜 好在CPU在 ...
  • 一、查看Oracle系統 1、Oracle資料庫服務 資料庫安裝完成後,在Windows操作系統環境下,Oracle資料庫伺服器以系統服務的方式運行。可以通過打開【控制面板】視窗,雙擊【管理工具】圖標,打開【管理工具】視窗,雙擊【服務】圖標,打開【服務】視窗,過程如下圖: 圖1 圖2 圖3 在圖3中 ...
  • 轉載、節選於 https://dev.mysql.com/doc/refman/8.0/en/innodb-in-memory-structures.html InnoDB Architecture The following diagram shows in-memory and on-disk ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...