数据库
首页 > 数据库> > 数据库调优-04 (特定语句调优、表结构设计优化)

数据库调优-04 (特定语句调优、表结构设计优化)

作者:互联网

目录

五、特定语句调优

1、jion 语句

1.1 jion语句种类:

1.2 join 算法

1.2.1 Nested-Loop Join (NLJ)

嵌套循环join:实际上就是简单粗暴的嵌套循环,如果table1有1万条数据,table2有1万条数据,那么数据比较的次数=1万 * 1万 = 1亿次,这种查询效率会非常慢。
image

1.2.2 Block Nested-Loop Join (BNUJ)

缓存块嵌套循环join:通过一次性缓存多条数据,把参与查询的列缓存到Join Buffer 里,然后拿join buffer里的数据批量与内层表的数据进行匹配,从而减少了内层循环的次数(遍历一次内层表就可以批量匹配一次Join Buffer里面的外层表数据)。
image
扫描次数计算公式:
(S* C)/join_buffer_size + 1

使用join buffer的条件:

explain查询结果中,Extra 列出现 'Using join buffer (Block Nested Loop)' 表示使用了BNLJ。

2.2.3 Batched Key Access Join (BKA)

批量键值访问,由MySQL5.6引入,BKA的基石: Multi Range Read ( MRR )

什么是MRR?
select * from salaries where from_date <='1980-01-01';
这条语句的查询结果可能会产生随机IO,应为数据是按照主键排列,而不是from_date字段排列的。如果开启MRR,那么会读取结果的主键索引,并按照主键索引排序,排序完成后再到表数据里读取数据,这样读出来的数据就是顺序IO,性能会比随机IO好很多。
: MRR核心:将随机IO转换成顺序1O,从而提升性能。

MRR参数:

show variables like '%optimizer_switch%';
show variables like '%read_rnd_buffer_size%';

set optimizer_switch ='mrr_cost_based=off';

一旦开启MRR,会在extra里面展示Using MRR

BKA流程;
image

参数:

-- 默认是关闭的,开启语句:
set optimizer_switch ='batched_key_access=on';

当使用BKA的时候,会在extra里面展示Using join buffer (Batched Key Access)

2.2.4 HAHS JOIN

注意:

1.3 JOIN调优原则

1、用小表驱动大表
image
看上图,先执行的就是驱动表,explin的结果就是按执行顺序展示的。

注:用小表驱动大表一般无需人工考虑,MySQL关联查询优化器会自动选择最优的执行顺序。如果优化器抽风失效,可使用STRAIGHT-JOIN,强制先读取左边的表,再读取右边的表。

2、如果有where条件,应当要能够使用索引,并尽可能地减少外层循环的数据量

3、join的字段尽量创建索引
注意:当join字段的类型不同时,索引无法使用

4、尽量减少扫描的行数(explain-rows)

5、参与join的表不要太多
阿里编程规约建议不超过3张。如果业务需要join多张的表,可以根据代码逻辑适当拆分。

6、如果被驱动表的join字段用不了索引,且内存较为充足,可以考虑把join buffer设置得大一些。

2、limit 语句

有这样一条sql

select *
from employees
limit 300000,10;

查询第一页的时候,花费92ms,查询第300001页的时候,花费174ms

优化方案

方案1:覆盖索引 (108ms)

select emp_no
from employees
limit 300000,10;

方案2:覆盖索引+join(109ms)

select *
from employees e
inner join (select emp_no from employees limit 300000,10) t
on e.emp_no = t.emp_no; -- 连接字段都叫emp_no,这行可以简写成 using (emp_no);

方案3:覆盖索引+子查询(126ms)

select *
from employees
where emp_no >=
      (select emp_no from employees limit 300000,1)
limit 10;

方案4:范围查询+limit语句

select *
from employees
limit 10;

select *
from employees
where emp_no > 10010
limit 10;

本例是拿第二页数据,首先查询第一页数据,拿到最后一个数据id值比如说是10010,再根据这个id值去拿第二页数据。这种方式扫描的行数永远都只有10行。

方案5:如果能获得起始主键值 & 结束主键值

select *
from employees
where emp_no between 20000 and 20010;

方案6:禁止传入过大的页码
从业务层面解决。

3、count 语句

count(*)

为什么?
innodb非主键索引:叶子节点存储的是:索引+主键;
主键索引叶子节点:主键+表数据;
在1个page里面,非主键索引可以存储更多的条目。

count(字段)

count(字段)只会针对该字段统计,使用这个字段上面的索引(如果有的话),并且会排除掉该字段值为null的行,而count(*)不会排除。

count(1)

count()和count(1)没有区别。
对于MyISAM引擎,如果count(
)没有where条件(形如 select count() from 表名),查询会非常的快。
对于MySQL 8.0.13,InnoDB引擎,如果count(
)没有where条件(形如 select count(*) from 表名),查询也会被优化,性能有所提升。

count 语句优化方案

select *
from `information_schema`.TABLES
where TABLE_SCHEMA = '库名'
  and TABLE_NAME = '表名';

-- 好处:不操作salaries表,不论salaries有多少数据,都可以迅速地返回结果
-- 缺点:估算值,并不是准确值
show table status where Name = 'salaries';
-- 好处:不操作salaries表,不论salaries有多少数据,都可以迅速地返回结果
-- 缺点:估算值,并不是准确值
explain select * from salaries;
-- 好处:不操作salaries表,不论salaries有多少数据,都可以迅速地返回结果
-- 缺点:估算值,并不是准确值

4、order by 语句

最好的做法是:利用索引避免排序,利用索引本身的有序性,让MySQL跳过排序过程

order by 实验

下面做个实验,employees表有组合索引index(first_name, last_name):
image
竟然是全表扫描,没有使用索引,我们在来给语句加个limit来看下
image
有变成了index,使用上了索引,这是为什么呢?
因为当MySQL优化器发现全表扫描开销比使用索引的开销更低时,会直接用全表扫描。

那这条语句到底能不能使用索引避免排序呢?我们来看下Extra的值:
image
Extra是null,说明是可以使用索引避免排序的,如果Extra是using filesort时,说明不可以。

按照上面这种方法,我又试了好几种排序语句,列在下面并给出结论:

/*
 * 可以使用索引避免排序
 * [Bader,last_name1, emp_no]
 * [Bader,last_name2, emp_no]
 * [Bader,last_name3, emp_no]
 * [Bader,last_name4, emp_no]
 * [Bader,last_name5, emp_no]
 * ..
 */
explain
select *
from employees
where first_name = 'Bader'
order by last_name;

/*
 * 可以使用索引避免排序
 * ['Angel', lastname1, emp_no1]
 * ['Anni', lastname1, emp_no1]
 * ['Anz', lastname1, emp_no1]
 * ['Bader', lastname1, emp_no1]
 */
explain
select *
from employees
where first_name < 'Bader'
order by first_name;

/*
 * 可以使用索引避免排序
 */
explain
select *
from employees
where first_name = 'Bader'
  and last_name > 'Peng'
order by last_name;

/*
 * 无法利用索引避免排序【排序字段存在于多个索引中】
 * - first_name => (first_name,last_name)
 * - emp_no => 主键
 */
explain
select *
from employees
order by first_name, emp_no
limit 10;

/*
 * 无法利用索引避免排序【升降序不一致】
 */
explain
select *
from employees
order by first_name desc, last_name asc
limit 10;

/*
 * 无法利用索引避免排序【使用key_part1范围查询,使用key_part2排序】
 * ['Angel', lastname1, emp_no1]
 * ['Anni', lastname1, emp_no1]
 * ['Anz', lastname1, emp_no1]
 * ['Bader', lastname1, emp_no1]
 */
explain
select *
from employees
where first_name < 'Bader'
order by last_name;

优化结论

5、group by 语句

group by 语句优化有三种方式,分别是(性能从高到低):
松散索引扫描( Loose Index Scan ) > 紧凑索引扫描( Tight Index Scan) > 临时表( Temporary table )

5.1 松散索引扫描

无需扫描满足条件的所有索引键即可返回结果。

explain的extra展示Using index for group-by 说明使用了松散索引扫描。

使用条件:

举例:
假设有index(c1,c2,c3)作用在表t1 (c1,c2,c3,c4)上,下面这些SQL都能使用松散索引扫描:

SELECT c1, c2 FROM t1 GROUP BY c1, c2;
SELECT DISTINCT c1, c2 FROM t1;SELECT C1, MIN (c2) FROM t1 GROUP BY c1;
SELECT c1, c2 FROM t1 WHERE c1 < const GROUP BY cl, c2;
SELECT MAX(c3), MIN (c3), cl, c2 FROM t1 WHERE c2 > const GROUP BY cl, c2;
SELECT c2 FROM t1 WHERE c1 < const GROUP BY cl, c2;
SELECT c1, c2 FROM t1 WHERE c3 = const GROUP BY c1, c2;

下面这些SQL不能使用松散索引扫描:

-- 聚合函数不是MIN()或MAX()
SELECT c1, SUM(c2) FROM t1 GROUP BY c1;

-- 不符合最左前缀则
SELECT c1, c2 FROM t1 GROUP BY c2, c3;

-- 查询了c3字段,但是c3字段上没有等值查询
-- 改成 SELECT c1, c3 FROM t1 WHERE c3 = const GROUP BY c1, c2; 则可以使用
SELECT c1, с3 FROM t1 GROUP BY с1, c2;

5.2 紧凑索引扫描

需要扫描满足条件的所有索引键才能返回结果,性能一般比松散索引扫描差,但一般都可接受。

explain-extra没有明显的标识。

5.3 临时表

紧凑索引扫描也没有办法使用的话, MySQL将会读取需要的数据,并创建一个临时表,用临时表实现GROUP BY操作。

explain-extra显示Using temporary。

如果发现group by语句出现临时表,可是适当加索引,让其使用松散索引扫描或紧凑索引扫描。


六、表结构设计优化

1、三范式

遵循三范式可以很好的防止冗余。

1.1 第一范式

字段具有原子性,即数据库表的每一个字段都是不可分割的原子数据项,不能是集合、数组、记录等非原子数据项,当实体中的某个属性有多个值时,必须拆分为不同的属性。

例如:
image
不符合第一范式,address字段还可以拆分成省、市、区等
修改:
image

1.2 第二范式

满足第一范式的基础上,要求每一行数据具有唯一性,并且非主键字段完全依赖主键字段。

例如:
image
不符合第二范式,课程学分字段不依赖于学号字段,而依赖于课程字段,只依赖了部分主键
修改:
image

1.3 第三范式:

满足第二范式的基础上,不能存在传递依赖。

例如:
image
不符合第三范式,学校地址和学校电话字段依赖了学校字段,学校字段又依赖主键字段,存在传递依赖
修改:
image

2、反模式设计

有时为了提升查询效率,我们也会不遵从三范式,适当增加冗余。

例如下面的两张表
image
这两张表是符合三范式的,假设两张表的数据量非常大,而业务要求每次查询学生信息时都要携带学校地址,这种情况下,我们就可以在学生表里冗余存储学校地址,这样就不用联表查询,提升效率。
image

3、表设计原则

标签:join,04,buffer,索引,select,调优,emp,主键,结构设计
来源: https://www.cnblogs.com/jinshengnianhua/p/15366137.html