一、背景
- 线上存在业务,需要每天定时整理某个表A未处理的数据,并写入另外一张表B;
- 每天查询出不存在B表中且未处理过的A表数据;
- A表中的数据主键放入B表中,未设定B表对应索引;
- 数据量初始值大概在几千条;
- 根据网上书籍介绍及多数网友介绍,left join 优于 not exists 优于 not in,not in不走索引,所以最终选择left join完成该业务;
- 数据量大约在10万条数据时,已经无法查询出任何数据;
二、测试环境
- mysql版本 5.7.30
- 数据库建表sql
create table test_a( id int(11) primary key, user_name varchar(11) ) create table test_b( id int(11), user_name varchar(11) )
- test_a存在主键索引,test_b表无索引
- 插入数据存储过程sql
CREATE DEFINER=`root`@`%` PROCEDURE `NewProc`() BEGIN #Routine body goes here... declare id int(11); DECLARE i int(11) DEFAULT 0; DECLARE user_name varchar(11); while i <= 1000000 do set i = i + 1; set id = i; set user_name = CONCAT('test',i); insert into test_a VALUES (id, user_name); set i = i + 1; set id = i; set user_name = CONCAT('test',i); insert into test_b VALUES (id, user_name); end while; END
- 保证test_a和test_b各存在id互不相等500001条数据
三、测试结果
left join
-
测试sql
select count(0) from test_a a left join test_b b on a.id = b.id where b.id is null
1.1 测试sql分析
1.2 测试结果
无结果,数据查询时间过长无响应
not exists
- 测试sql
2.1 测试sql分析EXPLAIN select count(0) from test_a where not exists (select * from test_b where test_b.id = test_a.id)
2.2 测试结果
无结果,数据查询时间过长无响应
not in
- 测试sql
3.1 测试sql分析EXPLAIN select count(0) from test_a where id not in (select id from test_b)
3.2 测试结果
唯一一个出数据的,1.190s
四、优化处理
- 增加test_b 的id索引
- 查看对应结果
2.1 left join , 0.596s
2.2 not exists , 1.998s
2.3 not in , 1.172s
五、结果分析
- 使用left join, not exists优化not in 语句时,必须在存在索引的情况下使用,否则有可能长时间查询无结果;
- left join在有索引的情况下对于此类情况优化最明显;
- not exists不一定速度快过not in,exists的原理是先查询exists前面表返回的数据,根据数据结果,作为条件与exists后面的查询进行对比(查询一条,对比一条),如果满足后面的条件,则返回真,然后返回数据,否则返回假,无数据,not exists则与之相反。所以not exists快过not in的前提是exists前面的语句对应的表查询返回的数据量必须小于后面的表查询返回的数据量;
- not in还是会走索引,至于是否与mysql版本有关,暂时不做论证;
- 使用Left join,exists优化 in语句时,必须要小心是否能触发索引,否则得不偿失。