MongoDB之索引

常用命令

查看表的索引

db.<table>.getIndexes()

查看表索引的大小

db.<table>.totalIndexSize()

重建索引

db.<table>.reIndex()

删除索引

db.COLLECTION_NAME.dropIndex("INDEX-NAME")
db.COLLECTION_NAME.dropIndexes()

_id 索引无法删除。

执行计划

参考:MongoDB干货系列2-MongoDB执行计划分析详解(1) | MongoDB中文社区MongoDB - 执行计划 - 听雨危楼 - 博客园

sql 复制代码
-- 千万测试数据
for(var i=1;i<10000000;i++){ db.indexDemo.insert({_id:i , num:'index:'+i ,address:'address:i%9999'})}

默认的查询计划 queryPlanner

sql 复制代码
-- 不使用索引
db.indexDemo.find({num:'index:99999'}).explain()

db.indexDemo.createIndex( { num: 1 } )
db.indexDemo.getIndexes()
db.indexDemo.dropIndex("num_1")
json 复制代码
{
        "explainVersion" : "1",
        "queryPlanner" : {
                "namespace" : "study.goods",                     【查询的表】
                "indexFilterSet" : false,                        【是否有indexfilter】
                "parsedQuery" : {                                【查询过滤条件】
                        "qty" : {
                                "$gt" : 50
                        }
                },
                "queryHash" : "3DC2392F",
                "planCacheKey" : "B7F8CFFA",
                "maxIndexedOrSolutionsReached" : false,
                "maxIndexedAndSolutionsReached" : false,
                "maxScansToExplodeReached" : false,
                "winningPlan" : {                                【最优执行计划详细内容】
                        "stage" : "FETCH",                       【FETCH:通过返回的index扫描。COLLSCAN:全表扫描】
                        "inputStage" : {                         【子stage】
                                "stage" : "IXSCAN",              【表示进行索引扫描】
                                "keyPattern" : {                 【扫描的索引内容】
                                        "qty" : 1
                                },
                                "indexName" : "qty_1",           【使用的索引名称】
                                "isMultiKey" : false,            【是否多列索引。索引建立在array上时是true】
                                "multiKeyPaths" : {
                                        "qty" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",         【查询的顺序:forward升序、backward降序】
                                "indexBounds" : {                【索引扫描范围,没有制定范围就是[MaxKey,MinKey]】
                                        "qty" : [
                                                "(50.0, inf.0]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [ ]                            【其它非最优的执行计划】
        },
        "command" : {
                "find" : "goods",
                "filter" : {
                        "qty" : {
                                "$gt" : 50
                        }
                },
                "sort" : {
                        "qty" : 1
                },
                "$db" : "study"
        },
        "serverInfo" : {                                         【服务器信息】
                "host" : "fe9b0d04fcbd",
                "port" : 27017,
                "version" : "5.0.5",
                "gitVersion" : "d65fd89df3fc039b5c55933c0f71d647a54510ae"
        },
        "serverParameters" : {
                "internalQueryFacetBufferSizeBytes" : 104857600,
                "internalQueryFacetMaxOutputDocSizeBytes" : 104857600,
                "internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600,
                "internalDocumentSourceGroupMaxMemoryBytes" : 104857600,
                "internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600,
                "internalQueryProhibitBlockingMergeOnMongoS" : 0,
                "internalQueryMaxAddToSetBytes" : 104857600,
                "internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600
        },
        "ok" : 1
}

附加执行状态 executionStats

sql 复制代码
-- 在默认的执行计划信息上,多了 executionStats 信息
db.indexDemo.find({num:'index:99999'}).explain("executionStats")
json 复制代码
{
  "explainVersion" : "1",
  "queryPlanner" : {...},
  "executionStats" : {
    "executionSuccess" : true,                     【是否执行成功】
    "nReturned" : 1,                               【匹配到的文档数】
    "executionTimeMillis" : 0,                     【选择和查询执行计划所需时间。毫秒】
    "totalKeysExamined" : 1,											 【扫描的索引条目数】
    "totalDocsExamined" : 1,                       【扫描文档数】
    "executionStages" : {                          【最优执行计划的完整信息】
      "stage" : "FETCH",                           【FETCH:根据索引结果去扫描文档】
      "nReturned" : 1,                             【stage=FETCH时,跟上面的nReturned一样】
      "executionTimeMillisEstimate" : 0,           【检索文档获得数据的时间】
      "works" : 2,                                 【执行查询阶段的各个工作以"单元"划分,这里表示工作单元数】
      "advanced" : 1,															 【返回到父阶段的结果数】
      "needTime" : 0,                              【中间结果返回给父级的工作循环次数】
      "needYield" : 0,
      "saveState" : 0,
      "restoreState" : 0,
      "isEOF" : 1,
      "docsExamined" : 1,
      "alreadyHasObj" : 0,
      "inputStage" : {...}
    }
  },
  "command" : {
    "find" : "indexDemo",
    "filter" : {
      "num" : "index:99999"
    },
    "$db" : "study"
  },
  "serverInfo" : {...},
  "serverParameters" : {
    "internalQueryFacetBufferSizeBytes" : 104857600,
    "internalQueryFacetMaxOutputDocSizeBytes" : 104857600,
    "internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600,
    "internalDocumentSourceGroupMaxMemoryBytes" : 104857600,
    "internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600,
    "internalQueryProhibitBlockingMergeOnMongoS" : 0,
    "internalQueryMaxAddToSetBytes" : 104857600,
    "internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600
  },
  "ok" : 1
}

关键参数

耗时 executionTimeMillis

  • executionStats.executionTimeMillis:整体查询时间。
  • executionStats.executionStages.executionTimeMillisEstimate:检索Document获得数据的时间
  • executionStats.executionStages.inputStage.executionTimeMillisEstimate:扫描文档 Index所用时间

扫描数 nReturned

  • nReturned:查询结果返回的条目数
  • totalKeysExamined:总索引扫描的条目数
  • totalDocsExamined :总索引扫描的条目数

扫描数越少越好,理想情况:

nReturned = totalKeysExamined = otalDocsExamined

stage 参数表

类型 描述
COLLSCAN 全表扫描
IXSCAN 索引扫描
FETCH 根据索引去检索指定document
SHARD_MERGE 将各分片的返回结果合并
SORT 在内存中进行了排序
LIMIT 限制返回数
SKIP 使用skip进行跳过
IDHACK 针对_id进行的查询
SHARDING_FILTER 通过mongos对分片数据进行查询
COUNT 利用db.coll.explain().count()之类进行count运算
TEXT 全文索引
PROJECTION 限定返回字段时候

返回最优与备选计划 allPlansExecution

db.indexDemo.find({num:'index:99999'}).explain("allPlansExecution")

慢查询分析

开启内置查询分析器

sql 复制代码
-- 开启内置查询分析器
db.setProfilingLevel([0,1,2],m)
0:不记录
1:记录超过阈值m的记录
2:记录所有读写操作

-- 例子
db.setProfilingLevel(1,100)
db.setProfilingLevel(2)

查看监听结果

db.system.profile.find().sort({millis:-1}).limit(3)

结果分析

慢查询常见于:

  • 应用设计不合理;
  • 数据模型不合理;
  • 硬件配置;
  • 缺少索引;

explain 分析是否跑索引

创建索引

测试数据

sql 复制代码
db.goods.insertMany( [
{ item: "canvas", qty: 100, size: { h: 28, w: 35.5, uom: "cm" }, status:
"A" },
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status:
"A" },
{ item: "mat", qty: 85, size: { h: 27.9, w: 35.5, uom: "cm" }, status:
"A" },
{ item: "mousepad", qty: 25, size: { h: 19, w: 22.85, uom: "cm" },
status: "P" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status:
"P" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status:
"D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status:
"D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" },
status: "A" },
{ item: "sketchbook", qty: 80, size: { h: 14, w: 21, uom: "cm" }, status:
"A" },
{ item: "sketch pad", qty: 95, size: { h: 22.85, w: 30.5, uom: "cm" },
status: "A" }
]);
db.inventory.insertMany([
{ _id: 1,item: "abc",stock: [{ size: "S", color: "red", quantity: 25 },{
size: "S", color: "blue", quantity: 10 },{ size: "M", color: "blue",
quantity: 50 }]},
{_id:2,item:"def",stock:[{size:"S",color:"blue",quantity:20},
{size:"M",color:"blue",quantity:5},{size:"M",color:"black",quantity:10},
{size:"L",color:"red",quantity:2}]},
{_id:3,item:"ijk",stock:[{size:"M",color:"blue",quantity:15},
{size:"L",color:"blue",quantity:100},{size:"L",color:"red",quantity:25}]}
])

单字段索引 Single Field

单列索引

db.<table>.createIndex(keys, options)

options:

  • 1:按照升序创建索引
  • -1:按照降序创建索引
sql 复制代码
db.indexDemo.find({num:"index:600"}).explain()

db.indexDemo.createIndex({num:1})

db.indexDemo.find({num:"index:600"}).explain()

给嵌入式字段创建索引

sql 复制代码
{
  "_id": ObjectId("570c04a4ad233577f97dc459"),
  "score": 1034,
  "location": { state: "NY", city: "New York" }
}

-- 创建索引
db.<table>.createIndex( { "location.state": 1 } )

-- 索引生效
db.<table>.find( { "location.state": "CA" } )
db.<table>.find( { "location.city": "Albany", "location.state": "NY" } )

给整个内嵌文档创建索引

sql 复制代码
{
  "_id": ObjectId("570c04a4ad233577f97dc459"),
  "score": 1034,
  "location": { state: "NY", city: "New York" }
}

-- 创建索引
db.<table>.createIndex( { location: 1 } )

-- 索引生效
db.records.find( { location: { city: "New York", state: "NY" } } )

复合索引 Compound Index

db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )

⚠️不能创建具有hashed索引类型的复合索引。如果试图创建包含hashed索引字段的复合索引,将收到一个错误。

sql 复制代码
{
 "_id": ObjectId(...),
 "item": "Banana",
 "category": ["food", "produce", "grocery"],
 "location": "4th Street Store",
 "stock": 4,
 "type": "cases"
}

-- 创建索引
db.<table>.createIndex( { "item": 1, "stock": 1 } )

-- 索引生效
db.<table>.find( { item: "Banana" } )
db.<table>.find( { item: "Banana", stock: { $gt: 5 } } )

⚠️特别注意字段的顺序与排序。复合索引遵循最左匹配原则。

多键索引 Multikey indexes

支持对数组中每个元素创建索引。元素类型:string、number、 nested documents(嵌套文档) 。

number 类型数组

sql 复制代码
db.inventory.remove({})
db.inventory.insertMany([
  { _id: 5, type: "food", item: "aaa", ratings: [ 5, 8, 9 ] },
  { _id: 6, type: "food", item: "bbb", ratings: [ 5, 9 ] },
  { _id: 7, type: "food", item: "ccc", ratings: [ 9, 5, 8 ] },
  { _id: 8, type: "food", item: "ddd", ratings: [ 9, 5 ] },
  { _id: 9, type: "food", item: "eee", ratings: [ 5, 9, 5 ] }
])
-- 没加索引
db.inventory.find({ratings:[5,9]}).explain("executionStats")
-- 给数组加索引
db.inventory.createIndex( { ratings: 1 } )

嵌套文档 类型数组

单列索引
sql 复制代码
db.inventory.dropIndexes()

db.inventory.insertMany([
  { type: "food", item: "aaa", ratings: [ 5, 8, 9 ], size:[{w:10,h:165},{w:9,h:158}] },
  {type: "food", item: "aaa", ratings: [ 5, 8, 9 ], size:[{w:11,h:175},{w:7,h:142}] },
  {type: "food", item: "aaa", ratings: [ 5, 8, 9 ], size:[{w:15,h:163},{w:8,h:157}] }
])
-- 无索引查询
db.inventory.find({"size.w":10}).explain("executionStats")
-- 添加索引
db.inventory.createIndex({"size.w":1})
复合索引
sql 复制代码
-- 添加复合索引
db.inventory.createIndex( {"size.w":1,"size.h":2})
-- 查询
db.inventory.find({"size.w":10}).explain("executionStats")
db.inventory.find({"size.w":10},{"size.h":10}).explain("executionStats")

地理空间索引 Geospatial Index

  • 2dsphere索引,用于存储和查找球面上的点
  • 2d索引,用于存储和查找平面上的点
json 复制代码
db.company.insert(
	{loc : { type: "Point", coordinates: [ 116.482451, 39.914176 ] },name:"来广营地铁站-叶青北园",category : "Parks"}
)
# 2dsphere 或者 2d 。可以建立组合索引。
db.company.ensureIndex( { loc : "2dsphere" } )


db.company.find({
  "loc" : {
    "$geoWithin" : {
      "$center":[[116.482451,39.914176],0.05]
    }
  }
})

全文索引 Text Index

一个集合最多一个全文索引,可以覆盖多个字段。中文分词支持不佳(推荐ES)。

json 复制代码
db.<table>.createIndex({"fieldA": "text"})

db.<table>.createIndex(
   {
     fieldA: "text",
     fieldB: "text"
   }
 )


db.store.insert([
{ _id: 1, name: "Java Hut", description: "Coffee and cakes" },
{ _id: 2, name: "Burger Buns", description: "Gourmet hamburgers" },
{ _id: 3, name: "Coffee Shop", description: "Just coffee" },
{ _id: 4, name: "Clothes Clothes Clothes", description: "Discountclothing" },
{ _id: 5, name: "Java Shopping", description: "Indonesian goods" }
])


db.store.createIndex( { name: "text", description: "text" } )


db.store.find( { $text: { $search: "java coffee shop" } } )
{ "_id" : 3, "name" : "Coffee Shop", "description" : "Just coffee" }
{ "_id" : 1, "name" : "Java Hut", "description" : "Coffee and cakes" }
{ "_id" : 5, "name" : "Java Shopping", "description" : "Indonesian goods" }

哈希索引 Hashed Index

只用于等值查询。

json 复制代码
db.<table>.createIndex({"字段": "hashed"})

MongoDB 索引底层数据结构

文档类型数据库使用 BSON 格式保存数据,比 RDBMS 存储更方便。RDBMS 适合用于多表之间的关联的场景,而 BSON 可以把关联的数据存在一起。

比如MySQL使用 RDBMS 格式,数据的关联性强,范围查询普遍。所以底层使用B+树。

MongoDB使用B树,通过索引能更快访问,但不适合范围查询。

B树特点

  • 多路搜索。
  • 节点存储既存储索引又存储数据。
  • 关键字在树中只出现一次。

跟B+树的区别:

  • 叶子节点之间的指向。
  • 数据保存的位置。
相关推荐
一 乐1 小时前
民宿|基于java的民宿推荐系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·源码
美林数据Tempodata3 小时前
大模型驱动数据分析革新:美林数据智能问数解决方案破局传统 BI 痛点
数据库·人工智能·数据分析·大模型·智能问数
野槐3 小时前
node.js连接mysql写接口(一)
数据库·mysql
Zzzone6834 小时前
PostgreSQL日常维护
数据库·postgresql
chxii4 小时前
1.13使用 Node.js 操作 SQLite
数据库·sqlite·node.js
冰刀画的圈4 小时前
修改Oracle编码
数据库·oracle
这个胖子不太裤4 小时前
Django(自用)
数据库·django·sqlite
麻辣清汤4 小时前
MySQL 索引类型及其必要性与优点
数据库·mysql
2501_915374355 小时前
Neo4j 图数据库安装教程(2024最新版)—— Windows / Linux / macOS 全平台指南
数据库·windows·neo4j
it-搬运工5 小时前
3.图数据Neo4j - CQL的使用
数据库·neo4j