背景
众所周知,数据库建立索引后速度会有极大的提升.但前提是正确的建立索引,否则数据库查询就不会走索引,而导致慢查询,不仅影响当前服务的性能,甚至可能影响其他使用同一数据库的服务的性能.
而索引分为独立索引和组合索引.
独立索引(Single field index)
对单独字段建立索引,例如:
1 | { "name": 1 } |
1 表示升序索引,-1 表示降序索引
查询条件为单一条件时有效,当查询条件为多个条件组合时,不会一一匹配独立索引
独立索引的 sort 排序
对于独立索引来说,由于 MongoDB index 本身支持顺序查找,所以对于独立索引来说不管你是{ sort: 1}
还是{ sort: -1 }
都是一样的
组合索引(Compound index)
当查询条件为多个条件组合时,需要建立组合索引,例如:
1 | { |
组合索引中条件的顺序对索引的性能有至关重要的影响,比如索引 {userid:1, score:-1} 首先根据 userid 排序,然后再在每个 userid 中根据 score 排序。
使用组合索引需要满足 prefix 原则
Index prefix 是指组合索引字段的左前缀子集,考虑以下索引:
1 | { "item": 1, "location": 1, "stock": 1 } |
复制代码这个索引包含以下 index prefix:
1 | { item: 1 } |
复制代码所以只要语句满足 index prefix 原则都是可以支持使用组合索引的:
1 | db.products.find( { item: "Banana" } ) |
复制代码相反如果不满足 index prefix 则无法使用索引,比如以下 field 的查询:
{ location: 1 }
{ stock: 1 }
{ location: 1, stock: 1 }
由于 index prefix 的存在,如果一个 collection 既有 {a:1, b:1} 索引 ,也有 {a:1} 索引,如果二者没有稀疏或者唯一性的要求,独立索引是可以移除的。
所以建立组合索引时需要把出现概率较高的条件放在前面,或者为一个查询建立多个组合索引.
组合索引的 sort 排序
前文说过独立索引的 sort 顺序无关紧要,但是组合索引则完全不同。
sort 条件必须要和索引完全相同或者完全相反才会走索引,否则不走索引.
理解 field 顺序对索引的影响
索引的真正作用是帮助我们限制数据的选择范围,field 数值不同的比较少的放在前面,能更好的缩小范围,比如渠道编号distributriNo,一共只有几十种不同的数值,放在最前面,一开始的时候就已经把范围缩小到这个渠道编号下的数据,然后再根据支付环境,支付类型,订单状态等条件,不断的缩小范围.
相反的,如果把 _id作为索引,那从一开始就要遍历整个表,对缩小搜索范围就没有什么帮助了.
所以,唯一索引要放在组合索引中最后的位置.
在以上的索引中,r1 就是把范围缩小到 origin后就去查询 id 了,相当于 12959 条start_time:1, end_time:1
都符合条件的,但是 r2 就是把范围再缩小到start_time:1, end_time: 1, origin: 1, orientation: 1
,剩下符合条件的数据就剩 2700 多条了,明显范围更小了
刚刚说的建立组合索引需要把出现概率较高的条件放在前面,但是还要考虑这个条件在数据库中数值的种类有多少种,如果有很多种数值的话,就要另外考虑是不是要再单独建立索引
非 index prefix 的排序
考虑索引 { a: 1, b: 1, c: 1, d: 1 },即使排序的 field 不满足 index prefix 也是可以的,但前提条件是排序 field 之前的 index field 必须是等值条件,
上面表格 r1 的排序 field 是 b 和 c,a 是 index field 而且在 b 和 c 之前,可以使用索引;r3 的排序中 b 是范围查询,但是 b 之前的 a 用的也是等值条件,也就是只要排序 field 之前的 field 满足等值条件即可,其它的 field 可以任意条件。
也就是说如果db.data.find( { a: { $lt: 3} } ).sort( { b: 1 } )
这个时候 b 前面的 a 不满足等值条件,就不会走索引了
模糊匹配时如何走索引
mongoDB 模糊查询关键字 $regex
如果不走索引对性能的影响非常大.
1 | where[key] = new RegExp('^' + where.regex[key]); |
在需要模糊匹配的字符串前面加上 ‘^’后这个模糊查询就可以走索引了,速度会快非常多.
参考资料: 掘金-wecatch
TTL 索引
某些数据需要定期删除,或者说过期删除,在MongoDB就要用到TTL索引。
1 | db.collection.createIndex({createdAt:1},{expireAfterSeconds:60*60*24*30*3}) // 90天后自动删除 |
原理
MongoDB服务器每分钟检查一次TTL索引,有TTL索引的字段服务器会进行计算:当前服务器时间-字段时间>=expreAfterSeconds的秒数的时候,就会执行删除该数据。所以如果是对数据做过期删除操作,建议把索引建立在 createdAt 这样的字段上.
注意事项
- TTL索引一样可以提高查询速度。
- TTL索引只能用于单字段,不能创建复合索引。
修改 TTL 过期时间
1 | db.runCommand({ |