一、索引规则
(1)索引可以大大减少要处理的文档数量,没有适当的索引,唯一满足条件的查询方式就是扫描全部文档,直到找到满足条件的查询。
(2)唯一的单键索引将会用来处理查询。对于包含多个键查询,包含这些键的复合索引是最好的解决方案
(3)如果有一个复合索引a-b,那么a上的单键索引就是多余的,b上的不多余
(4)复合索引的键值顺序很重要
example1:
db.products.find({'details.manufacturer':'Acme', 'pricing.sale':{$lt : 7500}})
如果details.manufacturer和pricing.sale各自有单键索引,需要单独遍历每个数据结构,找到它们的磁盘位置,计算交集。
如果建立了details.manufacturer和pricing.sale的复合索引,查询优化器需要找到索引中制造商manufacturer是Acme并且价格是7500的第一个入口。从那里开始,结果可以使用连续的扫描查找出来。
注意:(1)索引键值的顺序非常重要。如果我们定义的复合索引第一个值是price,第二个是manufacturer,那么查询效率就会非常低。键值必须按照出现的顺序比较,先找到7500,然后扫苗小于7500的文档判断是否是Acme公司生成的,假设有10000万个产品,所有价格低于10000,并且按价格均匀分布。这种情况需要扫描7500万个索引。
索引效率
如果某个集合包含10个索引,那么除了编写文档,每次插入都需要单独修改10个数据结构。这适用于任何写操作,无论是删除文档,还是因为空间不足挪动文档或者更新文档的索引键。对于读取密集型的应用,索引的成本是可以理解的。知道索引有成本,所以必须仔细选择。这意味着确保所有的索引都会被使用,不会有冗余。
第二个问题,即使所有的索引都建设得恰当,也可能无法加快查询,这会在索引和数据集没有加载到RAM的时候发生。
当使用默认的MMAPV1存储引擎时,使用系统调用mmap()方法,mongodb告诉os把所有的数据文件映射到内存中。但WiredTiger引擎使用了不同的方式,这一点上,包含所有文档,集合和索引的数据文件,被os加载和移除RAM,都按照4KB大小数据移动,这个数据块称为内存页。无论是否需要页上的数据,os都必须确保RAM中的数据页可用。如果没有,就会出现页面错误的异常,这会告诉内存管理器,要从磁盘加载数据到RAM里。
无论何时修改内存数据,比如写数据时,这些修改都会被os异步写入磁盘。写入时更快,因为直接操作内存,因此将磁盘的访问量降到最低。但是如果数据文件无法全部进入RAM就会出现页面错误。这意味着os将会频繁访问磁盘,大大降级读写速度。最坏的情况下,数据大小变得比RAM容量大很多。一种情形就是无论读写,数据都必须从磁盘或者向磁盘写入数据。这种情况有个专业的称呼:“颠簸”,它会严重导致性能变慢。
幸运的是,这种情形相对容易避免。至少我们可以确保索引加入RAM里,这也是为什么不创建不需要的索引的原因。设置的索引越多,就需要越多的RAM来维护这些索引。沿着相同的路线,每个索引应该只包含需要的键值。有时候也会需要3个键值的复合索引,但是需要知道它比单键索引需要更多的空间。创建一个或者2个字段索引,是为频繁地查询创建覆盖索引。覆盖索引是所有的查询都可以使用一个查询来满足查询的索引,让查询变得非常快。
多键索引
{name:"ww" , tags:["tools","soil"]}
如果在tags上创建索引,每个文档tags数组的值会出现在索引里,就意味着针对这些数组任意值在索引上的查询都会定位到文档上。这是多键索引背后的思想:多个索引入口或者键值,引用同一个文档。
哈希索引
db.tablename.createIndex({name:"hashed"})
限制:等值查询相似,不支持范围查询; 不支持多键哈希; 浮点数在哈希之前转换为整数,因此4.2和4.3有相同的哈希索引
为什么要使用:哈希索引的入口是均匀分布的。换句话说,当有键值数据不均匀分布时,哈希函数可以创建均匀性。‘Apple Pie’和“Artichoke Ravioli”在哈希索引中就不会相邻了。索引数据的位置已经变化了。它对于分片集合非常有用,分片索引决定文档分配到哪个片中。如果分片索引基于增长的值,比如mongo OIDs ,那么新创建的文档只会插入单个片中,除非索引是哈希的。
深入