MongoDB索引管理(1)——[九]
一、概述
数据库的索引与书籍的索引类似,有了索引就不需要翻转整本书。数据库的索引跟这个原理一样,首先在索引中找,在索引中找到条目以后,就可以直接跳转到目标文档的位置,从而使查询速度提高几个数据量级。
不使用索引的查询称为全表扫描(这个术语来源于关系型数据库),也就是说,服务器必须查找完一整本书才能找到查询结果。这个过程跟我们在一本没有索引(目录)的书中查找信息很像:从第一页开始一直读完整本书。通常来说,应该尽量避免使用全表扫描,因为对于大集合来说,全表扫描的效率非常低。
Mongodb的索引几乎与关系型数据库的索引一模一样,所以适用于关系型数据库索引优化技巧,在这里就不多说了。
二、创建索引
创建索引使用db.collectionName.ensureIndex(...)方法进行创建;
参考实例一:创建普通索引
> db.users.ensureIndex({"name":1})
这样就在users这个集合上面的name字段上面创建了一个name_1的索引,{"name":1}说明建立的索引是升序的,如果{"name":-1}说明创建的索引是降序的。
参考实例二:创建普通复合索引
db.users.ensureIndex({"name":1,"age":1})
这样就在users这个集合上面的name和age字段上面创建了一个name_1_age_1的索引.
参考实例三:创建唯一索引
唯一索引可以确保集合里面的每一个文档指定的键都有唯一值。例如,如果想保证不同文档的name键拥有不同的值,创建一个唯一索引就可以了:
> db.dept.ensureIndex({"name":1},{"unique":true})
创建完唯一索引以后,如果想向dept集合中添加如下文档:
> db.dept.insert({ "_id" : 1, "name" : "ickes" }) > db.dept.insert({ "_id" : 2, "name" : "ickes" })
会发现只有第一个文档添加进去了,添加第二个时就会抛出异常,所以使用唯一索引来应对偶尔可能出现键重复的问题,而不是在运行时对重复键进行过滤。"_id"就是这中类型的索引,这个索引会在创建集合时自动创建。
参考实例三:创建复合唯一索引
创建复合唯一索引时,单个键的值可以相同,但是所有键的组合的值必须是唯一的。
> db.users.ensureIndex({"name":1,"age":1},{"unique":true})
参考实例四:创建唯一索引时去除重复
在已有的集合上面创建唯一索引时,可能失败,因为集合中可能已经存在重复值了,如下所示:
> db.dept.find() { "_id" : 1, "name" : "ickes" } { "_id" : 2, "name" : "ickes" } { "_id" : 3, "name" : "ickes1" } { "_id" : 4, "name" : "eks" } { "_id" : 5, "name" : "eks" } --从上面的集合中看出name已经有大量重复值,创建唯一索引时抛出异常 > db.dept.ensureIndex({"name":1},{"unique":true}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "ok" : 0, "errmsg" : "E11000 duplicate key error index: test.dept.$name_1 dup key : { : \"eks\" }", "code" : 11000 }
通常需要对已有的数据进行处理(可以使用聚合框架,后面会说),找出重复的数据,想办法处理。
在极少数情况下,可能希望直接删除重复的值。创建索引时使用"dropDups"选项,如果遇到重复的值,第一个会被保留,之后的重复文档都会被删除。
> db.dept.ensureIndex({"name":1},{"unique":true,"dropDups":true}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }
"dropDups"会强制性的建立唯一索引,但是这个方式太粗暴了:你无法控制那些文档需要被保留,那些文档需要被删除(从上面打印的信息可以看出,如果文档被删除了,MongoDB也不会给出提示信息)。对于比较重要的数据,千万不要使用"dropDups".
参考实例四:创建稀疏索引
唯一索引会把null值看做值,所以无法将缺少键的多个文档插入到建立的唯一索引的集合中。然而,在某些情况下,你可能希望唯一索引只针对包含相应键的文档生效。如果有一个可能存在也有可能不存在的字段,当字段存在时是唯一索引,不存时不做处理,这时就可以将unique和sparse选项组合在一起使用。
使用sparse选项就可以创建稀疏索引。例如集合的结构如下:
> db.sparse.find() { "_id" : 1, "x" : 1 } { "_id" : 2, "x" : 2 } { "_id" : 3, "x" : null } { "_id" : 4 }
创建稀疏索引
> db.sparse.ensureIndex({"x":1},{"unique":true,"sparse":true})
稀疏索引不必是唯一的。只要去掉unique选项,就可以创建一个非唯一的稀疏索引
MongoDB的稀疏索引与关系型数据库中的稀疏索引是完全不同的概念。基本上来说,MongoDB中的稀疏索引只是不需要将每个文档都作为索引条目。那么问题来了,根据是否使用稀疏索引,同一个查询的返回结果可能不同。
例如上面文档,当在x上面执行查询时,他会返回相应匹配的文档:
> db.sparse.find({"x":{"$ne":1}}).hint({}) --hint({})强制不使用索引 { "_id" : 2, "x" : 2 } { "_id" : 3, "x" : null } { "_id" : 4 }
如果在x字段上面使用索引,那么{ "_id" : 4 }的文档将不会返回,因为他不在索引中,例如:
> db.sparse.find({"x":{"$ne":1}}).hint({"x":1}) { "_id" : 3, "x" : null } { "_id" : 2, "x" : 2 }
二、查看索引
所有数据库索引信息都存储在system.indexes集合中。这是一个保留集合,不能在其中插入或者删除文档。只能通过ensureIndex({...})或者dropIndexes对他进行操作:
参考实例一:查看所有数据库建立的索引详情
> db.system.indexes.find() { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.users" } { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.dept" } { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.food" } { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.test" } { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.emp" } { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.blog" } ................省略!
参考实例二:查看特定集合的索引信息
> db.users.getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.users" }, { "v" : 1, "key" : { "name" : 1, "age" : 1 }, "name" : "name_1_age_1", "ns" : "test.users" } ] >
这里最重要的字段是key和name,key说明了你创建索引时字段跟排序,name就是索引的名称,默认为key_dir_key_dir....的格式,"v"字段只在内部使用,用于标识索引的版本。如果你的索引不包含“v”:1这样的字段,说明你的索引是一种效率比较低的旧方式存储的。将MongoDB升级到至少2.0的版本,删除并重建这些索引,就可以把所有的存储方式升级到新的格式了。
三、标识索引
集合中的每一个索引都有一个名称,用于唯一标识索引,也可以用于服务器端来删除索引。索引默认的命名方式是key1_dir1_key2_dir2....keyn_dirn,其中key就是索引的键,dir是索引的排序方向(1或者-1),如果索引中包含的键比较多,这种默认的命名方式就显得比较笨重,可以再创建索引时指定索引的名称。
参考实例:
> db.users.ensureIndex({"name":1,"age":1},{"name":"name_age1"})
温馨提示:MongoDB索引名称的长度有限,所以新建复杂索引时可能需要自定义索引名称.
四、删除索引
参考实例一:删除集合里面的所有索引
> db.users.dropIndexes()
参考实例二:根据索引的key或者name删除集合里面的指定索引
> db.users.dropIndex("name_1_age_1")--根据索引的name删除 > db.users.dropIndex({"name":1,"age":1})--根据索引的key删除