MongoDB 索引(Index)
一、索引的CRUD
创建索引:ensureIndex()
db.collection.ensureIndex({"name":1}) 为collection的name属性创建正序索引
db.collection.ensureIndex({"name":1},{unique:true}) 为collection的name属性创建正序唯一索引。
db.collection.ensureIndex({"name":1},{unique:true,dropDups:true}) 为collection的name属性创建正序唯一索引。剔除为唯一重复值(在建唯一索引前已经存在重复值)。
查看索引:getIndexes()
db.system.indexes.find() 查看当前数据库的所有索引。 所有数据库都存在索引文档system.indexes
db.system.namespaces.find() 查看当前数据库的所有索引。
db.collection.getIndexes() 查询当前数据库中指定collection的索引。
删除索引:dropIndex()
db.collection.dropIndex({"name":1}) 删除collection中的name属性的索引。
db.collection.dropIndex("name_1") 删除collection中索引名称为name_1的索引
db.runCommand({dropIndexes:"collection",index:"indexName"}) 删除当前数据库中collection文档的索引名称为indexName的索引。注意indexName是索引名称,不是文档的属性名称。
db.runCommand({dropIndexes:"collection",index:"*"}) 删除当前数据库中collection文档的所有索引。
查询时指定索引:
Hint
eg:db.user.find({name:'张三',sex:'男'}).hint({name:1}); #指定索引key
db.user.find({name:'张三',sex:'男'}).hint("name_1"); #指定索引名称
注意:查询时指定索引时指定索引必须存在。
db.collection.getIndexes(); 查询指定集合的索引列表
重建索引 reindex()
语法:db.collection.reIndex()
适用场景:一个表经过大量的修改后会导致表的文件上产生空洞,索引文件也是一样,
可以通过重建索引来提高索引的效率。
不好的地方:对于数据量大或者索引多的表来说,重建索引代价重大。
一般不需要重建索引,只有集合大小发生显巨变化或磁盘空间占用不成比例时才需要
二、索引的分类
1、默认索引
2、单列索引
3、组合索引
db.collection.ensureIndex({"provinceId":1,"isDied":1});
注意:组合索引支持的索引查询只是索引的任意前缀索引字段。
eg:
建表:
db.products.insert({
"_id": ObjectId("56c448bb4b3a77a13d33d5d6"),
"item": "Banana",
"category": ["food", "produce", "grocery"],
"location": "4th Street Store",
"stock": 4,
"type": "cases"
});
新建索引:db.products.ensureIndex({"item":1,"location": 1,"stock": 1})
以下三个查询都不能使用索引:
db.products.find({"location":"4th Street Store"}).explain();
db.products.find({"stock":4}).explain();
db.products.find({"location":"4th Street Store","stock":4}).explain();
下面查询文档上说不能使用索引,但是实际可以使用,不知道为什么 20170220。
db.products.find({"item": "Banana","stock": 4}).explain();
MongoDB 支持索引的前缀查询。
4、内嵌索引
a,内嵌字段索引(Index on an Embedded Field)
#创建数据 db.records.insert({ "_id": ObjectId("570c04a4ad233577f97dc459"), "score": 1034, "location": { state: "NY", city: "New York" } })
#创建索引 db.records.createIndex( { "location.state": 1 } )
#使用索引查询 #使用了索引 查询到了数据 db.records.find( { "location.state": "NY" } ).explain(); #使用了索引 查询到了数据 db.records.find( {"location.city": "New York","location.state": "NY" } ).explain(); #使用了索引 查询不到数据 db.records.find( { location: { city: "New York", state: "NY" } } );
b,内嵌文档索引(Index on an Embedded Documents)
#创建数据 db.user.insert( { name: { first: "Yukihiro", last: "Matsumoto" } } ); db.user.insert( { name: { first: "Yukihiro", last: "Matsumoto", center:"center" } } )
#创建索引 db.user.createIndex( { "name": 1 } );
#使用索引查询 #使用了索引 查询到了数据(第一条数据) db.user.find( { name: { first: "Yukihiro", last: "Matsumoto" } } ).explain(); # 内嵌文档索引必须内嵌文档里面的内容完全匹配才能查出,所以这就是上面的查询为什么只返回了一条数据的理由 #没有使用索引 查询到了数据(两条数据都查到了) db.user.find( { "name.first": "Yukihiro", "name.last": "Matsumoto" } ).explain();
内嵌文档索引
#建立索引前查询 db.collection.find({"contents.content":"传统美术"}).explain() #建立索引 db.collection.ensureIndex({ "contents.content": 1} ) #建立索引后查询 db.collection.find({"contents.content":"传统美术"}).explain() #建立索引前查询 db.collection.find({"contents":{$elemMatch:{"fieldId":"project_type","content":"传统美术"}}}).explain() #建立索引 db.collection.ensureIndex({ "contents.fieldId": 1,"contents.content": 1} ) #建立索引后查询 db.collection.find({"contents":{$elemMatch:{"fieldId":"project_type","content":"传统美术"}}}).explain() #建立索引后性能有了明显的提升。
5、唯一索引 {unique:true}
db.collection.ensureIndex({"name":1},{unique:true}) 为collection的name属性创建正序唯一索引
6、全文索引
当前数据库开启全文索引:db.adminCommand({setParameter:true,textSearchEnabled:true})
新建全文索引:db.collection.ensureIndex({"contents.content":"text"});
使用全文索引进行查询:db.collection.find({$text:{$search:"景区"}})
参考文章:http://www.ziqiangxuetang.com/mongodb/mongodb-text-search.html
注意:一张表只能新建一个全文索引。
7、哈希索引 {'field':'hashed'}
哈希索引特点:速度比普通索引块,但是无法对范围查询进行优化。哈希索引不适合范围查询,哈希索引只支持单字段的索引,不支持多字段索引。
新建哈希索引:db.collection.ensureIndex({"createTime":"hashed"});
8、后台索引
db.people.ensureIndex({zipcode:1},{background:true})
后台索引不会像其它索引一样,不是个阻塞操作。
9、稀疏索引 { sparse: true }
稀疏索引的特点:如果针对field做索引,针对不含field列的文档将不建立索引。
#准备数据 db.user2.insert({'name':'zhangsan'}); db.user2.insert({'name':'lisi'}); db.user2.insert({}); db.user2.find({'name':null}); #唯一索引测试 db.user2.ensureIndex({"name":1},{unique: true}) db.user2.find({'name':null}).explain(); #使用了索引 db.user2.find({'name':'zhangsan'}).explain(); #使用了索引 db.user2.dropIndexes(); db.user2.ensureIndex({"name":1},{sparse: true}) db.user2.find({'name':null}).explain(); #没有使用索引 db.user2.find({'name':'zhangsan'}).explain(); #使用了索引
注意:db.user2.find({'name':null}).explain(); #没有使用索引,这是稀疏索引和其它索引的区别
10、地理位置坐标索引
xxxxxx
参考文章:http://www.jb51.net/article/39671.htm
http://www.cnblogs.com/Amaranthus/p/3574058.html#_Toc381385679
三、建立索引的原则
1、创建的索引要匹配查询
如果你仅仅要查询单个字段。索引这个字段即可。如 db.user.find({"account":"zhangsan"}) 这个例子中,唯一索引是最佳的 db.user.ensureIndex({"account":1 }, {unique: true});
然而,一般都查询多个键并且排序结果。这种情况,组合索引是最佳的,例子如下 db.project_material.find({ provinceId : '66ef8bd44b3a4a8d6a819120'}).sort({ createTime : -1 }); 创建的索引如下 db.project_material.ensureIndex({provinceId : 1, createTime : -1});
2、索引列颗粒越小越好
3、要小心单键索引的低选择性。
4、要关注应用读/写( read/write) 比率,不要随意添加索引
这个很重要,因为,添加索引意味着添加,更新,删除操作变慢。
如果你的应用是偏向于读取,使用索引是非常好的事情。
5、先后顺序不一样,效果也不一样
db.item_file.ensureIndex({"displayOrder":1,"itemTitle":1,"itemCode":1});不等于下面的
db.item_file.ensureIndex({"itemTitle":1,"itemCode":1,"displayOrder":1});
参考文章:http://www.2cto.com/os/201501/369209.html
索引如何设计(读取场景相应的索引设计)
1,查询返回某些字段,而这些字段都可以放在索引中时
索引覆盖查询,
这样做的好处是MongoDB就没有必要加载整个文档,通过索引就可以搞定。
2,联合查询
支持前缀索引查询。
MongoDB有个特性:不是前缀查询无法利用索引。
3,查询返回数据占文档集合数据的比重很大时
不建立索引查询会更快。
比如文档200G 索引50G
使用索引就必须加载到内存250G,如果不使用索引全表扫描 加载到内存只有200G 反而更快。
4,不要到处使用索引,索引的使用要适中。
返回数据量小的文档,建议使用索引。
返回>=50%文档数据时,不建议使用索引
四、性能比较
已建立索引和未建索引前后插入效率对比:
var start =new Date(); for(var i=0;i<100000;i++){ db.log_login_copy1.insert({"user":"ceshi"+i,"fromIP":"113.91.248.225","memo" : "成功登录","logLevel" : "INFO","logModel" : "浏览器"+i}); } var end = new Date(); print("建了索引插入10万行数据耗时="+(end-start));
var start =new Date(); for(var i=0;i<100000;i++){ db.log_login_copy2.insert({"user":"ceshi"+i,"fromIP":"113.91.248.225","memo" : "成功登录","logLevel" : "INFO","logModel" : "浏览器"+i}); } var end = new Date(); print("没建索引插入10万行数据耗时="+(end-start));
总结:
插入的速度没有网上说的那么快只要三秒左右,为什么呢?
已经建立索引和未建立索引的查询效率差异不大 实际是这样吗?
已建立索引和未建索引前后查询效率对比:
查询语句
db.log_login.find({"user":"qinghai"}).explain()
建立索引的语句
db.log_login.ensureIndex({"user":1})
没有建立索引查询耗时(总共100万行记录):
建立索引后查询耗时(总共100万行记录):
explain用法: 查看查询计划
explain()是非常有用的工具,会帮助你获得查询方面诸多有用的信息。只要对游标调用该方法,就可以得到查询细节
eg:db.role.find().explain()
字段说明:
cursor:返回游标类型,有BasicCursor和BtreeCursor,后者意味着使用了索引。
nscanned:扫描document的行数。
n:返回的文档行数。
millis:耗时(毫秒)。这个对于比较索引和非索引性能非常有用。
indexBounds:所用的索引。
总结:从上面两幅截图对比可以看出创建索引后速度提升了上百倍
问题:
模糊查询为什么使用了索引效率还是非常低下呢?201604 查询用了指定新建的索引后效率至少降低了一半,有什么解决方法吗? 是优化模糊查询的方式解决,还是建立其它索引的方式解决,还是没有解决方法呢?
常见错误:
1,Btree::insert: key too large to index 内容太多不能新建索引。
解决方法:在当前数据库下执行下面命令:db.adminCommand({setParameter:true,failIndexKeyTooLong:false});
2,如果执行查询报下面错误
com.mongodb.MongoException: Runner error: Overflow sort stage buffered data usage of 33555102 bytes exceeds internal limit of 33554432 bytes
那提取出查询语句:
db.item_file.find({ "itemTitle" : { "$regex" : "^.*?.*$" , "$options" : "i"} , "itemCode" : { "$regex" : "^.*?.*$" , "$options" : "i"}}).sort({ "displayOrder" : 1}).skip(58860).limit(10);
新建下面索引:
db.item_file.ensureIndex({"displayOrder":1});
但是为什么不是新建下面的索引呢? 20160321. 后面有时间的时候查查。
db.item_file.ensureIndex({"itemTitle":1,"itemCode":1,"displayOrder":1});
新建下面这个索引是可以的。
db.item_file.ensureIndex({"displayOrder":1,"itemTitle":1,"itemCode":1});