大概是这样,一个积分表 point ,我需要一次查询 100 个用户积分过期 expired 的数据。 表字段大概这样
字段 类型 索引
pointid varchar primary key
expired varchar index
userid varchar index
select userid from point where now()>= expired oser by id asc limit 0,100
我修改成了
select distinct userid from point a where exists (select userid from point b where a.userid =b.userid and now()>=expired )order by a.userid asc limit 0,100
为啥修改后的查询时间要比修改前快了 100 倍,修改前 8s ,修改后 0.07s 。 执行计划,修改前:index rows 约等于 160w 修改后:一个 range ,300 ,using index for group-by ;一个 ref ,16000;
1
plutome 2022-11-29 14:23:56 +08:00
同不懂
|
2
CEBBCAT 2022-11-29 14:28:28 +08:00
第一个是先过滤出*所有*过期的,然后按照 id (也不知道你说的是哪个 id )排序,然后再取前一百
后一个是在表上按照 userid 增序遍历,然后过滤出一百个后中止,返回结果 我建议按照主键遍历,利用 where pointid>${上一批结果中最后一个结果的 ID},来过滤数据 |
3
CEBBCAT 2022-11-29 14:33:27 +08:00
|
4
likunyan 2022-11-29 14:42:19 +08:00
第一条去掉 order by 看看
|
5
eijnix 2022-11-29 14:43:36 +08:00
你是不是用的 mysql 5.6 版本? 如果是的话可能是 mysql 的 order by + limit 的一个坑, 你这里可以把第一个 sql 的 limit 放的很大试试
前阵子写的: https://juejin.cn/post/7164423778033172517 |
8
chenqh 2022-11-29 15:21:54 +08:00
你这个应该是 order by 的问题吧,第二个是 order by user_id 不是 order by id 了
|
9
chenqh 2022-11-29 15:24:05 +08:00
还有 explain 呢?
|
10
CEBBCAT 2022-11-29 15:44:48 +08:00
@luxinfl
> 去掉也慢,而且改动了原来的逻辑,虽然这逻辑没卵用。。。 那直接去掉不就好了,保持最简。 > 上一批结果是啥意思。。不过我看这个业务每次找到 100 个用户就行了,两次任务重复了也没关系。。 你不是要分批查询,每次一百个吗?上一批就是上次那 100 个中最后一行的 pointid 。你指定了 pointid ,之前扫描过的行就不会再扫描了。你说的这个“两次任务重复了也没关系”我倒是有点没明白,是查询到了之后会立即做删除,所以下次查询之前表里已经没有这批数据了吗? 你得想想,怎么让整个任务周期中数据库引擎做尽量少的事。如果你每次都是从表的第一行开始查,那么你执行多少次 SQL ,第一行就得差多少词。这样的复杂度得奔 n^2 去了吧 |
12
luxinfl OP @CEBBCAT 这个定时任务只负责每次查 100 条满足的用户,貌似是加了锁,不会重复扫描到。。但是我后来又导入一批数据,发现 explain 一样,但是两条 sql 执行结果时间又差不多了。。。搞不懂
|
13
qinrui 2022-11-30 05:17:29 +08:00 via iPhone
在第一句的 where 后面加上 1=1 and 试一下
|