在页面加载的等待时间里,数据库查询一般是最不确定的部分。索引优化的目的不是增加索引的数量,而是要使重要的查询走最短的路径。

定位从慢查询日志开始。设置阈值来采集耗时靠前的语句,按照调用次数和总耗时进行排序,优先处理高频且单次偏慢的查询,这类语句对整体等待的影响远大于偶发的极慢语句。拿到语句之后用执行计划查看访问类型以及扫描行数,全表扫描、临时表、文件排序三项同时出现的时候,改造收益最大。
联合索引的列顺序决定了它的可用性。等值条件在前,范围条件在后,排序列如果参与索引的话,需要和前序列保持连续,否则排序还需要额外的处理。覆盖索引把查询所需要的字段全部纳入到索引中,消除了回表随机读,在列表页这种只取少量字段的场景下效果很好。
索引失效的原因要一条条地排除。字段参与函数运算、查询值与字段类型不一致触发隐式转换、前导通配符匹配、多个条件用 OR 连接而部分列无索引,都会使已经建立的索引失去作用。改造的时候把运算移到常量一侧,统一参数类型,模糊查询改成后缀匹配或者引入独立的检索组件。
分页深翻也是另一种隐形的开销。当偏移量很大时,数据库仍然要扫描和丢弃前面的记录,以上一页最后一条记录的排序值作为起始点,或者先通过索引获取主键再关联明细,这样扫描量就可以降到常数级别了。
索引不是越多越好。每一个索引都会带来写和读的成本,长期没有命中过的索引应该被删除掉,功能相同的索引应该合并。统计信息过时会导致优化器选择错误的路径,定期更新、检查执行计划,优化才算完成。