深入浅出 MySQL:SQL 优化 - 常用的 SQL 优化

时间:2019-04-15
本文章向大家介绍深入浅出 MySQL:SQL 优化 - 常用的 SQL 优化,主要包括深入浅出 MySQL:SQL 优化 - 常用的 SQL 优化使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、大批量插入数据

MyISAM 表的插入优化,插入前先关闭索引更新。

alter table xxx DISABLE KEYS;                         -- 关闭索引更新

... -- 插入数据

alter table xxx ENABLE KEYS;

InnoDB 表的插入优化:

1)导入的数据按主键排列,效率更高。因为 Innodb 数据是按主键顺序排列的

2)关闭唯一性校验

SET UNIQUE_CHECKS = 0;

-- 插入数据

SET UNIQUE_CHECKS = 1;

3)如果应用使用自动提交(事务)的方式,则插入前关闭自动提交,有助于提高效率

SET AUTOCOMMIT = 0;

-- 插入数据

SET AUTOCOMMIT = 1;

二、优化 insert 语句

1)数据量较大时,采用批量插入代替多条insert语句

insert into xx values(1, 2), (3, 4), (5, 6) ...;

2)如果从不同客户端插入多行时,使用 insert delayed 提高速度。该语句让 insert 马上执行,数据放入内存的队列中,并没有真正写入磁盘,比每条语句插入更快。

3)将索引与数据文件分在不同磁盘存放(通过建表的选项)

4)对于MyISAM,增加 bulk_insert_buffer_size 值提高批量插入速度

5)Load data infile 比多条 insert 快近 20 倍。

三、优化 order by 语句

1)MySQL 两种排序方式

a. 有序索引顺序扫描直接返回有序数据

select customer_id from customer order by store_id;       -- Using index 效率高

b. 对返回结果进行排序

select * from customer order by store_id;                -- Using Filesort

通过explain查看Extra是否为Using index,Using index效率高于Using Filesort

Filesort 将取得的数据在 sort_buffer_size 系统变量设置的内存排序区进行排序,若内存装不下,将磁盘的数据分块,再对各个数据块进行排序,然后各个块合并成有序结果集。 sort_buffer_size 设置的排序区是每个线程独占的,在同一时刻,MySQL 存在多个 sort buffer 排序区

优化目标:尽量减少额外排序,通过索引直接返回有效数据。

下列 order by 语句将使用索引:

select * from xxx order by key_part1, key_part2 ...;            -- 同属于一个联合索引

select * from xxx where key_part1 = xxx order by key_part1 desc, key_part2 desc, ...;

下列 order by 语句将不使用索引:

select * from xxx order by key_part1 desc, key_part2 asc;   -- 混合desc与asc

select * from xxx where key_part2 = xxx order by key_part1;   -- where与order by的字段不一致

select * from xxx order by key1, key2;                      -- key1与key2属于两个不同的关键字

2)filesort 优化

对于 filesort 存在两种排序算法

两次扫描算法:第一次获取排序字段和行指针信息,第二次根据行指针获取记录(产生大量随机I/O),该算法内存开销较小。

一次扫描算法:一次取出所有满足条件的行,在排序区进行排序后直接输出结果。内存开销大,效率高。

a)适当加大 max_length_for_sort_data 使其使用更优化的排序算法(一次扫描)

b)适当加大 sort_buffer_size 尽量让排序在内存中完成,而不是通过创建临时表放在文件中进行。

c)select 具体字段 代替 select * ,可减少排序区的使用

四、优化 group by 语句

默认情况下,MySQL会对group by的所有字段进行排序,可禁止该排序:

select xxx from xxx group by xxx order by null;

五、优化嵌套查询

select * from customer where customer_id not in (select customer_id from payment);

select * from customer a left join payment b on a.customer_id = b.customer_id where b.customer_id is null;

-- join代替子查询效率更好,因为join不需要在内存中创建临时表。

六、优化Or条件

对OR的每个字段增加索引,否则执行时涉及的索引将都不使用

七、优化分页查询

limit 1000,20 :MySQL 会对所有进行排序,但仅需要返回20条,明显查询的代价很高

1)优化思路一:在索引上完成排序分页,然后通过主键再从元表查找其他所需要的字段

select film_id, description from film order by title limit 50,5;    -- Using Filesort

select a.film_id, a.description from film a inner join (select film_id from film order by title limit 50,5) b on a.film_id = b.film_id;          

-- Using index

因为--

select film_id from film order by title limit 50,5 会使用索引

select film_id, description from film order by title limit 50,5; 不会使用索引

2)将Limit n,m 转为 Limit m (适用于排序字段不会出现重复值)

select * from xxx order by abc limit 400, 10;

select * from xxx where id < xxx(第410行的id) order by abc desc limit 10;

八、使用SQL提示

select count(*) from xxx use index (xxx);               -- 建议MySQL使用索引XXX

select count(*) from xxx ignore index (xxx);            -- 让MySQL忽略使用索引XXX

select count(*) from xxx force index (xxx);             -- 强制让MySQL使用索引XXX,尽管效率很低