索引类型:
MongoDB有多种索引类型,每种索引类型有其适合的场景。
- 单字段索引
- 复合索引
- 多key索引
- 文本索引
- 其他
对于单字段索引而言,升序和降序效果是一样的。
复合索引针对多个字段联合创建索引,先按第一个字段排序,第一个字段相同的文档按第二个字段排序,以此类推。复合索引也能满足单个字段的索引,但仅限复合索引首个字段。
当索引的字段为数组时,创建出的索引为多key索引,多key索引会为数组的每个元素建立一条索引,比如下图所示BillInterceptLifeInfo表中加入一个NeedSyncSiteCodes字段,需要查询NeedSyncSiteCodes中是否包含了某个sitecode就可以利用该字段的多key索引。
实际场景:
拦截件监控代码中,主要的数据在一个拦截件生命期表, 该表记录了拦截件的生命期状态,一个应下发设备表和一个实际下发设备表。
表结构如下:
其中更新应下发设备表是要查询出1.站点信息相符的所有单号,且该单号的2.结束时间大于当前时间,且该3.应下发List中不含即将插入的deviceID才会插入一条记录。
因为需要下发的站点列表和需要下发的设备列表是两个list,无法一起建索引,因此决定建EndTime和NeedSyncSiteCodes的复合索引,NeedSyncSiteCodes也是一个多key索引。
一开始建索引如下:
db.BillInterceptLifeInfo.createIndex({ "EndTime" : 1, "NeedSyncSiteCodes" : 1 })
代码上线后,一开始平稳,但数据量上千后开始不稳定
后根据系统优化提示将索引改为:
db.BillInterceptLifeInfo.createIndex({ "NeedSyncSiteCodes" : 1 , "EndTime" : 1,})
监控如图:
根据某两条查询对比来看:
前:
后:
可以看出,组合索引的先后顺序不同,EndTime在前的索引Key match到的记录有5K多条,而实际需要修改的只有3条,
NeedSyncSiteCodes在前的索引Key match到的记录有107条,而这107条都是需要修改的,NeedSyncSiteCodes在前的这条索引能缩小二次匹配的范围程度更大。
在复合索引中,多个字段应把最有区分度的字段放前面,比如一个person表中有一个[name + age]的复合索引,age字段的取值很有限,即拥有相同age字段的文档会有很多;而name字段的取值则丰富很多,拥有相同name字段的文档很少;显然先按name字段查找,再在相同name的文档里查找age字段更为高效。
同理,在此例子中的EndTime和NeedSyncSiteCodes两个字段建复合索引,EndTime的区分度肯定不如NeedSyncSiteCodes的多key索引。因此将NeedSyncSiteCodes作为复合索引的第一项更高效。
复合多key索引中:
- 对于一个复合多key索引,每一个索引最多可以包含一个数组。
- 在多于一个数组的情形下来创建复合多key索引不被支持。
另外谈下B树(即B-树)和B+树的区别,以及为什么mongodb用B-树而不用B+树。
首先B树结构(细节请自行查阅)如下图:
每个节点存有key对应的data, 每个节点上的n个key有n+1个子节点。
一个m阶的B树每个节点至少有m个孩子。
一棵含有N个总关键字数的m阶的B树的最大高度是log_ceil(m/2)(N+1)/2 + 1。
因此B数的高度相对会下降,而高度下降,定位数据会更快。由于B数节点中存数据,最快查询可为O(1)。
而B+树,如下图:
B+树的特点是,key的副本存储在内部节点,真正的key-data存储在叶子节点上。
n个key的节点指针域是n而不是n+1(mongodb 是n+1)
为了增加区间访问性,B+树的叶子节点两两相连,对于范围查询大大增加了友好度。
B树和B+树在磁盘访问这一点上
- B树因为key对应的data直接在节点上,因此查询到可以直接取出,最好的情况下查询非常快,且磁盘访问会下降。但是正因为如此,单个节点存储太大,如果没有访问到key,再要往下查询时,需要读入内存的数据量就很大。
- B+树因为内部节点只存key的副本,因此查询效率相对很稳定。但也是因为存储了key的副本,导致整个索引相对会比较大。
B树和B+树各有优缺点,对于mongodb来说,mongdb是一种文档型数据库,使用json格式保存数据,归属于聚合型数据库。适用于数据模型简单,性能要求高的场合。
而mysql之类的关系型数据库,区间访问是很常见的现象,B+树支持区间访问,因此mysql更适合用B+树。