关于索引
索引通常能够极大的提高查询的效率。在系统中使用查询时,应该考虑建立相关的索引。在MongoDB中创建索引相对比较容易。MongoDB中的索引在概念上和大多数关系型数据库如MySQL是一样的;需要在MySQL中建立索引的情况, 一般同样适合于MongoDB。
为何需要索引
当数据保存在磁盘类存储介质上时,它是作为数据块存放。这些数据块是被当作一个整体来访问的,这样可以保证操作的原子性。硬盘数据块存储结构类似于链表,都包含数据部分,以及一个指向下一个节点(或数据块)的指针,不需要连续存储。
记录集只能在某个关键字段上进行排序,所以如果需要在一个无序字段上进行搜索,就要执行一个线性搜索(Linear Search)的过程,平均需要访问N/2的数据块,N是表所占据的数据块数目。如果这个字段是一个非主键字段(也就是说,不包含唯一的访问入口),那么需要在N个数据块上搜索整个表格空间。
但是对于一个有序字段,可以运用二分查找(Binary Search),这样只要访问log2 (N)的数据块。这就是为什么性能能得到本质上的提高。
什么是索引
索引是对记录集的多个字段进行排序的方法。在一张表中为一个字段创建一个索引,将创建另外一个数据结构,包含字段数值以及指向相关记录的指针,然后对这个索引结构进行排序,允许在该数据上进行二分法排序。
副作用是索引需要额外的磁盘空间,对于MySQL的MyISAM引擎而言,这些索引是被统一保存在一张表中的,这个文件将很快到达底层文件系统所能够支持的大小限制,如果很多字段都建立了索引的话。
索引策略
设计索引的一个通用策略就是在和您的产品中使用的数据集相似的数据集上,根据一系列不同的索引配置分别建立索引,考察哪一种配置性能最优。检查当前所创建的索引以确保您当前所使用和计划使用的查询。如果一个索引不再使用,应该删除它。
MongoDB索引
创建索引
mongodb采用ensureIndex来创建索引,如:
db.user.ensureIndex({"name":1})
表示在user集合的name键创建一个索引,这里的1表示索引创建的方向,可以取值为1和-1
在这里面,我们没有给索引取名字,mongodb会为我们取一个默认的名字,规则为keyname1_dir1_keyname2_dir2…keynameN_dirN
keyname表示键名,dir表示索引的方向,例如,上面的例子我们创建的索引名字就是name_1
组合索引
索引还可以创建在多个键上,也就是联合索引,如:
db.user.ensureIndex({"name":1,"age":1})
这样就创建了name和age的联合索引。
唯一索引
与RDB类似,我们也可以定义唯一索引,方法就是指定unique键位true:
db.user.ensureIndex({"name":1},{"unique":true})
_id 是创建表的时候自动创建的索引,此索引是不能够删除的。
后台创建
当系统已有大量数据时,创建索引就是个非常耗时的活,我们可以在后台执行,只需指定“backgroud:true”即可。
db.user.ensureIndex({age:1} , {backgroud:true})
在mongo2.4以前有一个bug,在主库发起后台创建索引,从库建索引时不是在后台,从而导致主从同步和访问数据的问题。
对于给定的某个操作,MongoDB只能使用 一个 索引来支持该操作。此外, $or 查询语句中的每个子句可能会使用不同的一个索引。
查看索引
索引的信息存在每个数据库的system.indexes集合里面,对这个集合只能有ensureIndex和dropIndexes进行修改,不能手动插入或修改集合。
通过> db.system.indexes.find()可以找到数据库中多有的索引:
db.system.indexes.find()
删除索引
db.user.dropIndex({age:1}
强制使用索引
hint 命令可以强制使用某个索引。
db.user.find({age:{$lt:30}}).hint({name:1, age:1}).explain()
索引排序
随着集合的增长,需要针对查询中大量的排序做索引。如果没有对索引的键调用sort,MongoDB需要将所有数据提取到内存并排序。因此在做无索引排序时,如果数据量过大以致无法在内存中进行排序,此时MongoDB将会报错。
使用explain:
MongoDB 提供了一个 explain 命令让我们获知系统如何处理查询请求。利用 explain 命令,我们可以很好地观察系统如何使用索引来加快检索,同时可以针对性优化索引。
> db.test.find().explain()
{
"cursor" : "BasicCursor",
"nscanned" : 1,
"nscannedObjects" : 1,
"n" : 1,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : {
}
}
explain会返回查询使用的索引情况,耗时和扫描文档数的统计信息。
“cursor”:”BasicCursor”表示没有使用索引。
“nscanned”:1 表示查询了多少个文档。
“n”:1 表示返回的文档数量。
“millis”:0 表示整个查询的耗时。
常用优化方案
- 创建索引
- 限定返回结果数
- 只查询使用到的字段
- 采用capped collection
- 采用Server Side Code Execution
- 使用Hint,强制使用索引
- 采用Profiling
常用工具
mongosniff
此工具可以从底层监控到底有哪些命令发送给了MongoDB 去执行,从中就可以进行分析:以root 身份执行:
$./mongosniff –source NET lo
然后其会监控位到本地以localhost 监听默认27017 端口的MongoDB 的所有包请求。
mongostat
此工具可以快速的查看某组运行中的MongoDB 实例的统计信息 字段说明:
insert: 每秒插入量
query: 每秒查询量
update: 每秒更新量
delete: 每秒删除量
locked: 锁定量
qr | qw: 客户端查询排队长度(读|写)
ar | aw: 活跃客户端量(读|写)
conn: 连接数
time: 当前时间
它每秒钟刷新一次状态值,提供良好的可读性,通过这些参数可以观察到一个整体的性能情况。
db.stats
db.stats 查看数据库状态信息。
{
"db" : "test",
"collections" : 8,
"objects" : 31019,
"avgObjSize" : 1037.2548438054096,
"dataSize" : 32174608,
"storageSize" : 100569088,
"numExtents" : 28,
"indexes" : 9,
"indexSize" : 1095584,
"fileSize" : 201326592,
"nsSizeMB" : 16,
"dataFileVersion" : {
"major" : 4,
"minor" : 5
},
"extentFreeList" : {
"num" : 0,
"totalSize" : 0
},
"ok" : 1
}
db.serverStatus
查看实例运行状态。