索引本质上是树,最小的值在最左边的叶子上,最大的值在最右边的叶子上,使用索引可以提高查询速度(而不用全表扫描),也可以预防脏数据的插入(如唯一索引)
索引即支持普通字段也支持内嵌文档中某个键和数组元素进行索引
对某个键按照升续或降续创建索引,查询时首先根据查询条件查找到对应 的索引条目找到,然后找对索引条目对应的文档指针(文档在磁盘上的存储位置),根据文档指针再去磁盘中找到相应的文档,整个过程不需要扫描全表,速度比较快
[“along”] —-> 0x0c965148(文档指针) …… [“zhangsan”] —-> 0x0c965148(文档指针)
MongoDB是无结构型的NoSQL,同一个集合中的每条文档可以包含某个键,也可以不包含,为了达到如果文档中包含索引键,索引键的值必须唯一,如果不包含索引键那么不用校验唯一的效果,可以在创建索引时使用sparse: true, 也就是稀疏索引。
> db.foo.drop() true > db.foo.ensureIndex({"username": 1}, {"unique": true, "sparse": true}) { "createdCollectionAutomatically" : true, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > db.foo.insert({"email": "mengday1@163.com"}) WriteResult({ "nInserted" : 1 }) > db.foo.insert({"email": "mengday2@163.com"}) WriteResult({ "nInserted" : 1 }) > db.foo.insert({"username": "mengday3", "email": "mengday3@163.com"}) WriteResult({ "nInserted" : 1 }) > db.foo.insert({"username": "mengday3", "email": "mengday3@163.com"}) WriteResult({ "nInserted" : 0, "writeError" : { "code" : 11000, "errmsg" : "E11000 duplicate key error collection: test.foo index: username_1 dup key: { : \"mengday3\" }" } })稀疏索引:对不存在的键就不进行索引,也就是该文档上没有建立索引,索引条目中也不包含 索引键为null的索引条目,所以再次插入不包含索引键的文档不会报错,直接插入。注意:稀疏索引不光和唯一索引配合使用,也可以单独使用
唯一索引的目的是为了让数据库的某个字段的值唯一,为了确保数据的都是合法的,但是唯一索引在插入数据时会对数据进行检查,一旦重复会抛出异常,效率会比较低,唯一索引只是保证数据库数据唯一的最后一种手段,而不是最佳方式,更不是唯一方式,为了保证效率最好采用别的解决方案来保证数据的唯一合法性,尽量减少数据库的压力。
TTL索引是让文档的某个日期时间满足条件的时候自动删除文档,这是一种特殊的索引,这种索引不是为了提高查询速度的,TTL索引类似于缓存,缓存时间到了就过期了,就要被删除了
// expireAfterSeconds: 文档生存的时间,单位是秒,索引键是日期类型的 // 如果当期时间大于索引键的时间加上缓存时间就会删除该文档 > db.foo.ensureIndex({"create_at": 1}, {"expireAfterSeconds": 60}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 }全文索引是用于对长文本检索来使用的,是用正则表达式只能对字符串类型的值进行检索。注意:创建索引是一件比较耗时耗费资源的事情,而全文索引更是耗时更厉害,如果对索引键的内容比较长,需要对内容进行分词,会出现更严重的性能问题。
创建全文索引,建议在mongodb不忙的时候创建,mongodb的分词现在好像不支持中文,如果是对内容比较小的比如小于100个汉字的可以试用一下mongodb的全文索引,如果是对一篇很长的文章使用全文索引这是非常不合适的,这会把mongodb累死的,对于内容比较多可以采取其他技术如Lucenne、Solr、ElasticSearch等技术
创建索引时可以对一个字段创建索引,也可以对多个字段创建索引,对多个字段创建索引被称为 复合索引或者组合索引
> db.user.find() { "_id" : 1, "username" : "zhangsan", "age" : 25 } { "_id" : 2, "username" : "lisi", "age" : 18 } { "_id" : 3, "username" : "wangwu", "age" : 28 } { "_id" : 4, "username" : "fengwu", "age" : 27 } > // 1:为索引值以升续的方式创建索引条目,-1:代表降续 > db.user.ensureIndex({"username": 1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } // explain()函数用于查看当前查询的一些信息,比如使用使用了索引等 > db.user.find({"username": "wangwu"}).explain() // 创建组合索引(以后台模式创建) > db.user.ensureIndex({"username": 1, "age": 1}, {"background": true}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 } > db.user.find({"username": "wangwu", "age": 28}) // 如果查询时发现没有使用到索引,可以使用hint函数强制使用索引查询 > db.user.find().hint({"username": 1, "age": 1}) > db.user.update({"username": "zhangsan"}, {"$set": {"address": {"road": "yijiang", "code": 666}}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) // 对内嵌文档中字段创建索引 > db.user.ensureIndex({"address.road": 1}, {"background": true}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 3, "numIndexesAfter" : 4, "ok" : 1 } > > db.user.find({"address.road": "yijiang"}).explain() // 对数组创建索引,就是对数组中的每个元素分别创建索引,而不是对整个数组建立索引,对数组的每一个元素都创建索引,那么维护索引的代价就比普通的值大 > db.user.update({"username": "zhangsan"}, {"$set": {"hobby": ["eat", "drink", "mm", "money"] }}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.user.ensureIndex({"hobby": 1}, {"background": true}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 4, "numIndexesAfter" : 5, "ok" : 1 } > db.user.find({"hobby": "mm"}).explain()什么时候创建索引?
当需要对查询优化,或者经常使用某种查询的时候可以创建索引来提高查询效率应该选哪些字段创建索引?
一般应该在基数比较高的键上建立索引,或者至少把基数较高的键放在复合索引的前面位置