建表
```sql
CREATE TABLE `process_sku_overflow` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '新表自增id',
`pc_id` bigint(20) NOT NULL COMMENT 'pc仓ID',
`process_date` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '加工日期',
`process_sku_id` bigint(20) NOT NULL COMMENT '成品sku ID',
`actual_overflow_rate` decimal(19,4) DEFAULT NULL COMMENT '成品实际溢出率',
`ctime` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`utime` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_pc_process_sku` (`pc_id`,`process_sku_id`),
KEY `idx_sku_id` (`process_sku_id`),
KEY `date` (`process_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='成品溢出率表'
```
线上表50w行
线上表当前索引:
主键索引、pc_id+process_sku_id组合索引、process_sku_id单独索引
慢sql语句如下,执行时间平均300ms,低于公司标准100ms,故需优化
```sql
SELECT a.pc_id AS pc_id, a.process_sku_id AS process_sku_id, a.actual_overflow_rate AS actual_overflow_rate FROM process_sku_overflow a WHERE pc_id = ? AND process_sku_id IN (?) AND process_date = ( SELECT max(process_date) FROM process_sku_overflow WHERE process_sku_id = a.process_sku_id )
```
主查询rows 308,且process_date匹配不上索引,效率较低
子查询用到process_sku_id单独索引,主查询用到pc_id+process_sku_id组合索引,综上,建立process_sku_id + pc_id组合索引即可替代原先的两个索引;
SELECT * FROM `process_sku_overflow` ORDER BY `id` DESC LIMIT ?主键索引排序,效率没问题
SELECT `pc_id`, `pc_name`, `process_date`, `process_sku_id`, `process_sku_name` , `category?_name`, `category?_name`, `category?_name`, `category?_name`, `actual_overflow_rate` FROM process_sku_overflow WHERE ? = ?where 1=2,瞬间返回,不涉及数据检索,效率很高
综上,考虑两种方案:
1.process_sku_id+process_date+pc_id联合索引 +删除现有的pc_id+process_sku_id索引
2.process_sku_id+process_date+pc_id+ actual_overflow_rate覆盖索引 +删除现有的pc_id+process_sku_id索引
以及考虑是否可以删除原索引,暂时先新增,目前表数据量50w较少,索引三个倒是也不用细究去删除,稳妥起见先并存,后续如果表数据量过多(千万级别),考虑删除process_sku_id单独索引单独索引;
采用方案二建立覆盖索引后,子查询直接索引覆盖,主查询也在索引下推的帮助下实现了索引覆盖,性能提升
下面是过程的一些学习感悟记录,由于多次修改sql条件去测试Extra,故后续sql的explain结果均以**选中部分**执行,请勿认为每次都是执行的全部sql字句;
case1:匹配上索引,但是select 的字段不被索引覆盖
Extra信息为NULL,表示该查询只需通过索引匹配即完成了整个筛选过程,但是索引并没有包含需查询的所有字段,因此还需回表查询;
case1.1 在上一个sql语句中,将process_sku_id in括号里的数据增加到3个,成为范围查询
此时出现using index condition,即ICP,原因是此时引擎层会主动做索引下推过滤后回表查询数据给到server层用户,无需因为遇到范围查询而仅仅匹配pc_id后直接回表拿数据给server层过滤process_sku_id,减少引擎层返回过多的回表数据给到server层,“下推”其实是指原本需要在server层的过滤放到了引擎层;想了解ICP详情可以参考https://blog.csdn.net/b1303110335/article/details/105831083、https://blog.csdn.net/meser88/article/details/120953207或者百度搜“mysql 索引下推”;
case 2:如果select只查询pc_id和process_sku_id,即所需的字段都在索引中
此时应该由于索引覆盖的缘故,无需回表,直接using index;然而出现了using index; using where。各方查询,这是由于虽然索引已经覆盖了所有的字段无需回表,但此处的using where跟using index同时出现表示的是虽然索引覆盖了但在使用索引过滤时存在范围查询或者like匹配,在索引层面也经历了类似where条件的匹配,若是没有in的范围查询,则Extra显示索引覆盖;
case3:此时若在where条件中加入process_date字段,则当前索引无法覆盖需回表,则此时在索引层面由于范围查询先过滤了一下,属于using index condition,再回表查询去过滤process_date条件,因此还会有using where;