【后端高階面經:MongoDB篇】40、怎么優化MongoDB的查詢性能?

在這里插入圖片描述

一、索引優化:構建高效查詢的基石

(一)索引類型與適用場景

1. 五大核心索引類型
索引類型適用場景示例代碼性能影響
單字段索引單條件查詢(如用戶ID、狀態字段)db.users.createIndex({ user_id: 1 })
復合索引多條件組合查詢/排序(如狀態+時間)db.orders.createIndex({ status: 1, time: -1 })
多鍵索引數組字段查詢(如標簽、商品規格)db.products.createIndex({ specs.size: 1 })
文本索引全文搜索(如文章內容、評論)db.articles.createIndex({ content: "text" })
哈希索引分片鍵/等值查詢(需均勻分布數據)sh.shardCollection("data", { _id: "hashed" })
2. 復合索引設計黃金法則(ESR原則)
  • E(Equality等值查詢):優先放置等值查詢字段(如user_id
  • S(Sort排序):其次放置排序字段(如create_time
  • R(Range范圍查詢):最后放置范圍查詢字段(如price
    示例
// 查詢:status=paid + 按create_time降序 + price>100
db.orders.createIndex({ status: 1, create_time: -1, price: 1 })

(二)覆蓋索引與避免回表

1. 覆蓋索引原理
  • 定義:索引包含查詢所需的所有字段,無需訪問文檔數據
  • 優勢:減少磁盤I/O,提升查詢速度
    示例
// 創建覆蓋索引(包含status、create_time、amount)
db.orders.createIndex({ status: 1, create_time: -1 }, { amount: 1 })// 使用覆蓋索引查詢
db.orders.find({ status: "paid" }, { create_time: 1, amount: 1 } // 字段均在索引中
).hint("status_1_create_time_-1") // 強制使用索引
2. 回表優化對比
操作覆蓋索引(命中)非覆蓋索引(回表)
掃描類型索引掃描(IXSCAN)索引掃描+文檔掃描(COLLSCAN)
內存占用
示例延遲20ms120ms

二、查詢模式優化:減少數據掃描量

(一)規避全集合掃描

1. 低效操作符優化
反例(全掃描)正例(索引友好)性能提升
db.users.find({ email: /@gmail$/ })db.users.find({ email: { $regex: "^user" } })10倍+
db.orders.find({ qty: { $exists: true } })db.orders.createIndex({ qty: 1 }); db.orders.find({ qty: { $gt: 0 } })5倍+
2. 前綴匹配優化
// 反例:后綴匹配(無法使用索引)
db.users.find({ email: /@gmail.com$/ })// 正例:前綴匹配(可使用索引)
db.users.find({ email: /^admin/ })

(二)分頁查詢性能優化

1. 傳統分頁(skip+limit)的缺陷
  • 問題skip(n)會掃描前n條文檔,深度分頁時性能驟降
  • 示例db.orders.find().skip(100000).limit(10)需掃描100010條文檔
2. 游標分頁(基于排序字段)
// 按時間戳排序,記錄最后一條的時間戳
const lastTime = new Date("2023-10-01T00:00:00");// 下一頁查詢
db.orders.find({ create_time: { $lt: lastTime } 
}).sort({ create_time: -1 }).limit(10)
3. 鍵值分頁(基于_id)
// 記錄最后一條的_id
const lastId = ObjectId("6401f015f9b1b4f2a1c000001");// 下一頁查詢
db.orders.find({ _id: { $gt: lastId } 
}).sort({ _id: 1 }).limit(10)

(三)聚合管道優化

1. 管道階段順序優化
  • 原則:盡早過濾數據($match前置),減少后續階段處理量
    示例
// 反例:先分組再過濾(處理全量數據)
db.sales.aggregate([{ $group: { _id: "$product", total: { $sum: "$amount" } } },{ $match: { total: { $gt: 1000 } } }
])// 正例:先過濾再分組(僅處理符合條件的數據)
db.sales.aggregate([{ $match: { amount: { $gt: 10 } } }, // 前置過濾{ $group: { _id: "$product", total: { $sum: "$amount" } } }
])
2. 使用索引加速聚合
// 創建復合索引
db.sales.createIndex({ product: 1, amount: 1 })// 聚合時使用索引
db.sales.aggregate([{ $match: { product: "P001" } },{ $group: { _id: null, total: { $sum: "$amount" } } }
]).hint({ product: 1, amount: 1 })

三、分片集群優化:水平擴展查詢能力

(一)分片鍵選擇策略

1. 三大分片鍵類型對比
類型適用場景示例字段數據分布查詢性能
哈希分片高并發寫、均勻分布user_id、order_id均衡等值查詢高效
范圍分片時間序列、范圍查詢create_time、date可能熱點范圍查詢高效
復合分片混合查詢需求region+time較均衡組合查詢高效
2. 分片鍵設計禁忌
  • 避免低基數字段:如status(僅少數取值,導致數據傾斜)
  • 避免頻繁更新字段:如last_login(影響分片穩定性)
3. 分片集群部署示例
客戶端
mongos路由節點
Shard1副本集
Shard2副本集
主節點
從節點
主節點
從節點

(二)分片集群查詢流程

  1. 路由階段:mongos解析查詢,確定目標Shard
  2. 并行查詢:各Shard執行本地查詢(利用本地索引)
  3. 結果合并:mongos聚合各Shard結果,返回客戶端

優化點

  • 確保分片鍵包含在查詢條件中,避免全分片掃描
  • 為每個Shard的本地集合創建復合索引

四、硬件與配置調優:釋放底層性能

(一)內存配置最佳實踐

1. WiredTiger引擎參數
# mongod.conf配置示例
storage:wiredTiger:engineConfig:cacheSizeGB: 32  # 建議為物理內存的50%-80%,確保索引常駐內存collectionConfig:blockSize: 4096  # 減小塊大小,提升小文檔查詢效率
2. 內存使用監控
# 查看內存使用情況
db.serverStatus().mem
# 關鍵指標:
# - "resident":常駐內存大小(理想值接近cacheSizeGB)
# - "virtual":虛擬內存使用(應避免過高,否則觸發swap)

(二)磁盤與文件系統優化

1. 存儲介質選擇
類型隨機IOPS延遲適用場景成本
NVMe SSD20000+<1ms主節點、熱數據
SATA SSD5000+1-5ms從節點、溫數據
HDD200+10-20ms冷數據、備份
2. 文件系統配置
# 禁用透明大頁(THP)提升性能
echo never > /sys/kernel/mm/transparent_hugepage/enabled# XFS文件系統掛載選項
mount -t xfs -o noatime,nodiratime /dev/nvme0n1p1 /data/mongodb

(三)讀寫關注調優

1. 寫入關注(Write Concern)
場景配置延遲(ms)數據可靠性
日志寫入{ w: 1 }1-5弱一致
訂單創建{ w: majority }5-20強一致
資產變更{ w: majority, j: true }20-50最強一致
2. 讀取關注(Read Preference)
// 從Secondary節點讀取(讀擴展)
db.orders.find().readPreference("secondaryPreferred")// 從最近的節點讀取(全球部署)
db.orders.find().readPreference("nearest")

五、監控與分析:定位性能瓶頸

(一)執行計劃分析(Explain)

1. 核心指標解析
const plan = db.orders.find({ status: "paid" }).explain("executionStats")
指標含義優化目標
executionTimeMillis總執行時間<100ms
totalDocsExamined掃描的文檔數盡可能接近查詢結果數
nReturned返回的文檔數等于查詢結果數
stage執行階段(如IXSCAN/COLLSCAN)確保為IXSCAN(索引掃描)
2. 優化示例
// 優化前:COLLSCAN(全表掃描)
db.orders.find({ customer: "Alice" }).explain()// 優化后:IXSCAN(索引掃描)
db.orders.createIndex({ customer: 1 })
db.orders.find({ customer: "Alice" }).explain()

(二)慢查詢日志

1. 配置慢查詢監控
# mongod.conf
operationProfiling:mode: slowOpslowOpThresholdMs: 100  # 慢查詢閾值(毫秒)slowOpSampleRate: 1.0   # 采樣率(1.0表示記錄所有慢查詢)
2. 分析慢查詢日志
# 查看慢查詢統計
db.system.profile.find({ts: { $gt: new Date("2023-10-01") },millis: { $gt: 100 }
}).sort({ millis: -1 })

六、實戰案例:電商訂單系統性能優化

(一)場景描述

  • 數據規模:訂單量10億條,日均新增100萬條
  • 高頻查詢
    1. 按用戶ID查詢最近100條訂單(user_id + create_time
    2. 統計已支付訂單總量(status=paid
    3. 按日期范圍查詢訂單金額分布(create_time + amount

(二)優化前性能指標

查詢類型平均延遲掃描文檔數索引使用情況
用戶訂單查詢800ms10000+未命中索引
支付統計1200ms全表掃描無索引
范圍查詢1500ms500萬+部分索引命中

(三)優化方案實施

1. 索引優化
// 用戶訂單查詢索引(覆蓋查詢)
db.orders.createIndex({ user_id: 1, create_time: -1 
}, { amount: 1, status: 1 
})// 支付統計索引
db.orders.createIndex({ status: 1 })// 范圍查詢索引
db.orders.createIndex({ create_time: 1, amount: 1 })
2. 分片策略
// 哈希分片(用戶ID均勻分布)
sh.shardCollection("ecommerce.orders", { user_id: "hashed" })
3. 查詢改寫
// 優化后用戶訂單查詢(游標分頁)
const lastTime = new Date("2023-10-05T00:00:00");
db.orders.find({user_id: "U123",create_time: { $lt: lastTime }
}).sort({ create_time: -1 })
.limit(100)
.hint("user_id_1_create_time_-1")

(四)優化后性能指標

查詢類型平均延遲掃描文檔數索引使用情況
用戶訂單查詢65ms100條覆蓋索引命中
支付統計45ms1200條單字段索引命中
范圍查詢180ms5000條復合索引命中

七、面試核心考點與應答策略

(一)基礎問題

  1. Q:如何判斷查詢是否使用了索引?
    A:使用explain()分析執行計劃,若stageIXSCAN則命中索引;查看totalDocsExamined是否接近查詢結果數,若遠大于則可能全表掃描。

  2. Q:復合索引的字段順序如何影響性能?
    A:遵循ESR原則:等值查詢字段→排序字段→范圍查詢字段。例如,查詢status=paid AND time>2023-01-01 AND sort by amount,索引應為{status:1, time:1, amount:1}

(二)進階問題

  1. Q:深度分頁為什么慢?如何優化?
    A

    • 原因:skip(n)需掃描前n條文檔,時間復雜度O(n)
    • 優化:
      1. 使用search_after基于排序字段分頁
      2. 記錄最后一條的排序值,通過范圍查詢替代skip
      db.orders.find({ create_time: { $lt: last_time } }).sort({ create_time: -1 }).limit(10)
      
  2. Q:分片集群中如何避免數據傾斜?
    A

    • 選擇高基數分片鍵(如用戶ID哈希)
    • 監控塊分布,通過sh.rebalanceShard()手動遷移熱點塊
    • 啟用自動平衡器(默認開啟),調整塊大小(如256MB)

(三)架構設計問題

Q:設計一個千萬級數據的查詢系統,如何優化MongoDB性能?
回答思路

  1. 索引層
    • 為高頻查詢字段創建復合索引,確保覆蓋查詢
    • 使用文本索引優化全文搜索場景
  2. 集群層
    • 分片集群部署,哈希分片鍵均勻分布數據
    • 獨立部署mongos節點,橫向擴展路由能力
  3. 存儲層
    • 使用SSD存儲熱數據,HDD存儲冷數據
    • 調整WiredTiger緩存大小,確保索引常駐內存
  4. 查詢層
    • 避免skip深度分頁,改用游標分頁
    • 聚合查詢前置過濾條件,減少數據處理量

八、性能優化的黃金法則

(一)索引優先原則

  • 80%的性能問題可通過優化索引解決,優先分析查詢是否命中索引
  • 定期清理冗余索引(db.xxx.getIndexes()),減少寫入開銷

(二)數據分片原則

  • 單集合數據量超過1TB時啟用分片,分片鍵選擇需平衡查詢與分布
  • 每個Shard節點數≥3(1主2從),確保高可用

(三)監控驅動原則

  • 建立常態化監控:索引使用率、慢查詢頻率、分片負載均衡
  • 使用mongostat實時監控節點狀態,mongotop分析讀寫分布

(四)漸進優化原則

  1. 分析:通過explain()和慢查詢日志定位瓶頸
  2. 驗證:小范圍測試優化方案(如灰度環境)
  3. 部署:滾動更新索引或分片配置,避免服務中斷
  4. 監控:對比優化前后性能指標,持續迭代

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/82580.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/82580.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/82580.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux wget 常用命令詳解

目錄 1.1 工具定位 基礎下載示例 二、高效下載參數詳解 2.1 下載控制類 2.2 文件管理類 2.3 網絡優化類 三、高級應用場景 3.1 遞歸下載與整站鏡像 3.2 自動化下載實踐 3.3 安全下載配置 四、參數速查手冊 4.1 常用參數匯總 1.1 工具定位 基礎下載語法 wget [選項…

Pytorch中文文本分類

本文為&#x1f517;365天深度學習訓練營內部文章 原作者&#xff1a;K同學啊 將對中文文本進行分類&#xff0c;示例如下&#xff1a; 文本分類流程圖 1.加載數據 import time import pandas as pd import torch from torch.utils.data import DataLoader, random_split impo…

13.「極簡」扣子(coze)教程 | 小程序UI設計進階(三)讓界面動起來,實操講透“聚焦”事件

前一期大師兄介紹了扣子平臺組件的兩種狀態“禁用”和“加載”。這兩種方法使控件可以通過簡單設置表示出更多的運行狀態。今天大師兄將詳細介紹控件的一種事件“聚焦”。 扣子&#xff08;coze&#xff09;編程 「極簡」扣子(coze)教程 | 小程序UI設計進階 II&#xff01;讓…

劍指offer11_矩陣中的路徑

矩陣中的路徑 請設計一個函數&#xff0c;用來判斷在一個矩陣中是否存在一條路徑包含的字符按訪問順序連在一起恰好為給定字符串。 路徑可以從矩陣中的任意一個格子開始&#xff0c;每一步可以在矩陣中向左&#xff0c;向右&#xff0c;向上&#xff0c;向下移動一個格子。 如…

騰訊2025年校招筆試真題手撕(三)

一、題目 今天正在進行賽車車隊選拔&#xff0c;每一輛賽車都有一個不可以改變的速度。現在需要選取速度差距在10以內的車隊&#xff08;車隊中速度的最大值減去最小值不大于10&#xff09;&#xff0c;用于迎賓。車隊的選拔按照的是人越多越好的原則&#xff0c;給出n輛車的速…

《三維點如何映射到圖像像素?——相機投影模型詳解》

引言 以三維投影介紹大多比較分散&#xff0c;不少小伙伴再面對諸多的坐標系轉換中容易弄混&#xff0c;特別是再寫代碼的時候可能搞錯&#xff0c;所有這篇文章幫大家完整的梳理3D視覺中的投影變換的全流程&#xff0c;一文弄清楚這個過程&#xff0c;幫助大家搞清坐標系轉換…

Ini配置文件讀寫,增加備注功能

1.增加備注項寫入 例: #節點備注 [A] #項備注 bbb1 ccc2 [B] bbb1 IniConfig2 ic new IniConfig2(); //首次寫入 if (!ic.CanRead()) { ic.AddSectionReMarke("A", "節點備注"); ic.SetValue("A&qu…

OpenHarmony 5.0中狀態欄添加以太網狀態欄圖標以及功能實現

目錄 1.前置條件 2.方案 1.前置條件 首先以太網接口是有問題的,如下按照如下流程將以太網接口進行修復 OpenHarmony 以太網卡熱插拔事件接口無效-CSDN博客 然后上述的接口可以了就可以通過這個接口獲取以太網是否連接狀態 要注意wifi連接的干擾和預置虛擬網口干擾 2.方案…

RNN GRU LSTM 模型理解

一、RNN 1. 在RNN中&#xff0c; 2. RNN是一個序列模型&#xff0c;與非序列模型不同&#xff0c;序列中的元素互相影響&#xff1a; 是由 計算得來的。 在前向傳播中&#xff1a; 用于計算 和 用于計算 和 因此&#xff0c;當進行反向鏈式法則求導時候&#xf…

多路徑傳輸(比如 MPTCP)控制實時突發

實時突發很難控制&#xff0c;因為 “實時” 和 “突發” 相互斥。實時要求避免排隊&#xff0c;而突發必然要排隊&#xff0c;最終的解決方案都指向找一個公說公有理&#xff0c;婆說婆有理的中間點&#xff0c;這并沒解決問題&#xff0c;只是權衡了問題。 這種局部解決問題的…

函數式編程思想詳解

函數式編程思想詳解 1. 核心概念 不可變數據 (Immutable Data) 數據一旦創建&#xff0c;不可修改。任何操作均生成新數據&#xff0c;而非修改原數據。 優點&#xff1a;避免副作用&#xff0c;提升并發安全&#xff0c;簡化調試。 Java實現&#xff1a;使用final字段、不可變…

iOS 主要版本發布歷史

截至 2025 年 5 月&#xff0c;iOS 的最新正式版本是 iOS 18&#xff0c;于 2024 年 9 月 16 日 正式發布。此前的 iOS 17 于 2023 年 9 月 18 日 發布&#xff0c;并在 2024 年被 iOS 18 取代。(維基百科) &#x1f4f1; iOS 主要版本發布歷史 以下是 iOS 各主要版本的發布日…

矩陣詳解:線性代數在AI大模型中的核心支柱

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家、CSDN平臺優質創作者&#xff0c;高級開發工程師&#xff0c;數學專業&#xff0c;10年以上C/C, C#, Java等多種編程語言開發經驗&#xff0c;擁有高級工程師證書&#xff1b;擅長C/C、C#等開發語言&#xff0c;熟悉Java常用開…

基于51單片機和8X8點陣屏、獨立按鍵的飛行躲閃類小游戲

目錄 系列文章目錄前言一、效果展示二、原理分析三、各模塊代碼1、8X8點陣屏2、獨立按鍵3、定時器04、定時器1 四、主函數總結 系列文章目錄 前言 用的是普中A2開發板。 【單片機】STC89C52RC 【頻率】12T11.0592MHz 【外設】8X8點陣屏、獨立按鍵 效果查看/操作演示&#xff…

區塊鏈可投會議CCF C--APSEC 2025 截止7.13 附錄用率

Conference&#xff1a;32nd Asia-Pacific Software Engineering Conference (APSEC 2025) CCF level&#xff1a;CCF C Categories&#xff1a;軟件工程/系統軟件/程序設計語言 Year&#xff1a;2025 Conference time&#xff1a;December 2-5, 2025 in Macao SAR, China …

pdf圖片導出(Visio\Origin\PPT)

一、Visio 導入pdf格式圖片 1. 設計->大小&#xff0c;適應繪圖。 2. 文件->導出&#xff0c;導出為pdf格式。 上面兩部即可得到只包含圖的部分的pdf格式。 如果出現的有默認白邊&#xff0c;可以通過以下方式設置&#xff1a; 1. 文件->選項->自定義功能區->…

vector的實現

介紹 1. 本質與存儲結構 動態數組實現&#xff1a;vector 本質是動態分配的數組&#xff0c;采用連續內存空間存儲元素&#xff0c;支持下標訪問&#xff08;如 vec[i]&#xff09;&#xff0c;訪問效率與普通數組一致&#xff08;時間復雜度 O (1)&#xff09;。動態擴容機制&…

【Linux筆記】防火墻firewall與相關實驗(iptables、firewall-cmd、firewalld)

一、概念 1、防火墻firewall Linux 防火墻用于控制進出系統的網絡流量&#xff0c;保護系統免受未授權訪問。常見的防火墻工具包括 iptables、nftables、UFW 和 firewalld。 防火墻類型 包過濾防火墻&#xff1a;基于網絡層&#xff08;IP、端口、協議&#xff09;過濾流量&a…

el-date-picker 前端時間范圍選擇器

控制臺參數&#xff1a; 前端代碼&#xff1a;用數組去接受&#xff0c;同時用 value-format"YYYY-MM-DD" 格式化值為&#xff1a;年月日格式 <!-- 查詢區域 --><transition name"fade"><div class"search" v-show"showSe…

在 macOS 上安裝 jenv 管理 JDK 版本

在 macOS 上安裝 jenv 并管理 JDK 版本 在開發 Java 應用程序時&#xff0c;你可能需要在不同的項目中使用不同版本的 JDK。手動切換 JDK 版本可能會很繁瑣&#xff0c;但幸運的是&#xff0c;有一個工具可以簡化這個過程&#xff1a;jenv。jenv 是一個流行的 Java 版本管理工…