MySQL优化

一、单表、双表、三表优化

1、单表

   首先结论就是,range类型查询字段后面的索引全都无效

(1)建表

create table if not exists article(
id int primary key auto_increment,
author_id int not null,
category_id int not null,
views int not null,
comments int not null,
title varchar(255) not null,
content text not null
);
insert into article values(null,1,1,1,1,'','');
insert into article values(null,2,2,2,2,'','');
insert into article values(null,1,1,3,3,'','');

(2)未创建索引查询

explain select  id,author_id,views from  article
where category_id = 1
and comments > 1
order by views desc limit 1;
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
| 1 | SIMPLE | article | ALL | NULL | NULL | NULL | NULL | 3 | Using where; Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
-- 总结上面出现的情况:type=all,产生了全表扫描, 并且出现了Using filesort,使用了外部的索引排序,所以优化是必须的

(3)创建category_id,comments,views复合索引

  create index ind_article_ccv on article(category_id,comments,views);

  --再次执行如下指令:
explain select id,author_id,views from article
where category_id = 1
and comments > 1
order by views desc limit 1;
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+---------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+---------------------------------------+
| 1 | SIMPLE | article | range | ind_article_ccv | ind_article_ccv | 8 | NULL | 1 | Using index condition; Using filesort |
+----+-------------+---------+-------+-----------------+-----------------+---------+------+------+---------------------------------------+
--创建索引之后type=range, 但是Using filesort 依然存在.

索引创建了,为什么在排序的时候没有生效?

这是因为先排序category_id, 如果遇到相同的category_id,则再排序comments, 如果遇到相同的comments则再排序views,

当comments字段在联合索引处于中间位置时,因为comments>1条件是一个范围值,所以type=range,mysql无法再利用索引对后面的views部分进行检索.

即range类型查询字段后面的索引全都无效

(4)、创建category_id,views复合索引

   -- 删除上面创建的索引:
drop index ind_article_ccv on article;
-- 重新创建索引:
create index ind_art_cb on article(category_id,views);
-- 重新执行如下代码
explain select id,author_id,views from article
where category_id = 1
and comments > 1
order by views desc limit 1;
+----+-------------+---------+------+---------------+------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------------+---------+-------+------+-------------+
| 1 | SIMPLE | article | ref | ind_art_cb | ind_art_cb | 4 | const | 2 | Using where |
+----+-------------+---------+------+---------------+------------+---------+-------+------+-------------+
--完美!

2、两张表

记住一句话:左连接索引建在右表,右连接索引做在左表

select * from class left join book on class.card = book.card;

--这是左连接,那么索引应该建在book表中的card(create index X on book(card);)

3、三张表

select * from class left join book on class.card = book.card left join phone on book.card = phone.card;
--这里就可以见两个索引,一个在book表,一个在phone表中
-- 创建索引:
create index Y on book(card);
create index Z on phone(card);

二、经典案例分析

1、建表,插数据,建索引

--建表
create table test3(
id int primary key auto_increment,
c1 char(10),
c2 char(10),
c3 char(10),
c4 char(10),
c5 char(10)
);
--插入数据
insert into test3 values(null,'a1','a2','a3','a4','a5');
insert into test3 values(null,'b1','b2','b3','b4','b5');
insert into test3 values(null,'c1','c2','c3','c4','c5');
insert into test3 values(null,'d1','d2','d3','d4','d5');
insert into test3 values(null,'e1','e2','e3','e4','e5');
--查看数据
select * from test3;
-- 创建索引
create index ind_test3_c1234 on test3(c1,c2,c3,c4);
show index from test3;

2、分析下面索引情况

 explain select * from test3 where c1='a1';
explain select * from test3 where c1='a1' and c2='a2';
explain select * from test3 where c1='a1' and c2='a2' and c3='a3';
explain select * from test3 where c1='a1' and c2='a2' and c3='a3' and c4='a4';
explain select * from test3 where c1='a1' and c2='a2' and c3='a3' and c4='a4' and c5='a5';
--上面五条所以肯定都有用,而且越到下面越精准 -- 请执行如下问题SQL,分析会出现的问题
--(1)
explain select * from test3 where c1='a1' and c2='a2' and c4='a4' and c3='a3' ;
--四个字段索引都有效 -- (2)
explain select * from test3 where c4='a1' and c3='a2' and c2='a4' and c1='a3' ;
--四个字段索引都有效 -- (3)
explain select * from test3 where c1='a1' and c2='a2' and c3>'a3' and c4='a4';
--c1、c2、c3索引有效, -- (4)
explain select * from test3 where c1='a1' and c2='a2' and c4>'a4' and c3='a3';
--四个字段索引都有效 -- (5)
explain select * from test3 where c1='a1' and c2='a2' and c4='a4' order by c3;
--c1、c2有效,c3仅对排序有效 -- (6)
explain select * from test3 where c1='a1' and c2='a2' order by c3;
--c1、c2有效,c3仅对排序有效 -- (7)
explain select * from test3 where c1='a1' and c2='a2' order by c4;
--c1、c2有效,因为中间缺少c3,所以索引排序无效。会报:Using filesort -- (8)
explain select * from test3 where c1='a1' and c5='a5' order by c2,c3;
--c1有效,但是c2,c3用于排序,无filesort --(9)
explain select * from test3 where c1='a1' and c5='a5' order by c3,c2;
-- 只用了c1这个字段索引,但是由于c3,c2顺序颠倒了,所以无法使用索引排序,出现filesort --(10)
explain select * from test3 where c1='a1' and c2='a2' and c5='a5' order by c3,c2;
--因为排序字段c2已经是一个常量了,所以对于后面c2排序其实没有啥意义,所以不会出现filesort --(11)
explain select * from test3 where c1='a1' and c4='c4' group by c2,c3;
--用到了c1字段索引,但用到c2、c3排序 --(12)
explain select * from test3 where c1='a1' and c4='c4' group by c3,c2;
-- 索引字段顺序不正确,出现了Using temporary; Using filesort

三、in和exists区别

1、 优化规则: 小表驱动大表,即小的数据集驱动大的数据集

如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。 所以如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in。 反之如果外层的主查询记录较少,子查询中的表大,又有索引时使用exists。

    另外IN时不对NULL进行处理。

    -- 当A的数据集大于B的数据集时, in 优于 exists
select * from A where id in (select id from B);
--等价于:
for select id from B
for select * from A where A.id = B.id; -- 当A的数据集小于B的数据集时, exists优于in
select * from A where exists(select 1 from B where B.id = A.id);
--等价于:
for select * from A
for select * from B where B.id = A.id

2、not in 和not exists

   如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;而not extsts 的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。

四、Order by 排序优化

1、单路排序、排序

    order by 子句,尽量使用index方式排序,避免使用Filesort方式排序,尽可能再索引列上完成排序操作,遵照索引建的最佳左前缀, 如果不在索引列上。filesort算法:双路排序,单路排序。

(1)双路排序: mysql4.1之前使用的双路排序,字面意思就是扫描两次磁盘,从而得到最终的数据,读取行指针和order by 列,对它们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出. 从磁盘取排序字段,在buffer进行排序,再从磁盘取其它字段

(2)单路排序:从磁盘读取查询需要的所有列,按照order by列在buffer对它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据.并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存到内存中

2、结论:

(1)单路排序,优于双路排序
   (2)在sort_buffer中,单路要比双路占用更多的空间,因为单路是把所有的字段都取出来,有可能导致取出的数据总大小超出了sort_buffer的容量,导致每次只能取sort_buffer容量大小的数据,进行排序(创建tmp,多路合并),排完再取sort_buffer容量大小,再排…从而导致了多次IO. (本来想节省一次IO操作,反而导致了大量的IO操作,得不偿失)

3、优化策略

(1)增大sort_buffer_size参数设置
  (2)增大max_length_for_sort_data参数设置

4、why

(1)order by时,select * 时一个大忌,只查询需要的字段,这点非常重要。当查询的字段大小总和小于max_length_for_sort_data而且排序字段不是TEXT|BLOB类型时,会用单路排序,否则会用多路排序
       两种算法的数据都有可能超过sort_buffer的容量,超出之后,会创建tmp文件进行合并排序,导致多次IO,但是用单路排序算法的风险会更大一些,所以要提高sort_buffer_size
(2)尝试调高sort_buffer_size: 不管使用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去调高,因为这个参数是针对每个进程的
(3)尝试调高max_length_for_sort_data: 调高这个参数,会增加使用单路算法的概率,但是如果设置太高,数据总容量超出sort_buffer_size的概率就增大,明显症状是高的磁盘IO活动和低的处理器使用率

5、总结

--mysql有两种排序方式: 文件排序和扫描有序索引排序
-- mysql能为排序和查询使用相同的索引 index abc(a,b,c)
order by --能使用最左前缀
order by a
order by b
order by a,b,c
order by a desc,b desc,c desc --如果where使用索引的最前缀定义为常量,则order by能使用索引
where a=const order by b,c
where a=const and b = const order by c
where a=const order by b,c
where a=const and b > const order by b,c --不能使用索引进行排序
order by a asc,b desc, c desc /*排序顺序不一致*/
where g=const order by b,c /*丢失a索引*/
where a=const order by c /*丢失b索引*/
where a=const order by a,d /*d不是索引*/
where a in(…) order by b,c /*对于排序来说,in 相当于是范围查询*/
```

参考

1.索引优化

 想太多,做太少,中间的落差就是烦恼。想没有烦恼,要么别想,要么多做。上尉【16】

MySQL(3)---MySQL优化的更多相关文章

  1. mysql查询性能优化

    mysql查询过程: 客户端发送查询请求. 服务器检查查询缓存,如果命中缓存,则返回结果,否则,继续执行. 服务器进行sql解析,预处理,再由优化器生成执行计划. Mysql调用存储引擎API执行优化 ...

  2. MYSQL数据库的优化

    我们究竟应该如何对MySQL数据库进行优化?下面我就从MySQL对硬件的选择.MySQL的安装.my.cnf的优化.MySQL如何进行架构设计及数据切分等方面来说明这个问题. 服务器物理硬件的优化 在 ...

  3. 1229【MySQL】性能优化之 Index Condition Pushdown

    转自http://blog.itpub.net/22664653/viewspace-1210844/  [MySQL]性能优化之 Index Condition Pushdown2014-07-06 ...

  4. mysql中的优化, 简单的说了一下垂直分表, 水平分表(有几种模运算),读写分离.

    一.mysql中的优化 where语句的优化 1.尽量避免在 where 子句中对字段进行表达式操作select id from uinfo_jifen where jifen/60 > 100 ...

  5. MySQL 调优/优化的 100 个建议

    MySQL 调优/优化的 100 个建议   MySQL是一个强大的开源数据库.随着MySQL上的应用越来越多,MySQL逐渐遇到了瓶颈.这里提供 101 条优化 MySQL 的建议.有些技巧适合特定 ...

  6. 理解MySQL——索引与优化

    转自:理解MySQL——索引与优化 写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存 ...

  7. (转)MySQL配置文件mysql.ini参数详解、MySQL性能优化

    本文转自:http://www.cr173.com/html/18331_1.html my.ini(Linux系统下是my.cnf),当mysql服务器启动时它会读取这个文件,设置相关的运行环境参数 ...

  8. Mysql监控、优化

    一.查询语句的生命周期 1.MYSQL服务器监听3306端口 2.验证访问用户 3.创建MySQL线程 4.检查内存(Qcache),当查询命中缓存,MYSQL立刻返回结果,跳过解析.优化.执行阶段. ...

  9. MySQL配置文件mysql.ini参数详解、MySQL性能优化

    my.ini(Linux系统下是my.cnf),当mysql服务器启动时它会读取这个文件,设置相关的运行环境参数. my.ini分为两块:Client Section和Server Section.  ...

  10. mysql服务性能优化—my.cnf配置说明详解

    MYSQL服务器my.cnf配置文档详解硬件:内存16G [client]port = 3306socket = /data/3306/mysql.sock [mysql]no-auto-rehash ...

随机推荐

  1. 实现一个简易版RPC

    上篇博客主要介绍了dubbo的使用,这篇文章主要深入rpc的核心原理 准备知识: 1 java 网络编程(这里使用的bio) 2 java动态代理 3 反射 ===================== ...

  2. 【Mybatis】MyBatis之动态SQL(六)

    MyBatis 的强大特性之一便是它的动态 SQL,本章介绍动态 SQL 查看本章,请先阅读[Mybatis]MyBatis对表执行CRUD操作(三). 本例表结构 CREATE TABLE `emp ...

  3. CodeForces - 939A,解题报告

    题意:给出一个n个点有向图,问是否存在三个点,这三个点构成一个回路.n<=5000 模拟即可. 注意是必须三个点 多了居然不行. import java.util.*; public class ...

  4. Python从入门到精通之Forth!

    Python基本数据类型之列表 补充:range()方法 python2: range   立即创建 xrange  for循环的时候才一个一个创建 Python3: range     for循环的 ...

  5. Webpack-dev-server的proxy用法

    前言: devServer:{ contentBase:'./', proxy:{ // 当你请求是以/api开头的时候,则我帮你代理访问到http://localhost:3000 // 例如: / ...

  6. Flink 报错 "Could not find a suitable table factory for 'org.apache.flink.table.factories.StreamTableSourceFactory' in the classpath"

    先上代码: table = tablexx.select('*).tablexx.groupBy('x).select('x, xx.count ) tableEnvironment // decla ...

  7. 01 C语言程序设计--01 C语言基础--第3章 基本数据类型01

    01.1.3.1序言 00:02:17 01.1.3.2 C语言中的基本元素和常量的概念 00:08:54 01.1.3.3示例--常量 00:12:08 01.1.3.4变量的概念和命名规则 00: ...

  8. keil5到iar8的使用配置迁移

      1.关于头文件的包含. keil: ALT+F7——>C/C++ IAR:ALT+F7——>C/C++ Compiler——>Preprocessor,(高版本汇编需要包含的头文 ...

  9. appium 1.6.x版本去除安装Unlock、Setting

    (appium目录不知道可以查看appium运行日志) 修改目录/usr/local/lib/node_modules/appium/node_modules/appium-android-drive ...

  10. Unity编辑器扩展-Custom List, displaying data your way

    本文转自http://catlikecoding.com/unity/tutorials/editor/custom-list/ Custom List, displaying data your w ...