索引的作用是用来加速查询,数据库索引与书籍索引类似,创建数据库索引好像确定何如组织书的索引一样。
# 批量查询数据
for(var i=0; i<30000; i++){
db.books.insert({name:'book'+i, sort:i});
}
db.books.count();
# 查看查询计划
db.books.find(query).explain();
"cursor":"BasicCursor",//说明没有索引发挥作用
"nscannedObjects":1000,//理论上要扫描的行数
# 计算查询效率
var begin = new Date();
db.books.find({sort:30000});
var end = new Date();
print(end-begin);
# 创建索引,1表示正序,-1表示倒序。
db.books.ensureIndex({sort:1});
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1.0
}
explain
只要对游标调用explain()会返回一个文档而非游标本身,explain()会返回查询使用的索引情况,耗时及扫描文档数的统计信息。
强制查询使用指定索引
# 指定索引必须是已经创建了的索引
db.books.find({name:'bookname1', sort:1}).hint({name:-1})
# 查看数据库已建立的索引
db.system.indexes.find();
db.system.namespaces.find();
1 创建索引
索引提高查询速度,但会降低写入速度,权衡常用查询字段,不必在太多字段上创建索引。在MongoDB中索引可按字段升序或降序来创建,便于排序。默认使用btree来组织索引文件,在2.4版本后允许建立hash索引。
- 创建索引时索引的方向,1为正序索引,-1为倒序索引。
- 索引的创建在提高查询性能的同时会影响插入的性能,对于经常查询少插入的文档可考虑使用索引。
- 复合索引要注意索引的先后顺序
- 索引不是万能的
- 排序时大数据量可考虑添加索引
1.1 索引类型
索引的作用类型分为单列索引、多列索引、子文档索引。
# 查看查询计划
db.books.find(query).explain()
"cursor":"BasicCursor",//说明没有索引发挥作用
"nscannedObjects":1000,//理论上要扫描的行数
# 创建单列索引
db.books.sureIndex({field:1/-1})
# 创建多列索引(复合索引)
db.books.sureIndex({field:1/-1, field:1/-1...})
# 创建子文档索引
db.books.sureIndex({field.subfield:1/-1})
# 查看查询计划
db.books.find(query).explain()
"cursor":"BtreeCursor sn_1",//使用btree索引
"nscannedObjects":1000,//理论上要扫描的行数
创建索引的缺点是每次插入、更新、删除时会产生额外的开销。这是因为数据库不但需要执行这些操作,还需将这些操作在集合中的索引中标记。因此,要尽可能少创建索引。每个集合默认最大索引个数为64个。
# 查看当前索引状态
db.books.getIndexes()
一般来说或要是查询返回集合中一半以上的结果,用表扫描会比几乎每条文档都查询索引要高效。查询是否存在某个键或检查某个布尔值类型,使没必要建立索引的。
创建索引时需考虑什么呢?
- 会做什么样的查询呢?其中哪些键需要索引呢?
- 每个键的索引方向是怎样的呢?
- 如何应对扩展?有没有不同的键的排列可使用常用数据更多地保留在内存中?
1.2 索引名称
集合中每个索引默认使用字符串命名并具有唯一性,其格式为 keyname1_dir1_keyname2_dir2_..._keynameN_dirN
,keyname
表示索引的键,dir
表示索引的方向(1正序 -1逆序)。索引太多时可手工指定索引名称。
# 创建索引同时指定索引名称
db.books.ensureIndex({name:-1}, {name:'bookname'});
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1.0
}
# 为内嵌文档的键建立索引
db.blogs.ensureIndex({'comments.date': 1});
1.3 排序索引
随着集合的增长,需针对查询中大量的排序做索引。若对无索引的键调动sort
,MongoDB需将所有数据提取到内存来排序??勺鑫扌蚺判蚴庇猩舷薜?,是不可能在内存中做T
级别的数据排序。一旦集合达到不能再内存中排序,MongoDB会报错。按照排序来索引以便让MongoDB按顺序提取数据,在排序大数据时不必担心耗光内存。
创建索引时,程序执行过程会暂时锁表,该如何解决呢?
# 为了不影响查询,创建索引可在后台执行。
db.books.ensureIndex({name:-1}, {background:true})
通常来说要尽量避免让服务器做表扫描,因为集合很大时会非常慢。实践证明,一定要创建查询中用到的所有健的索引。
2 删除索引
# 精确删除
db.books.dropIndex({field:1/-1});//删除某列字段的单个索引
db.runCommand({dropIndexes:'name', index:'name_1'});
# 批量删除
db.books.dropIndexes();//删除所有索引
db.runCommand({dropIndexes:'name', index:'*'})
3 索引性质
3.1 唯一索引
建立唯一索引可解决插入重复的数据,以确保集合文档指定键是唯一值。
db.books.ensureIndex({name:-1}, {unique:true});
在插入数据时并不检查文档是否已经插入过,为避免插入的文档包含于唯一键重复的值,可能要用安全插入才能满足要求。这样,在插入文档时会看到存在重复键错误的提示。
默认的_id与普通的唯一索引的区别是,_id的索引时无法删除的。
创建索引后若无对应的键,索引会将其作为null存储。若对键建立唯一索引,但插入了多个缺少该索引键的文档,则由于文档包含null值而导致插入失败。
消除重复
倘若建立唯一索引之前已存在重复数据,该怎么办呢?可将包含重复值的文档都删掉,dropDups选项可保留发现的第一个文档,而删除剩余具有重复值的文档。对于重要数据而言,应写脚本做预处理比较稳妥。
db.books.ensureIndex({name:-1}, {unique:true, dropDups:true})
复合唯一索引
创建复合唯一索引时,单个键值可相同,只要所有健的值组合起来不同即可。
GridFS是MongoDB中存储大文件的标准方式,用到了复合唯一索引。存储文件内存的集合有一个复合唯一索引{files_id:1, n:1}。
3.2 稀疏索引
若针对字段做索引时且不含字段的文档,将不建立索引。与之相对的是普通索引,它会把文档的字段值默认为NULL并建立索引。稀疏索引适用于小部分文档含有某字段时使用。
db.collection.ensureIndex({field:1/-1}, {sparse:true})
3.3 哈希索引
btree和二叉树,对于顺序读取具有优势。hash取决于hash算法,根据hash算法获得硬盘保存位置,对于散列的数据,仅通过hash计算即可获取数据,由于天生无顺序,因此适合于离散的数据。
哈希索引速度比普通索引快,但不能对范围查询进行优化,适用于随机性强的散列数据。
db.collection.ensureIndex({field:'hashed'})
4 重建索引
数据表经多次修改后导致文件产生空洞,索引文件也是如此。因此可通过重建索引来提高索引的查询效率,类似MySQL的optimize表。
db.collection.reIndex();