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优化的主要内容,如果未能解决你的问题,请参考以下文章

SQL优化之SQL 进阶技巧(上)

MySQL进阶篇之SQL优化

mysql 开发进阶篇系列 5 SQL 优化

第31天MYSQL进阶-写优化- 插入优化(SQL 小虚竹)

超详细图解!MySQL进阶篇SQL优化-索引-存储引擎

6.MySQL优化---高级进阶之表的设计及优化