程序员书籍笔记 程序员书籍笔记
  • JavaScript
  • HTML/CSS
  • PHP
  • Python
  • Go
  • 数据库
  • 容器
  • 微服务
  • 消息队列
  • 搜索引擎
  • 大数据
  • 鸟哥Linux私房菜
其他
  • 大数据
  • 深度学习
APP下载 (opens new window)
GitHub (opens new window)
  • JavaScript
  • HTML/CSS
  • PHP
  • Python
  • Go
  • 数据库
  • 容器
  • 微服务
  • 消息队列
  • 搜索引擎
  • 大数据
  • 鸟哥Linux私房菜
其他
  • 大数据
  • 深度学习
APP下载 (opens new window)
GitHub (opens new window)
  • 数据库

    • MySQL必知必会

    • MongoDB权威指南

      • 入门
      • 文档创建、更新、删除
      • 查询
      • 索引
        • 索引简介
          • 扩展索引
          • 索引内嵌文档的键
          • 为排序创建索引
          • 索引名称
        • 唯一索引
          • 消除重复
          • 复合唯一索引
        • 使用explain和hint
          • hint可以强制使用索引
        • 索引管理
          • 修改索引
        • 地理空间索引
      • 聚合
  • 容器

  • 微服务

  • 消息队列

  • 搜索引擎

  • 大数据

  • 框架和软件
  • 数据库
  • MongoDB权威指南
小游
2021-05-14

索引

# 索引简介

MongoDB的索引几乎与传统的关系型数据库索引一模一样,所以如果已经掌握了那些技巧,则可以跳过本节的语法说明。后面会有些索引的基础知识,但一定要记住这里涉及的只是冰山一角,绝大多数优化 MySQLOracle/ SQLite索引的技巧也同样适用于 MongoDB。

创建索引,比如我们给name创建一个索引

db.test.ensureIndex({"name":1})
/* 1 */
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1.0
}
1
2
3
4
5
6
7
8

对某个键创建的索引会加速对该键的查询。然而,对于其他查询可能没有帮助,即便是查询包含了被索引的键。例如,下面的査询就不会从先前建立索引中获得任何的性能提升。

db.test.find({"age":20})
1

这个时候数据库会对整个表进行扫描,所以我们最好就是给所有需要查询的字段加上索引。

索引的顺序问题

image-20210514194122315

image-20210514194135786

# 扩展索引

image-20210514194240021

这点数据看着还行,但是应用会有数百万的用户,每人每天有数十条状态更新。若是每条用户状态的索引值占用类似一页纸的磁盘空间,那么对于每次“最新状态”的查询,数据库都会将不同的页载入内存。若是站点太热门,内存放不下所有的索引,就会非常非常慢。

image-20210514194332668

# 索引内嵌文档的键

db.test.ensureIndex({"name.first":1})
1

# 为排序创建索引

随着集合的增长,需要针对查询中大量的排序做索引。如果对没有索引的键调用sort, MongoDB需要将所有数据提取到内存来排序。因此,可以做无索引排序是有个上限的,那就是不可能在内存里面做T级别数据的排序。一旦集合大到不能在内存中排序, MongoDB就会报错。

# 索引名称

集合中的每个索引都有一个学符串类型的名字,来唯一标识索引务器通过这名字来删除或者操作索引。默认情况下,索引名类似κename1_dir1_keyname2_keynamen_dirN这种形式,其中 keynameX代表索引的键,dixx代表索引的方向(1或者-1)。要是索引的键特别多,这样命名就略显愚笨了,不过还好可以通过 ensureIndex的选项来指定自定义的名字

db.test.ensureIndex({"name":1,"age":1},{"name":"test"})
/* 1 */
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 2,
    "numIndexesAfter" : 3,
    "ok" : 1.0
}
1
2
3
4
5
6
7
8

# 唯一索引

唯一索引可以确保集合的毎一个文档的指定键都有唯一值。例如,如果想保证文档的" username"键都有不一样的值,创建一个唯一索引就好了

db.test.ensureIndex({"name":1},{"unique":true})
1

定要牢记默认情况下, insert并不检查文档是否插入过了。所以,为了避免插入的文档中包含与唯一键重复的值,可能要用安全插入才能满足要求。这样,在插入这样的文档时会看到存在重复键错误的提示可能我们最熟悉的唯一索引就是对"id"的索引了,这个索引是在创建普通集合时同创建的。这个索引和普通唯一索引只有一点不同,就是不能删除

# 消除重复

当为已有的集合创建索引,可能有些值已经有重复了。若是真的发生这种情况,那索引的创建就是失败。有些时候,可能希望将所有包含重复值的文档都删掉dropυps选项就可以保留发现的第一个文档,而删除接下来的有重复值的文档

db.test.ensureIndex({"name":1},{"unique":true},"dropDups":true)
1

# 复合唯一索引

image-20210514195042312

# 使用explain和hint

explain是一个非常有用的工具,会帮助你获得查询方面诸多有用的信息。只要对游标调用该方法,就可以得到查询细节。 explain会返回一个文档,而不是游标本身,这是与多数游标方法不同之处。

db.test.find({}).explain()
/* 1 */
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.test",
        "indexFilterSet" : false,
        "parsedQuery" : {},
        "queryHash" : "8B3D4AB8",
        "planCacheKey" : "8B3D4AB8",
        "winningPlan" : {
            "stage" : "COLLSCAN",
            "direction" : "forward"
        },
        "rejectedPlans" : []
    },
    "serverInfo" : {
        "host" : "SC-201912061743",
        "port" : 27017,
        "version" : "4.2.8",
        "gitVersion" : "43d25964249164d76d5e04dd6cf38f6111e21f5f"
    },
    "ok" : 1.0
}
db.test.find({"name":"小游"}).explain()
/* 1 */
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.test",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "name" : {
                "$eq" : "小游"
            }
        },
        "queryHash" : "01AEE5EC",
        "planCacheKey" : "0BE5F32C",
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "name" : 1.0
                },
                "indexName" : "name_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "name" : []
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "name" : [ 
                        "[\"小游\", \"小游\"]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ 
            {
                "stage" : "FETCH",
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "name" : 1.0,
                        "age" : 1.0
                    },
                    "indexName" : "test",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "name" : [],
                        "age" : []
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "name" : [ 
                            "[\"小游\", \"小游\"]"
                        ],
                        "age" : [ 
                            "[MinKey, MaxKey]"
                        ]
                    }
                }
            }
        ]
    },
    "serverInfo" : {
        "host" : "SC-201912061743",
        "port" : 27017,
        "version" : "4.2.8",
        "gitVersion" : "43d25964249164d76d5e04dd6cf38f6111e21f5f"
    },
    "ok" : 1.0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

# hint可以强制使用索引

db.test.find({}).hint({"name":1})
/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "age" : 20,
    "like" : [ 
        "写代码", 
        "看番"
    ]
}

/* 2 */
{
    "_id" : ObjectId("609cd4df71a920015b3da7ac"),
    "name" : "张三",
    "age" : 100,
    "like" : []
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 索引管理

image-20210514195803505

# 修改索引

随着你的应用和你一起慢慢变老,你会发现数据或者查询已经发生了改变,原来的索引也不那么好用了。不过使用 ensureindex随时可以向现有集合添加新的索引

db.test.ensureIndex({"username":1},{"background":true})
1

使用 {"background":true} 可以使过程在后台完成,如果想删除索引,可以这样做:

db.runCommand({"dropIndexes":"test","index":"name
# 如果想删除所有索引,可以这样做
db.runCommand({"dropIndexes":"test","index":"*"})
/* 1 */
{
    "nIndexesWas" : 3,
    "msg" : "non-_id indexes dropped for collection",
    "ok" : 1.0
}
1
2
3
4
5
6
7
8
9

# 地理空间索引

还有一种查询变得越来越流行(尤其是随着移动设备的出现):找到离当前位置最近的N个场所。 MongoDB为坐标平面查询提供了专门的索引,称作地理空间索引。假设要找到给定经纬度坐标周围最近的咖啡馆,就需要创建一个专门的索引来提高这种查询的效率,这是因为这种查询需要搜索两个维度。地理空间索引同样可以由ensure Index来创建,只不过参数不是1或者-1,而是"2d"

db.test.ensureIndex({"gps":"2d"})
1

image-20210514200505926

image-20210514200617193

查询和插入可以按照下面这样的方式来实现

image-20210514200702528

这个用的不多,就不说了

编辑 (opens new window)
上次更新: 2021/05/14, 21:28:33
查询
聚合

← 查询 聚合→

Theme by Vdoing | Copyright © 2021-2021 小游
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式