MySQL进阶之SQL优化
Posted 潇潇O
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL进阶之SQL优化相关的知识,希望对你有一定的参考价值。
1、索引的分类
普通索引(单列索引):一个索引只包含单个列,一个表可以有多个;
create index idx_brand_name on brand(name);
alter table brand add index idx_brand_name(name);
show index from brandG;
drop index idx_brand_name on brand;
唯一索引:索引列唯一,可以有NULL;
alter table brand add unique idx_brand_name(name);
复合索引:一个索引包含多个列
2、视图
一条select语句执行后结果集,虚拟表
创建视图:
create view view_class_student as select class.class_name,student.* from class,student where class.class_id = student.class_id;
把视图当作表
select * from view_class_student;
如果对视图进行更改,那么基表里的数据也会改变。
update view_class_student set gender=‘女‘ where stu_id=1001;
查看视图
show tables;
删除视图:
drop view view_class_student;
3、存储过程和函数
是经过事先编译并存储在数据库中一段SQL语句的集合,可以减少数据在数据库和应用服务器之间的传输。
函数:有返回值
过程:没有返回值
创建存储过程:
delimiter $
create procedure pro_test()
begin
select * from class;
end$
调用存储过程
call pro_test()$
查看数据库中所有存储过程
select name from mysql.proc where db=‘db1‘$
查看存储过程的状态信息
show procedure statusG$
查看某个存储过程的创建语法
show create proceduer pro_testG$
删除某个创建过程
drop procedure pro_test$
声明变量:
delimiter $
create procedure pro_test()
begin
declare i int defalut 10;
select concat(‘i=‘,i);
end$
变量赋值:
方法一:
create procedure pro_test01()
begin
declare num int defalut 0;
set num=num+10;
select num;
end$
方法二:
create procedure pro_test02()
begin
declare num int;
select count(*) into num from brand;
select concat(‘brand表中行数为‘,num);
end$
if-else语句,传入参数
create procedure pro_test04(in price int)
begin
declare desp varchar(50) default ‘ ‘;
if price >=50 then
set desp=‘高档‘;
elseif price >=20 and price <50 then
set desp=‘中高档‘;
else
set desp=‘普通‘;
end if;
select concat(‘价格‘,price,‘是‘,desp);
end$
传出参数
create procedure pro_test05(in price int,out desp varchar(50))
begin
if price >=50 then
set desp=‘高档‘;
elseif price >=20 and price <50 then
set desp=‘中高档‘;
elseset desp=‘普通‘;
end if;
end$
调用
call procedure pro_test05(50,@description)$ //@会话变量
select @description$
case语句
create procedure pro_test06(in mon int)
begin
declare result varchar(10);
case
when mon>=1 and mon<=3 then
set result=‘第一季度‘;
when mon>=4 and mon<=6 then
set result =‘第二季度‘;
when mon>=7 and mon<=9 then
set result=‘第三季度‘;
else
set result=‘第四季度‘;
end case;
select concat(‘月份是‘,mon,‘计算的结果是‘,result) as content;
end$
while循环
计算1加到100
create procedure pro_test07()
begin
declare i int default 1;
declare sum int default 0;
while i<=100 do
set sum=sum+i;
set i=i+1;
end while;
select sum;
end$
repeat循环
create procedure proc_test08(in n int)
begin
declare sum int default 0;
repeat
set sum=sum+n;
set n=n-1;
until n=0
end repeat;
select sum;
end$
loop循环
create procedure pro_test09(in n int)
begin
declare sum int default 0;
ins:loop
set sum=sum+n;
set n=n-1;
if n<=0 then
leave ins;
end if
end loop ins;
select sum;
end$
游标,用来存储查询结果集
create procedure pro_test10()
begin
declare res_id int (11);
declare res_name varchar(50);
declare has_data int default 1;
declare res_result cursor for select id,name from brand; //把select查询到的结果放在res_resul里面
DECLARE EXIT HANDLER FOR NOT FOUND set has_data=0; //如果获取不到数据就把has_data设为0
open res_result;
repeat
fetch res_result into res_id,res_name; //从结果集中获取一条数据分别赋值给res_id、res_name
select concat (‘id=‘,res_id,‘name=‘,res_name);
until has_data=0
end repeat;
close res_result;
end$
存储函数
create function count_factory( factory_name varchar(50))
RETURNS int
begin
declare num int;
select count(*) into num from brand where factory=factory_name;
return num;
end$
调用函数
select count_factory(‘一汽大众‘)$
4、触发器
在对表进行删除、增加、修改之前或者之后,触发并执行触发器中定义的SQL语句集合。
创建一个日志表
create table factory_logs(
id int(11) not null auto_increment,
operation varchar(20) not null,
operate_time datetime not null,
operate_id int(11) not null,
operate_params varchar(500),
primary key(id)
)engine=innodb default charset=utf8;
创建一个插入触发器
create trigger factory_insert_trigger
after insert
on factory
for each row
begin
insert into factory_logs(id,operation,operate_time,operate_id,operate_params)
values(null,‘insert‘,now(),new.id,concat(‘插入后(id:‘,new.id,‘,name:‘,new.name,‘)‘));
end$
创建一个修改触发器
create trigger factory_update_trigger
after update
on factory
for each row
begin
insert into factory_logs(id,operation,operate_time,operate_id,operate_params)
values(null,‘update‘,now(),new.id,concat(‘修改前(id:‘,old.id,‘,name:‘,old.name,‘)修改后(id:‘,new.id,‘,name:‘,new.name,‘)‘));
end$
创建一个删除触发器
create trigger factory_delete_trigger
after delete
on factory
for each row
begin
insert into factory_logs(id,operation,operate_time,operate_id,operate_params)
values(null,‘delete‘,now(),old.id,concat(‘删除前(id:‘,old.id,‘,name:‘,old.name,‘)‘));
end$
5、存储引擎
1、innodb
支持事务
#开启事务 mysql> start transaction; #插入数据 mysql> insert into factory(id,name) values(null,‘新的数据1‘)$ #在提交之前,其他客户端访问不到新的数据 mysql> commit;
支持外键
锁
存储方式:.frm文件存储的是表结构;idb文件存储数据文件和索引
2、MyISAM
不支持事务
文件存储方式
.frm文件 存储表结构
.MYD文件 存储数据
.MYI文件 存储索引
3、MEMORY
memory引擎是将数据存放在内存中,每个memory表实际对应一个磁盘文件.frm,数据存储在内存中。默认使用hash索引
4、MERGE
merge引擎表是一组myisam表的组合,这些myisam必须构建完全相同,merge表本身并没有存储数据,但是可以对merge表进行更新、删除、插入操作,其实这些操作是对内部的myisam表的操作。
6、SQL优化步骤
在应用的开发过程中,由于初期数据量小,开发人员在SQL语句时更注重功能实现,应用系统正式上线之后,随着数据量的急剧增长,初期的SQL语句可能会出现性能上的问题,从而影响整个系统的性能。此时我们就需要对它进行优化。
1、查看SQL执行频率
show global status like ‘Com_______‘;(7个下划线)
select、update、insert、delete、commit等待的执行频率,可以为SQL提供借鉴性的指标。
+---------------+-------+ | Variable_name | Value | +---------------+-------+ | Com_binlog | 0 | | Com_commit | 1 | | Com_delete | 1 | | Com_insert | 9 | | Com_repair | 0 | | Com_revoke | 0 | | Com_select | 69 | | Com_signal | 0 | | Com_update | 3 | | Com_xa_end | 0 | +---------------+-------+
show global status like ‘Innodb_row_%‘
查看Innodb表在进行增删改查后影响的行数
+-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 0 | | Innodb_row_lock_time_avg | 0 | | Innodb_row_lock_time_max | 0 | | Innodb_row_lock_waits | 0 | | Innodb_rows_deleted | 1 | | Innodb_rows_inserted | 8 | | Innodb_rows_read | 103 | | Innodb_rows_updated | 1 | +-------------------------------+-------+
2、定位低效率的SQL
1)show processlist
+----+------+----------------+------+---------+------+-------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+------+----------------+------+---------+------+-------+------------------+ | 4 | root | localhost:3821 | car | Sleep | 50 | | NULL | | 5 | root | localhost:6454 | car | Query | 0 | init | show processlist | +----+------+----------------+------+---------+------+-------+------------------+
表头说明:“Id”:用户登录时,系统分配的connection_id;
command:当前执行的命令,取值Sleep、Query、Connect
Time:保持当前状态持续时间;
State:当前执行SQL语句的状态;
Info:哪一条SQL;
2)慢查询日志
3、explain分析执行计划
explain select * from brand where=8;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ | 1 | SIMPLE | brand | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
表头说明:id表示select查询的序号,表示查询中操作表的顺序。id相同加载表的顺序从上到下
mysql> explain select * from course left join teacher on teacher.teacher_id=course.teacher_id; +----+-------------+---------+------+---------------+------+---------+------+------+----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+------+----------------------------------------------------+ | 1 | SIMPLE | course | ALL | NULL | NULL | NULL | NULL | 4 | NULL | | 1 | SIMPLE | teacher | ALL | PRIMARY | NULL | NULL | NULL | 5 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+---------+------+---------------+------+---------+------+------+----------------------------------------------------+
id不同,值越大优先级越高,id大的最先执行
mysql> explain select stu_id from (select * from score where score>=60) as B; +----+-------------+------------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+------+---------------+------+---------+------+------+-------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 8 | NULL | | 2 | DERIVED | score | ALL | NULL | NULL | NULL | NULL | 8 | Using where | +----+-------------+------------+------+---------------+------+---------+------+------+-------------+
mysql> explain select * from (select stu_id ,avg(score) from score group by stu_id having avg(score)>70) as B -> left join student on B.stu_id = student.stu_id; +----+-------------+------------+-------+---------------+------+---------+------+------+----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------+------+---------+------+------+----------------------------------------------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 8 | NULL | | 1 | PRIMARY | student | ALL | PRIMARY | NULL | NULL | NULL | 4 | Using where; Using join buffer (Block Nested Loop) | | 2 | DERIVED | score | index | fk_3 | fk_3 | 5 | NULL | 8 | NULL | +----+-------------+------------+-------+---------------+------+---------+------+------+----------------------------------------------------+
select_type说明:(效率从上到下)
SIMPLE:单表简单操作,效率最高。
PRIMARY:查询中若包含任何复杂子查询,最外层查询标记为PRIMARY;
SUBQUERY:在select或者where中包含了子查询;
DERIVED:将子查询的结果放在临时表中,在from列表中包含子查询。
UNION:第二个select 出现在UNION之后;
UNION RESULT:
mysql> explain select * from student where stu_id=1001 union select * from student where stu_id = 1002; +----+--------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ | 1 | PRIMARY | student | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL | | 2 | UNION | student | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | Using temporary | +----+--------------+------------+-------+---------------+---------+---------+-------+------+-----------------+
type说明:(效率从上到下)
NULL:不查询任何表和索引,效率最高;
const:表示通过索引一次找到;
eq_ref:使用唯一索引查询,使用主键的关联查询,关联查询出的记录只有一条;
ref:使用非唯一索引,匹配单个值;
index_merge:
range:where之后出现between、<、>、in;
index:查询整个B+树;
all:遍历全部记录;
possible_keys、key、key_len说明:
posslible_keys:可能用到的索引;
rows:在查询时扫描的行数;
key:实际用到的索引;
key_len:索引长度;
Extra说明:
using filesort:文件排序,效率低,需要优化,比如说建检索;
using temporary:使用了临时表,效率低;
using index:使用了索引,效率不错;
using index condition:使用了索引,并且从索引中拿走了数据,但是还要回表查询数据。比如使用"select*"索引查询一整行。
Using where; Using index:使用了索引,想要的列在索引中都能找到,不需要回表查询。
4、show profile分析SQL
可以分析出时间到底耗时在哪个阶段。
--查看当前有没有profiling mysql> select @@have_profiling; --有没有开启 mysql> select @@profiling; --开启profiling mysql> set profiling=1;
mysql> show profiles; +----------+------------+--------------------------------------+ | Query_ID | Duration | Query | +----------+------------+--------------------------------------+ | 1 | 0.00079975 | select * from student | | 2 | 0.00070025 | select * from stuent order by sname | | 3 | 0.00239075 | select * from student order by sname | +----------+------------+--------------------------------------+
--查看某个sql语句执行过程中每个线程的状态和消耗时间 mysql> show profile for query 3; +----------------------+----------+ | Status | Duration | +----------------------+----------+ | starting | 0.000053 | | checking permissions | 0.000005 | | Opening tables | 0.000014 | | init | 0.000014 | | System lock | 0.000014 | | optimizing | 0.000004 | | statistics | 0.000009 | | preparing | 0.000007 | | Sorting result | 0.000002 | | executing | 0.000002 | | Sending data | 0.000007 | --mysql线程开始访问数据并把结果返回给客户端 | Creating sort index | 0.002025 | | end | 0.000013 | | query end | 0.000007 | | closing tables | 0.000014 | | freeing items | 0.000123 | | cleaning up | 0.000081 | +----------------------+----------+
7、索引的使用
索引是数据库优化最常用的手段
一个数据量很大的表,如果安主键(id)查询,它的查询效率依然很高,但是如果按照name(既不是主键也没有索引),查询会很慢。解决办法:为name字段创建索引。
create index idx_name_student on student(name);
使用索引还要注意避免索引失效,可以使用explain查看key
1)全值匹配
create index idx_name_status_addr on table(name,status,addr);
select * from table where name=‘露露‘ and status=‘1‘ addr=‘上海市‘;
2)最左前缀法则,从索引最左列开始,并且不跳过中间的列。
--索引生效 select * from table where name=‘露露‘ ; --索引生效,索引长度会变长,用explain查看 select * from table where name=‘露露‘ and status=‘1‘; --索引失效,不符合最左前缀法则,用explain查看key值为NULL select * from table where status=‘1‘ and addr=‘上海市‘; --索引失效 select * from table where addr=‘上海市‘; --索引生效 select * from table where status=‘1‘ and addr=‘上海市‘ and name=‘露露‘ ; --索引生效,虽然跳过中间列,name走索引但是addr没有走索引 select * from table where name=‘露露‘ and addr=‘上海市‘;
3)范围查询右边的列不走索引
mysql> explain select * from test where name=‘露露‘and statue<‘1‘ and addr=‘上海‘; +----+-------------+-------+------+----------------------+----------------------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+----------------------+----------------------+---------+-------+------+--------------------------+ | 1 | SIMPLE | test | ref | idx_name_status_addr | idx_name_status_addr | 152 | const | 1 | Using where; Using index | +----+-------------+-------+------+----------------------+----------------------+---------+-------+------+--------------------------+
4)索引列上进行运算操作索引会失效
5)索引列要加单引号,否则索引失效
6)尽量使用覆盖索引,减少使用select*,避免用了索引还要回表查询。
mysql> explain select * from test where name=‘露露‘; +----+-------------+-------+------+----------------------+----------------------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+----------------------+----------------------+---------+-------+------+-----------------------+ | 1 | SIMPLE | test | ref | idx_name_status_addr | idx_name_status_addr | 152 | const | 1 | Using index condition | +----+-------------+-------+------+----------------------+----------------------+---------+-------+------+-----------------------+
7)避免在where后面使用or,索引会失效;
8)使用like并在首部进行模糊匹配,索引会失效;如果不得不需要在首部模糊匹配,可以使用覆盖索引,使查询的列都是索引列。
mysql> explain select * from test where name like ‘%喜‘; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | test | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
mysql> explain select name,addr from test where name like ‘%喜‘; +----+-------------+-------+-------+---------------+----------------------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+----------------------+---------+------+------+--------------------------+ | 1 | SIMPLE | test | index | NULL | idx_name_status_addr | 308 | NULL | 4 | Using where; Using index | +----+-------------+-------+-------+---------------+----------------------+---------+------+------+--------------------------+
8、SQL优化
1)大批量插入数据时,按主键顺序插入。innodb表是按主键顺序保存的。还可以在导入之前关闭唯一校验——"SET UNIQUE_CHECKS=0",倒入之后再开启——"SET UNIQUE_CHECKS=1",还可以在导入之前把自动提交事务改为手动提交事务——"SET AUTOCOMMIT=0"
从本地倒入数据到数据库中的某个表:
load data local infile ‘路径‘ into table ‘xxx‘ fields terminated by ‘,‘ lines terminated by ‘/n‘;
2)优化insert语句
--在事务中进行数据的插入 mysql> start transaction; --插入的数据有序 mysql> insert into test value(1004,‘橙橙‘,1,‘上海‘,1234); mysql> insert into test value(1005,‘洋洋‘,1,‘上海‘,1234); mysql> insert into test value(1006,‘哈哈‘,1,‘上海‘,1234); mysql> commit;
3)优化order by
--没有覆盖索引,没有通过索引直接返回排序结果,是using filesort mysql> explain select * from test order by name; +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ | 1 | SIMPLE | test | ALL | NULL | NULL | NULL | NULL | 7 | Using filesort | +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 1 row in set (0.00 sec) --覆盖索引,通过索引直接返回排序结果,是using index,这种排序方式效率高 mysql> explain select id,name from test order by name; +----+-------------+-------+-------+---------------+----------------------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+----------------------+---------+------+------+-------------+ | 1 | SIMPLE | test | index | NULL | idx_name_status_addr | 308 | NULL | 7 | Using index | +----+-------------+-------+-------+---------------+----------------------+---------+------+------+-------------+
总的来说,尽量减少额外排序,通过索引直接返回排序结果;order by 后面的字段要么都是升序,要么都是降序。但是有时候无法避免using filesort的出现,就要用到”一次扫描算法“,一次性取出满足条件的数据,放在sort buffer中进行排序后输出。这时候需要提高sort buffer size和max_length_sort_data这两个系统变量的大小,否则MySQL内部会选择”两次扫描算法“(效率相比一次扫描算法要低)
4)group by语句优化
group by也会用到排序,可以可虑去掉排序,同时也可以考虑为相应的列添加索引
--没有取消排序,没有添加索引 mysql> explain select addr,count(*)as num from test group by addr; +----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+ | 1 | SIMPLE | test | ALL | NULL | NULL | NULL | NULL | 6 | Using temporary; Using filesort | +----+-------------+-------+------+---------------+------+---------+------+------+---------------------------------+ 1 row in set (0.00 sec) --取消排序 mysql> explain select addr,count(*)as num from test group by addr order by null; +----+-------------+-------+------+---------------+------+---------+------+------+-----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------+ | 1 | SIMPLE | test | ALL | NULL | NULL | NULL | NULL | 6 | Using temporary | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------+ 1 row in set (0.00 sec) --添加索引 mysql> create index idx_addr on test(addr); --取消排序,并且有索引 mysql> explain select addr,count(*)as num from test group by addr order by null; +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ | 1 | SIMPLE | test | index | idx_addr | idx_addr | 152 | NULL | 6 | Using index | +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
5)or优化
or所关联的每一个字段都必须有索引,也不能用到符合索引,否则索引会失效。
--stu_id有索引,sname没有索引,索引失效 mysql> explain select * from student where stu_id=1001 or sname=‘张三‘; +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | student | ALL | PRIMARY | NULL | NULL | NULL | 4 | Using where | +----+-------------+---------+------+---------------+------+---------+------+------+-------------+
--创建了复合索引,但是索引失效 mysql> create index idx_sname_gender on student(sname,gender); mysql> explain select * from student where gender=‘男‘ or sname=‘张三‘; +----+-------------+---------+------+------------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+------------------+------+---------+------+------+-------------+ | 1 | SIMPLE | student | ALL | idx_sname_gender | NULL | NULL | NULL | 4 | Using where | +----+-------------+---------+------+------------------+------+---------+------+------+-------------+
用union替换or,我们来比较一下两者优劣
--or 走索引,但是 type 为range mysql> explain select * from student where stu_id=‘1003‘ or stu_id=1002; +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | student | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+---------+-------+---------------+---------+---------+------+------+-------------+ --union type 为 const,const的效率远高于range mysql> explain select * from student where stu_id=‘1003‘ union select * from student where stu_id =‘1001‘; +----+--------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+-------+---------------+---------+---------+-------+------+-----------------+ | 1 | PRIMARY | student | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL | | 2 | UNION | student | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | Using temporary | +----+--------------+------------+-------+---------------+---------+---------+-------+------+-----------------+
--or,虽然走了索引,但是type=all mysql> explain select * from student where stu_id=1001 or sname=‘王四‘; +----+-------------+---------+------+--------------------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+--------------------------+------+---------+------+------+-------------+ | 1 | SIMPLE | student | ALL | PRIMARY,idx_sname_gender | NULL | NULL | NULL | 4 | Using where | +----+-------------+---------+------+--------------------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) --union type为const、ref,效率高于all mysql> explain select * from student where stu_id=1001 union select * from student where sname=‘王四‘; +----+--------------+------------+-------+------------------+------------------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+-------+------------------+------------------+---------+-------+------+-----------------------+ | 1 | PRIMARY | student | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL | | 2 | UNION | student | ref | idx_sname_gender | idx_sname_gender | 61 | const | 1 | Using index condition | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | Using temporary | +----+--------------+------------+-------+------------------+------------------+---------+-------+------+-----------------------+
6)优化分页查询
场景:当数据表的数据非常大时,我们仅仅需要第3000000—3000010条记录,这时系统会对前3000010条记录进行排序,效率可想而知。
方法一:根据主键进行排序分页操作,select stu_id from student order by stu_id limit 3000000,10;
然后根据主键关联原表,select * from student ,(select stu_id from student order by stu_id limit 3000000,10) B where student.stu_id=B.stu_id;
方法二:适用于主键自增的表,且中间不能断号,把limit转到某个位置的查询
select * from student where id>3000000 limit 10;
7)认为干预系统使用索引
当有多个可用的索引,使用“use index 索引名”告诉MySQL使用某个索引,不会再去比较使用哪个索引。
select * from student use index(idx_sname) where sname=‘张三‘;
使用“ignore index 索引名”,忽略某个索引。
特殊场景:当系统认为走全表扫描比走索引快时,系统就会放弃使用索引。可以用“force index 索引名”告诉系统必须用到某个索引。
以上是关于MySQL进阶之SQL优化的主要内容,如果未能解决你的问题,请参考以下文章