Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > Mysql多表关联不走索引的原因

Mysql多表关联不走索引的原因及分析

作者:Singinwind

这篇文章主要介绍了Mysql多表关联不走索引的原因及分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

刚入职第一天,有个大佬写了一个统计函数count(*)需要对两张表a,b做统计。咋一看挺简单的,可是表a有1000万条数据,表b有300万条数据。使用LEFT JOIN进行查询。结果,一直查询不出来,可能时间就很久了。然后,这个锅就甩给第一天入职的我(我???)。

接下来,就研究一下如何对海量数据的查询进行优化。

一、准备过程

1.创建两张表,表A large_student_tb(幼儿园大班学生哈哈):1000万条。表B samll_student_tb(小班学生orzzzzzzz):300万条。不建立索引的情况。

a,建立存储过程:插入1000万条数据。n=10000000+1//为结束判断条件

-- 创建存储过程
 
DROP PROCEDURE IF EXISTS my_insert;
CREATE PROCEDURE my_insert()
BEGIN
   DECLARE n int DEFAULT 1;
        loopname:LOOP
            INSERT INTO `large_student_tb`(`id`,`username`,`password`) VALUES ( n,CONCAT('myname',n),CONCAT('password',n));
            SET n=n+1;
        IF n=1000000+1 THEN
            LEAVE loopname;
        END IF;
        END LOOP loopname;
END;
 
 
-- 执行存储过程
CALL my_insert();
 
 
-- 数据插入成功后修改表模式InnoDB 时间稍微久点
 alter table `large_student_tb` engine=InnoDB;

鹅,确实很慢了。跑了1000s还没有跑完

继续让它跑一下吧。

笑了,这么久跑完了。可怜的电脑~~

 

b.查询一下条数

SELECT COUNT(*) FROM LARGE_STUDENT_TB

??懵了,是100万条??我少写了一个零。

为了科学的严谨。我还得再跑900万条。1万s??

 先记录一下,100w条:

查所有:1.3s~1.5s。

查某条 username999999:0.6s

继续插入表剩下的900万条。。来把英雄联盟吧哈哈哈哈

还是先查询一下如何进行表的迁移吧。因为预期想来,1000万的表,加入索引,会加快查询速度和聚簇函数的计算速度。从而进行优化。但是我之前在办公室试过,往一张1000万的表里面加索引,速度很慢很慢,第一个想法是先建立一个一样的表,先加上索引,再进行表的迁移。相关操作如下

1.表的迁移:

insert into db1.table1 select * from db2.table2  #完全复制
 
-- 创建存储过程
 
DROP PROCEDURE IF EXISTS my_insert;
CREATE PROCEDURE my_insert()
BEGIN
   DECLARE n int DEFAULT 1000000+1;
        loopname:LOOP
            INSERT INTO `large_student_tb`(`id`,`username`,`password`) VALUES ( n,CONCAT('myname',n),CONCAT('password',n));
            SET n=n+1;
        IF n=10000000+1 THEN
            LEAVE loopname;
        END IF;
        END LOOP loopname;
END;
 
 
-- 执行存储过程
CALL my_insert();
 
 
-- 数据插入成功后修改表模式InnoDB 时间稍微久点
 alter table `large_student_tb` engine=InnoDB;

二、比较

1.对增加了索引和没有索引的效果。查询速度是指数级别的增加,如下

SELECT * FROM LARGE_STUDENT_TB a where a.username = 'myname1002554'  

-- index before 5.532s  --index after 0.037s

我查询 username。没有对username增加索引的时候,需要5s才能从千万数据级别中查出某一条数据,增加了username字段为索引,秒查询。

2.索引增加后所占据的空间大小,以及表本身的空间大小

1.查询表的大小

select concat(round(sum(DATA_LENGTH/1024/1024),2),'MB') as data  from TABLES where table_schema='simonsdb' and table_name='large_student_tb';

550.00MB

2.查询该索引的大小

SELECT CONCAT(ROUND(SUM(index_length)/(1024*1024), 2), ' MB') AS 'Total Index Size' FROM TABLES  WHERE table_schema = 'simonsdb'
and table_name='large_student_tb'

235.94MB

如上,索引的增加会带来存储空间的增加。但是速度却是很快。以牺牲空间换取这么大倍数的时间效率,值得。

3.多表连接查询的比较

-- 两表联查

EXPLAIN SELECT * FROM small_student_tb a left join  large_student_tb  b on a.username = 'myname1002554'

---这个查询不出来,有索引也没有用。待优化

SELECT * FROM small_student_tb a left join  small_student_tb  b on a.username = b.username;

--这个可以查询出来,用时间55s左右,需要优化

3.1 多表查询没有用上索引的原因。 如上3所显示,有个多表查询。我们需要用EXPLAIN关键字来排查原因。

1.单表可快速查询EXPLAIN

EXPLAIN SELECT * FROM LARGE_STUDENT_TB a where a.username = 'myname1002554' 

2.两表连接查询ON。可以查出来,但是速度很慢55s。EXPLAIN一下

EXPLAIN SELECT * FROM small_student_tb a left join  small_student_tb  b on a.username = b.username

我们可以看到表a 也就是 small_student_tb在possible_keys中,没有用上索引。是什么原因导致它没有用上索引。会不会用上了以后就变快了?

综合比较,得出的结论是,左连接会做全盘扫描。类型为ALL,自然就不能使用索引了。因为左表a要全部扫描一遍。

3.查询不出来的语句。

EXPLAIN SELECT * FROM small_student_tb a left join  large_student_tb  b on a.username = 'myname1002554'

三、千万级别的数据查询个人优化建议

1.加索引。千万级别数据查询需要增加索引,索引在数据越多的情况下,效率越加明显

2.单独查表。两张千万级别的表查询,不建议用联表查。查一张结果,输出一个数据。去查询另外一张。

3.实在需要多表联查,应该注意两张表的字符编码级别是否相同。

四、MYSQL多表查询的区别

1.笛卡尔积:CROSS JOIN

笛卡尔积就是将A表的每一条记录与B表的每一条记录强行拼在一起。所以,如果A表有n条记录,B表有m条记录,笛卡尔积产生的结果就会产生n*m条记录。下面的例子,t_blog有10条记录,t_type有5条记录,所有他们俩的笛卡尔积有50条记

2.内连接INNER JOIN

内连接INNER JOIN是最常用的连接操作。从数学的角度讲就是求两个表的交集,从笛卡尔积的角度讲就是从笛卡尔积中挑出ON子句条件成立的记录。有INNER JOIN,WHERE(等值连接)         

SELECT * FROM t_blog INNER JOIN t_type ON t_blog.typeId=t_type.id;

SELECT * FROM t_blog,t_type WHERE t_blog.typeId=t_type.id;

3.左连接LEFT JOIN

左连接LEFT JOIN的含义就是求两个表的交集外加左表剩下的数据。依旧从笛卡尔积的角度讲,就是先从笛卡尔积中挑出ON子句条件成立的记录,然后加上左表中剩余的记录(见最后三条)。

SELECT * FROM t_blog LEFT JOIN t_type ON t_blog.typeId=t_type.id;

左边的表格t_blog会全部输出来,右边的表格,没有的数据会为NULL 

4.右连接RIGHT JOIN

同理右连接RIGHT JOIN就是求两个表的交集外加右表剩下的数据。

5.外连接:OUTER JOIN

外连接就是求两个集合的并集。从笛卡尔积的角度讲就是从笛卡尔积中挑出ON子句条件成立的记录,然后加上左表中剩余的记录,最后加上右表中剩余的记录。另外MySQL不支持OUTER JOIN,但是我们可以对左连接和右连接的结果做UNION操作来实现。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文