微信公众号:molashaonian
id
),idx_status_ct
(status
, created_time
)SELECT *
FROM rop
WHERE `status` IN (0, 3)
AND is_delete = 0
LIMIT 112000, 1000
显然,这条SQL之所以慢是因为分页查询,到后面需要跳过的数据太多了,而且 status 状态的区分的也不是很高,所以越往后查询就越慢
SELECT *
FROM rop
WHERE `status` IN (0, 3)
AND is_delete = 0
AND created_time >= '2020-05-13 18:31:35.0'
AND id > 17735072
LIMIT 1000
优化点:
a. 对于这种分页慢查,一般采取分页得到的最后一条数据的自增ID作为查询条件来替代 offset,避免跳过数据;
b. 同时时间范围的比较,创建时间 created_time 等同于 自增ID,而 rop 表刚好有联合索引 (status
,created_time
) ,所以此处还增加 created_time 作为查询条件,使得 id 字段不需要 索引下推 就能过滤出来,直接通过联合索引过滤出需要回表的数据,大大减少了回表的数据量
id
),idx_pc_code_status
(pc_code
, status
) ,idx_last_update_time
(last_update_time
)SELECT *
FROM apc
WHERE pc_code = 'H11067'
AND is_delete = 0
AND is_done = 0
AND STATUS <> 64
从索引,SQL来看这条语句只能走到联合索引 idx_pc_code_status 的第一节点索引;status 范围查询索引失效
SELECT MAX(id)
FROM apc
WHERE last_update_time = '2020-05-13 18:31:35';
SELECT *
FROM apc
WHERE pc_code = 'H11067'
AND is_delete = 0
AND is_done = 0
AND status IN (65, 71, 73, 72, 74, 76, 75, 81, 82, 85, 86, 87, 88, 89, 90, 91)
AND id > 19331461
优化点:
a. 首先业务场景同一个 pc_code 下 is_done 为非完成的 肯定是只有一个,所以先通过 Redis 缓存上一次该 pc_code 完成的最后 last_update_time,id; 当然第一次查询肯定还没缓存,所以 id 作为查询条件得先判空,但是之后的查询就可以获得上一次的 id,并且把最新查询获取的 id 更新到缓存中,这里还需注意 MAX(id) 可能为 null,这里可以重试多几个时间节点就可以了,第一个SQL走索引 (last_update_time
) 不用回表;
b. status <> 64,不等于 64,但是范围查询索引失效,所以就替换成 IN (64以外的其他状态),加上 id 这样就可以把索引 (pc_code
,status
) 用到极致
id
),idx_pc_code
(pc_code
)SELECT *
FROM apc
WHERE pc_code = 'H19941'
AND is_delete = 0
ORDER BY create_time DESC
LIMIT 1
该SQL走到索引 idx_pc_code,由于进行了 ORDER BY create_time,导致了文件排序 Using filesort
SELECT MAX(id)
FROM apc
WHERE (pc_code = 'H19941' AND is_delete = 0);
SELECT *
FROM apc
WHERE id = 19448001
优化点:a. 先根据条件查询出 MAX(id),再根据 id 查询数据,规避文件排序
id
),idx_pno
(pno
),idx_create_time
(create_time
),idx_aid
(aid
),idx_flo
(flo
)SELECT MIN(pe.pno)
FROM ape pe
WHERE pe.create_time >= '2020-03-06 00:00:00'
AND pe.create_time <= '2020-03-08 23:59:59'
AND pe.aid = 50
AND pe.flo = '1'
AND pe.is_delete = 0
该SQL的查询条件涉及到三个单列索引,create_time,aid,flo,这里边有可能走到索引情况是 create_time 或者 aid 与 flo 的索引合并 index merge
SELECT MIN(pe.id), MAX(pe.id)
FROM ape pe
WHERE pe.create_time >= '2020-03-06 00:00:00'
AND pe.create_time <= '2020-03-08 23:59:59'
SELECT MIN(pe.pno)
FROM ape pe
WHERE pe.create_time >= '2020-03-06 00:00:00'
AND pe.create_time <= '2020-03-08 23:59:59'
AND pe.aid = 50
AND pe.flo = '1'
AND pe.is_delete = 0
AND pe.id >= minId AND pe.id <= maxId
优化点:a. 先根据 create_time 查询出 MIN(id),MAX(id);再把 minId, maxId 作为查询条件,使得 索引合并(aid & flo)后还能再通过 id 过滤减少回表的数据;
当然它还是有可能走 create_time 索引,这就看走哪个索引的区分度更高,选择更优的索引。
id
),idx_wc_sw
(wc
, sw
)SELECT MIN(create_time)
FROM apcd
WHERE sw IN (1, 2, 3, 27, 30)
AND wc IN (
'S0887',
'S0888',
'S0889',
'S1095'
)
GROUP BY wc, sw
该SQL的查询条件,group by 都使用了联合索引 idx_wc_sw,但查询的是 MIN(create_time),所以还是要回表
SELECT MIN(id)
FROM apcd
WHERE sw IN (1, 2, 3, 27, 30)
AND wc IN (
'S0887',
'S0888',
'S0889',
'S1095'
)
GROUP BY wc, sw;
SELECT create_time WHERE id IN(minId1,minId2,minId3)
优化点:a. 因为 create_time 等同于 自增ID,所以先根据联合索引 idx_wc_sw 查询出 MIN(id),无需回表;再根据这些 minId 查询出 create_time
补充总结
推荐阅读
Java 全栈知识体系
下篇笔录
ThreadPoolExecutor
molashaonian