MySQL

Mysql的几个灵魂拷问(四)

2020-10-17  本文已影响0人  千淘萬漉

Mysql前面已经把基础和原理部分铺垫的差不多了,现在要来讲讲的是Sql优化和调优部分了,这个基本是Mysql拷问系列最直接灵魂的环节了,优化这几乎是每场面试对话的最高潮部分,但这里又是一个开放的话题,能不能绽放出亮点,就看平时在调优方面下的功夫够不够了,数据库优化维度有四个:硬件、系统配置、数据库表结构、SQL及索引。这一篇的重点是放在SQL语句的优化上,表设计与大表优化方案则放在下一个篇文章中。

一、总优化思路

对于程序开发者来说,MySQL层优化一般可以遵从五个原则:

总结到 SQL 优化中,就如下三点:

二、索引优化策略


众所周知,索引类似于字典的目录,可以提高查询的效率。索引从物理上可以分为:聚集索引,非聚集索引。从逻辑上可以分为:普通索引,唯一索引,主键索引,联合索引,全文索引。这一部分主要总结Sql优化的一些技巧和规范,尤其对于一些还有歧义和误导性的说法做一个纠正。

1、合理建立覆盖索引

合理的覆盖索引,可以减少回表次数。表数据是放在一个聚集索引上的,而建立的索引为非聚集索引,非聚集索引的叶子节点存放索引键值,以及该索引键指向的主键。一般查找的过程是从非聚集索引上找到数据的主键,然后根据该主键到聚集索引上查找记录,这个过程称为回表。

如有下面这个sql

select uid, login_time from user where username = ? and passwd = ?

可以建立(username, passwd, login_time)的联合索引,由于 login_time的值可以直接从索引中拿到,不用再回表查询,提高了查询效率。

建立联合索引的时候注意将区分度最高的字段放在最左边!

2、union,or,in都能命中索引,建议使用in

新版Mysql中union,or,in都是会走索引的,所以如下三条sql语句都是等价的:

select * from article where id = 1
union all
select * from article where id = 2

select * from article where id in (1 , 2)

select * from article where id = 1 or id = 2

效率从高到低为union,in,or。in和union的效率差别可以忽略不计,所以直接建议使用in。union 比 union all 多一个去重逻辑,业务上知道已经有字段有唯一属性的话,直接用union all 会比 union 效率更高点儿。

exist和in的差别,并不是exist替换都比in要好:

负向条件索引不会使用索引,建议用in

负向条件有:!=、<>、not in、not exists、not like 等

-- 全表扫描
select * from article where id != 1 and id != 2

知道id的所有取值范围,可以改为类似如下形式

-- 走索引
select * from article where id in (0, 3, 4)

在索引列上进行运算或使用函数会走全表

在列上进行运算或使用函数会使索引失效,从而进行全表扫描。如下面例子在publish_time,id列上分别加上索引,publish_time为datetime类型,id为int类型:

-- 全表扫描
select * from article where year(publish_time) < 2019
-- 走索引
select * from article where publish_time < '2019-01-01'
-- 全表扫描
select * from article where id  + 1 = 5
-- 走索引
select * from article where id = 4

小心隐式类型转换

假设id为varchar类型,用int类型查会走全表。

-- 全表扫描
select * from article where id = 100
-- 走索引
select * from article where id = '100'

隐式类型转换在索引字段上做了函数操作,因此会全表扫描。

select * from article where id = 100
-- 等价于
select * from article where CAST(id AS signed int) = 100

那么如果id是int,执行下面这个语句是否会导致全表扫描呢?select * from article where id = '100',答案是会用到索引。

不建议使用%前缀模糊查询

%李,%李%都会导致全表扫描,非前导模糊查询可以使用索引

避免在where子句中进行null值判断

对于null的判断会导致引擎放弃使用索引而进行全表扫描。

经常更改,区分度不高的列上不宜加索引

区分度可以使用 count(distinct(列名))/count(*) 来计算
“性别”这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似。

多表关联查询时,小表在前,大表在后

在 MySQL 中,执行 from 后的表关联查询是从左往右执行的(Oracle 相反),第一张表会涉及到全表扫描。

调整 Where 字句中的连接顺序

MySQL 采用从左往右,自上而下的顺序解析 where 子句。根据这个原理,应将过滤数据多的条件往前放,最快速度缩小结果集。

查询是否存在可以加limit1

查询是否存在或者当查询确定只有一条记录时,可以加liimit1,让MySQL停止游标移动,提高查询效率

select uid from user where username = ? and passwd = ?

可改为:

select uid from user where username = ? and passwd = ? limit 1

Explain调优


说了这么多的优化总结,最重要的可能还是通过分析手段去,使用explain关键字可以模拟优化器执行SQL语句,号称SQL中的debug。通过explain可以知道mysql是如何处理sql语句,分析查询语句或者表结构的性能瓶颈。

explain + SQL

explain包含很多的Item,主要关注type,possible_key,key,row和Extra。
id:执行权重,查询多张表后对应的id越大优先级越高
type:查询使用的类型,system > const > eq_ref > ref > range > index > all

        system 只有一行数据,一般不出现
        const 通过索引一次就找到
        eq_ref  唯一性索引扫描
        ref  非唯一性索引扫描
        range 检索制定范围
        index 只遍历索引树
        all 全表扫描

table:表名
possible_key:表中存在的索引
key:本次查询使用的索引
row:找到目标大致所需的行数
Extra:

        using filesort 使用外部的索引排序
        using temporary 使用临时表保存数据
        using index 使用了覆盖索引
        using where 使用where条件
        using join buffer 使用连接缓存
        impossible  where where条件无结果
        select tables optimized away 最佳优化状态,无需遍历索引

优化思路还是沿着我们上面总结的那些来进行即可。

参考引用


1、这次被问懵了!搞定了这些SQL优化技巧,下次横着走
2、MySQL索引优化实战
3、巧用 explain 优化 MySQL 语句

上一篇 下一篇

猜你喜欢

热点阅读