mongodb索引讲解与性能调优

标签: mysql 索引 MongoDB 优化 | 发表时间:2012-05-07 11:49 | 作者:admin
出处:http://blog.haohtml.com

mongodb索引规则基本上与传统的关系库一样,大部分优化MySQL/Oracle/SQLite索引的技巧也适用于mongodb。

一、 为什么用索引:

当查询中用到某些条件时,可以对该键建立索引,以提高查询速度。

如果数据量很多且查询多于更新时,可以用索引提高查询的速度。

二、 索引管理:

a)         查询索引:

  1. 查询已有索引的明细:

查询索引很简单,比如说需要查询mailaccess数据库中的Mail collection上的索引时:

mongo                          进入mongo

MongoDB shell version: 1.8.1

connecting to: test

> use mailaccess                  进入mailaccess database

switched to db mailaccess

> db.Mail.getIndexes()             查询索引明细

[

{

"name" : "_id_",

"ns" : "mailaccess.Mail",

"key" : {

"_id" : 1

},

"v" : 0

},

{

"_id" : ObjectId("4df063ac48857df7ac35c348"),

"ns" : "mailaccess.Mail",

"key" : {

"user" : 1,

"folderId" : 1,

"mailfilename" : 1

},

"name" : "user_1_folderId_1_mailfilename_1",

"v" : 0

},

……

  1. 查询索引大小:

> db.Mail.totalIndexSize()                  查询索引大小

114688                                                    索引所占大小,单位:字节

b)         创建索引:

db.Mail.ensureIndex({user:1,folderId:1,mailfilename:1})

该命令会在Mail上创建一索引,默认索引名称的规则为:

Keyname1_dir1_keyname2_dir2…,如果该索引的默认名称:

user_1_folderId_1_mailfilename_1

其中:1表示升序 -1表示降序

要是索引键很多时,最好自定义名称,如:定义名称为:index1

db.Mail.ensureIndex({user:1,folderId:1,mailfilename:1} ,{name:'index1'})

c)         删除索引:

如需删除名称为index1的索引:

db.Mail.dropIndex(‘index’)

如需删除Mail中的所有索引时:

db.Mail.dropIndex(‘*’)   _id索引不会删除

还有一种方式是删除collection,collection中的所以索引也会消失,注意:_id索引也会被删除。(删除collection中的数据不会删除索引)

d)         修改索引:

Mongodb没有单独的修改索引的方法,如果需要修改某个索引,需要先删除旧有的索引,再创建新的索引。

随着数据量的不断增长,你可能会发现某个collection需要修改索引或增加索引,此时创建索引就会很费力了,同时也很消耗性能。创建索引时mongodb默认是阻塞式,阻塞会让索引建立得更快,任何此期间的请求将不能响应。可以使用{”background”:true}选项在后台完成,同时也可能正常处理请求。不过这种方式也会造成请求的响应很慢。如果非紧急情况,最好在晚上统一处理。

三、 索引种类:

a)         默认索引:

Mongodb每个collection都会有一个默认主键_id,这个不能删除、也不会更名。当collection创建后,系统会自动创建一个”_id_”的索引,这个也是无法删除与更名的。

b)         单列索引:

在单个栏位上创建的索引,比如,需要对Mail的read创建升序索引:

db.Mail.ensureIndex({‘read’:1})

c)         组合索引:

对多个键创建的索引,比如,需要对Mail的user与folderId创建降序索引:

db.Mail.ensureIndex({‘user’:-1,’folderId’:-1})

d)         子文档索引:

可以为内嵌文档的键创建索引,这种与普通索引没有什么区别,比如:需要对Mail中的attachments下的filename创建索引:

db.Mail.ensureIndex({‘attachments.filename’:1})

注意:attachments.filename必须位于’’之中,否则会报错

e)         唯一索引:

唯一索引可能确保collection的每一个document指定的键的唯一性。当文档不存在指定键时,会被认为键值是“null”,所以“null”也会被认为是重复的,所以一般被作为唯一索引的键,最好都要有键值对。比如:要保证Mail中每个用户的mailfilename的唯一性:

db.Mail.ensureIndex({‘user’:1,‘filename’:1},{name:’index1’,‘unique’:true})

当为已有的collection增加唯一索引时,可能会有数据已经重复了。有时候可能希望将所有包含重复的文档都删除,可能在创建唯一索引时,使用dropDups选项:

db.Mail.ensureIndex({‘user’:1,‘filename’:1},{‘unique’:true,’dropDups’:true})

这个会将重复的数据只保留一份,不过有点鲁莽,如果数据很重要的话,建议不好这样做。

注意了:

Insert并不检查文档是否插入过,所以确保数据的唯一性,可能要用安全模式插入才行。这样,在插入时,如果有重复就会有错误提醒

f)          Sparse索引:

Sparse index解决索引文件过大的问题,有时候我们要索引的某个属性并非是所有记录都有,普通的索引是将所有的记录都包含进来,而sparse索引则仅包含含有这个属性的记录,它不会对该项值为空的行作索引。这样就大大减小了某些列的索引大小。目前的限制是,sparse index只能包含一个属性。比如:在Mail中有个标签属性labels,这个属性是唯一的,且有值的情况也不多,这种情况就最适合用sparse索引了,创建索引的命令为:

db.Mail.ensureIndex({labels:1},{sparse:true})

g)  Covered 索引 :

如果你查找的值正好是在索引中,则可以直接返回索引中存的值,而不用到数据文件中查找。(这个在传统关系型数据库中也有实现),不过,必须满足以下条件:

  • 必须提供准备的返回字段,以便可以直接从索引库中查询
  • 必须明确地排除使用_id字段{_id:0}

当用explain时,当indexOnly=true,表示有用到covered index:

// do a login with a covered index, returning the users roles/groups

> db.users.ensureIndex( { username : 1, password : 1, roles : 1} );

> db.users.save({username: "joe", password: "pass", roles: 2})

> db.users.save({username: "liz", password: "pass2", roles: 4})

> db.users.find({username: "joe"}, {_id: 0, roles: 1})

{ "roles" : 2 }

> db.users.find({username: "joe"}, {_id: 0, roles: 1}).explain()

{

"cursor" : "BtreeCursor username_1_password_1_roles_1",

...

"indexOnly" : true,

...

}

四、 正则表达式在索引中的使用:

正则表达式可以灵活地匹配查询条件,如果希望正则表达式能命中索引,就要注意了:

Mongodb能为前缀型的正则表达式命中索引,比如:需要查询Mail中user以z开头的:

/^z/

如果有user索引,这种查询很高效

但其他的即使有索引,也不会命中索引,比说:需要查询Mail中的user中含有z的:

/.*z.*/

/^.*z.*/

这种查询是不会命中到索引的,当数据量很大,速度很慢

总之,^后的条件必须明确,不能^.* ^[a-z]之类开头的

五、 索引分析:

a)         索引命中:

假设索引为:{a:1,b:1,c:1,…,z:1}:

实际上是有了:{a:1},{a:1,b:1},{a:1,b:1,c:1}…等索引的。

但是使用{b:1}、{a:1,c:1}等索引的查询是会被优化的,只有使用索引前部的查询才能使用该索引。

Mongodb的查询优化器会重排查询项的顺序,以便命中索引,比如:查询{x:’a’,y:’b’}的时候,如果已有了{y:1,x:1}的索引,mongodb也会自己找到并命中的。

创建索引的缺点是每次插入、更新与删除时都会产生额外的开销,这是因为数据库不但需要执行这些操作,还是处理索引,因些,要尽量可能少创建索引。每个集合默认的最大索引个数为64个。

b)         查询时,不要使用$ne or $nin,这样不能命中索引

c)         使用explain

db.collection.find(query).explain();

返回的信息如下

{“cursor” : “BasicCursor”,

“indexBounds” : [ ],

“nscanned” : 57594,

“nscannedObjects” : 57594,

“nYields” : 2 ,

“n” : 3 ,

“millis” : 108,

“indexOnly” : false}

现实结果可以得知cursor的类型,DB扫描的数据数,返回的数据数,还有执行的毫秒数。

“cursor” : “BasicCursor”:

命中的索引,当为BasicCursor时表示没有命中任何索引

indexBounds: 所使用的索引,被设置为表示为索引扫描的关键边界。

nscanned – 扫描的数据条数。

nscannedObjects – 扫描对象的数。

nYields – 查询所产生的锁的个数。

isMultiKey- MongoDB中提供了可以自动索引数组对象的值

If true, a  multikey index was used.

n- 返回文档的数量

millis- 数据库中执行查询的时间

indexOnly – 是否使用了covered index。

六、 强制索引:

如果发现mongodb用了非预期的索引,可以用hint强制用某个索引,如:

db.Mail.find({user:’[email protected]’,folderId:’inbox’}).hint(‘index1’)

多数情况下这种指定没有什么必要,mongodb会替你选择用哪个索引,初次查询时,查询优化器会同时尝试各种查询方案,最先完成的被确定使用,其他的则终止掉。查询方案也会记录下来,以备是后应对相同键的查询,查询优化器定期也重试其他的方案,以防因为添加新的数据后,之前的方案不再是最优的。

七、 为排序创建索引:

随着集合的增长,如果查询中有用到排序时,就要创建索引了。如果对没有索引的键用sort,mongodb需要将所有的数据提到内存中进行排序,这个是很影响性能的。

八、 索引优化:

这个是有人统计的不同数据类型所占的索引的大小:

另外,如果有关于日期的索引,我针对于String/Long/Date做了下测试:

可以看出,用Date类型所占的索引最小(Long/Date所占的大小差不多,1000w时也只相差3M左右),所以如果有需要日期索引的,需要将类型设置为Date或Long

九、 实例讲解:

以MA为例,场景为:

  1. 获取某个邮件夹的所有邮件列表
  2. 查看某个邮件夹中的已读邮件
  3. 查看某个邮件夹中的未读邮件
  4. 查看某个邮件夹中的重要邮件
  5. 查看某个邮件夹中的邮件来源

索引为:

{user:1,folderId:1,sendTime:-1,read:1,sourceSystem:1,importantFlag:1},{name:’folder_list_index’}

需要用到的查询条件组合(都需要以sendTime降序排列):

user:1,folderId:1,read:1,sourceSystem:1,importantFlag:1

user:1,folderId:1, read:1,sourceSystem:1

user:1,folderId:1,read:1

user,folderId, sourceSystem:1,importantFlag:1

user,folderId, sourceSystem:1

(红包部分必须hint,否则第一次速度会很快)

设计规则:

  1. 索引中的栏位,尽量用int类型,这样可减少索引的内存大小。

比如:sendTime的类型为Long,read、sourceSystem、importantFlag的类型为int

2.    索引的组合尽量包含常见的查询:

比如:获取邮件夹列表,查看邮件夹中的已读、未读邮件、重要邮件这几个查询很常用,设计索引时尽量包括这些查询

3.   有需要排序的栏位,要建索引,否则mongodb会在内存中排序。另外,排序索引尽量加在某个固定的大范围之后。

比如:获取邮件夹列表并按照sendTime降序排列,查询条件的大范围肯定要有user/folderId,此时,可将sendTime放在user/folderId之后,这样user/folderId对应的记录已经都以sendTime:-1的形式存放在索引库中(此时,用sort与不用sort的效果是相同的),sort时,也不用在内存中进行排序。如果有其他查询都在这个范围之内时,即使不能自动命中索引,也可以通过强制索引来提高查询的速度

4. 查询列表范围尽量小,数据量很多时,不加limit的查询会死人的。

您可能也喜欢:

MongoDB 索引数据类型优化,节省60%内存

mongodb的监控与性能优化

Mongodb亿级数据量的性能测试

三招解决MongoDB的磁盘IO问题
无觅

相关 [mongodb 索引 性能调优] 推荐:

mongodb索引讲解与性能调优

- - haohtml's blog
mongodb索引规则基本上与传统的关系库一样,大部分优化MySQL/Oracle/SQLite索引的技巧也适用于mongodb. 当查询中用到某些条件时,可以对该键建立索引,以提高查询速度. 如果数据量很多且查询多于更新时,可以用索引提高查询的速度. a)         查询索引:. 查询索引很简单,比如说需要查询mailaccess数据库中的Mail collection上的索引时:.

MongoDB 索引

- - 博客园_首页
索引是用来加快查询的,数据库索引与数据的索引类似,有了索引就不需要翻遍整本书,数据库可以直接在索引中查找,. 使得查询速度很快,在索引中找到条目后,就可以直接跳转到目标文档的位置.. 要掌握如何为查询配置最佳索引会有些难度.. MongoDB索引几乎和关系型数据库的索引一样.绝大数优化关系型数据库索引的技巧同样适用于MongoDB..

MongoDB索引内部实现

- Rehtron - NoSQLFan
在数据库优化中,索引优化是非常重要也是每一个数据库使用者必须了解的. MongoDB的索引采用BTree结构,除了其地理位置索引外,数据索引本质上和MySQL 的 BTree索引没有什么差别. 下面是一个接近200页的PPT,对MongoDB读写操作相关的索引操作进行了图文讲解. 如果你对数据库索引的实现原理感兴趣,可以仔细看看.

MongoDB索引实战技巧

- - NoSQLFan
本文内容源自 Kyle Banker 的 MongoDB In Action一书. 主要描述了MongoDB 索引相关的一些基础知识和使用技巧. 虽然MongoDB的索引在存储结构上都是一样的,但是根据不同的应用层需求,还是分成了唯一索引(unique)、稀疏索引(sparse)、多值索引(multikey)等几种类型.

MongoDB中索引的用法

- - 数据库 - ITeye博客
本文是一篇转载文章,作者在对 MongoDB文档进行了细致的阅读后,总结出了MongoDB的各种索引的用法. 原文链接: http://iamcaihuafeng.blog.sohu.com/151638529.html. 索引能提高检索数据的速度,你可以想像成在MySQL中创建索引一样,同样索引也是用B-Tree也实现的.

关于Mongodb索引实战

- - snoopyxdy的博客
最近碰到这样的一个需求,一张酒店政策优惠表,我们要根据用户入住和离开的时间,计算一家酒店的最低价政策前10位,数据库表字段如下:. 'hid':88,     酒店id. 'date':20150530,  入住日期整形(不要纠结unix时间戳). 'enable':1,  政策是否启用. 'price':100, 政策价格.

Dex – MongoDB索引优化工具

- - NoSQLFan
Dex是一个开源的MongoDB 优化工具,它通过对查询日志和当前数据库索引进行分析,向管理员提出高效的索引优化策略. 在监控过程中,dex会通过stderr输出推荐的结果. 我们看到,在输出结果中,有一个shellCommand字段,里面就是添加索引的语句,如果你觉得dex的推荐不错,就可以直接复制这段脚本在 MongoDB上添加索引了.

MongoDB范围查询的索引优化

- - NoSQLFan
我们知道, MongoDB的 索引是B-Tree结构的,和MySQL的索引非常类似. 所以你应该听过这样的建议:创建索引的时候要考虑到sort操作,尽量把sort操作要用到的字段放到你的索引后面. 但是有的情况下,这样做反而会使你的查询性能更低. 比如我们进行下面这样的查询:. 查询条件是 {“country”: “A”},按 carsOwned 字段的正序排序.

Linux常用性能调优工具索引

- - 系统技术非业余研究
原创文章,转载请注明: 转载自 系统技术非业余研究. Linux常用性能调优工具索引. 前段时间看到brendangregg的 Linux Performance Analysis and Tools PPT里面提到Linux常用性能调优工具, 见下图:. 其中提到了的工具,大部分我之前有推荐过或者在实践的案例里面使用过,这里方便大家索引下:.

清除Oracle中无用索引 改善DML性能-性能调优-Oracle频道-中国IT实验室

- - Delicious/searchfull
DML性能低下,其中最严重的原因之一是无用索引的存在. 所有SQL的插入,更新和删除操作在它们需要在每一行数据被改变时修改大量索引的时候会变得更慢. 许多Oracle 管理人员只要看见在一个SQL 查询的WHERE语句出现了一列的话就会为它分配索引. 虽然这个方法能够让SQL运行得更快速,但是基于功能的Oracle 索引使得数据库管理人员有可能在数据表的行上过度分配索引.