索引基本操作
在没有索引时查询称为全表扫描,对于大集合来说全表扫描效率非常低。有了索引就可以避免全表扫描,数据库可以直接在索引中查找,找到条目后可以直接跳转到目标文档的位置,使得查找速度提高几个数量级。
创建索引
语法结构:
db.COLLECTION_NAME.ensureIndex(keys, [options])
keys
是要建立索引的参数列表,结构:{"k_name": 1}
,字典的键为文档字段名,字典的值决定排序方向(1-升序,-1-降序)options
为可选参数,表示建立索引的设置,可选参数有background、unique、name、dropDups、sparse等,后续逐一说明;
例如一个用户信息集合user,集合user的文档结构{"age":10, "name": "小明", “gender”:"男"}
- 创建单索引
db.user.ensureIndex({age:1})
- 创建复合索引
db.user.ensureIndex({age:1, "name":-1})
查看索引
所有数据库索引都存储在system.indexes集合中,这是一个保留集合。
- 查看集合索引的大小
db.COLLECTION_NAME.totalIndexSize()
- 查看集合的索引
db.COLLECTION_NAME.getIndexes()
例如查看集合user的索引,db.user.getIndexes()
,返回值如下:[ { "v" : 1, # 索引版本 "key" : { # 索引,注意索引的顺序 "_id" : 1 }, "ns" : "testDB.user", # 数据库命名.集合名 "name" : "_id_" # 索引名称,可以被当做标识符使用 }, { "v" : 1, "key" : { "age" : 1 }, "ns" : "newDB.sites", "name" : "age_1" } ]
索引名称
- 集合中每一个索引都有一个名称,用于唯一标识这个索引,索引名默认形式
keyname1_dir1_keyname2_dir2...
。 - 在创建索引时可以指定名称,例如
db.user.ensureIndex({age:1}, {"name":"age"})
。
删除索引
- 删除指定索引dropIndex()
db.COLLECTION_NAME.dropIndex("indexname")
例如db.user.dropIndex("age_1")
- 删除集合所有索引
dropIndexes()
db.COLLECTION_NAME.dropIndexes()
例如db.user.dropIndexes()
复合索引
单索引比较简单没啥好说的,下面主要说下复合索引。
复合索引就是一个建立在多个字段上的索引。当查询中有多个排序方向或查询条件中有多个键,复合索引就会非常有用。创建复合索引如db.user.ensureIndex({age:1, "name":1})
。
- 复合索引顺序很重要,使用不当会影响到查询效率。例如
db.user.ensureIndex({age:1, "name":1})
,age
字段是严格的升序排列,而username
仅在age
相同的条目中升序排序。在日常使用中需要根据实际情况合理使用索引,例如以下3中查询情况:db.user.find({"age":21}).sort({"name":1})
,单字段查询,且排序字段为索引的第二个字段已经有序,这类查询非常高效。db.user.find({"age":{"$gte":21,"$lte":30}}).sort({"name":1})
,单字段多值查询并且需要对name
排序,mongodb需要在内存中对查询结果排序后再返回,如果结果集合条目比较多,这个查询速度就会比较慢,甚至不能用,因为mongodb会拒绝对大小超过32MB的结果集合进行排序。db.user.find({"age":{"$gte":21,"$lte":30}}).sort({"name":1})
,这种情况,如果索引顺序调换,用索引{"name":1,"age":1}
就不需要在内存中对大量数据排序了,但这样的代价是需要扫描整个索引才能找出所有匹配的文档。
- 在实际应用中,
{"sortKey":1, "queryCriteria":1}
索引通常是很有用的,因为大多数程序在一次查询中只需要最前面的少数结果而不是所有满足条件的结果。另外,索引的本质是树,最小值在左叶子,最大值在右叶子上,当需要“大值”数据(树的右侧)的机会比较多时,MongoDB只需要在内存保留这棵数的右侧数据,类似这样的索引是右平衡的,例如{"_id"}
索引就是一个典型的右平衡索引。
使用复合索引注意事项
- 基于多键排序时,索引键方向的选择很重要,索引键的升序还是降序会影响到查询结果集是否需要在内存排序再返回。
- 使用覆盖索引,当一个索引包含了用户请求查询的所有字段,可以认为这个索引覆盖了本次查询;实际应用中,根据情况应优先使用覆盖索引,因为省去了查找到索引后去找对应文档。
- 隐式索引,例如
{age:1, "name":1}
这个复合索引的"age"
字段是被自动排序的了,和索引{"age":1}
一样,那么这个复合索引隐式包含了{"age":1}
。需要注意的是只有使用到索引最前的字段的查询,隐式索引才能发挥作用。
常见索引变种
创建索引时options
参数选择不同选项建立的索引会有不同行为,下面介绍下可选项unique、sparse。
唯一索引
唯一索引可以确保集合的每一文档的指定键都有唯一值,常见的"_id"
就是唯一索引。例如想保证文档的"name"
唯一,创建唯一索引即可。创建语句如下:
db.user.ensureIndex({"name":1}, {"unique":true})
创建唯一索引后,如果插入文档的"name"
字段重复会抛出异常。
复合索引也能创建唯一索引,例如
db.user.ensureIndex({"age":1, "name":1}, {"unique":true})
复合唯一索引的单个键的值可以相同,但是所有键的组合值必须是唯一的。
使用唯一索引需要注意的地方
- 一个文档如果没有索引字段对应的键,索引会将其作为null存储,如果创建的是唯一索引,就不能插入多个缺少该字段的文档了。
- 由索储桶存在大小限制(上限是1024字节),超出1kb大小的键不会受到唯一索引的约束。
- 对已有数据的集合建立唯一索引,如果希望直接删除重复值,创建索引时可以使用
"dropDups"
选项,例如db.user.ensureIndex({"name":1}, {"unique":true, "dropDups": true})
。友情提醒下该选项慎重使用。
稀疏索引
稀疏索引是指不需要将每个文档都作为索引条目,前面说到的唯一索引无法将多个缺少索引字段的文档插入到集合中,但有些情况下希望唯一键只对包含相应键的文档生效,此时稀疏索引就派上用场了,创建稀疏索引语句如下:
db.user.ensureIndex({"email":1}, {"unique":true, "sparse":true})
文档可选字段"email"
,如果提供了就必须唯一。
以上是学习mongodb的笔记,如果不合理的欢迎纠正。后续会继续更新mongodb相关的内容