MySQL多方面优化汇总

目录

一、如何发现SQL语句是否有性能问题?

(一)开启慢查询日志
(二)慢查询分析工具

二、如何通过分析工具查出具体哪条SQL语句有问题?

三、如何对找到性能不好的SQL语句进行分析?

(一)explain执行计划

四、SQL语句具体优化

五、索引的建立与优化

最左前缀原则
(一)索引优化选择
(二)索引的维护

六、数据库结构上的优化

(一)选择合适的数据类型
(四)表的范式化和反范式化
(三)表的垂直和水平拆分

七、数据库系统配置的优化

(一)操作系统配置优化
(二)数据库配置优化

八、服务器硬件的优化


一、如何发现SQL语句是否有性能问题?

开启MySQL慢查询日志对有效率问题的SQL进行监控。
->
但使用慢查日志可能导致日志文件大量增加。
->
使用慢查日志分析工具

(一)开启慢查询日志

开启慢查询日志开启,配置.cnf文件

//开启
slow_query_log          = 1
//位置
slow_query_log_file     = /var/log/mysql/mysql-slow.log
//查询超过x时间将写入
long_query_time = 0
//未使用索引也记录
log-queries-not-using-indexes = 1
//不包括用户主机信息和查询时间
#log-short-format = 1
//将慢管理语句也记录(alter、optimize等)
log-slow-admin-statements = 1

//重启mysql服务器
service mysql restart

(二)慢查询分析工具

1、mysqldumpslow(mysql自带),查询结果少,包行信息不充足

例子:
查询前三条日志

mysqldumpslow -t 3 /慢日志位置 | more
2、pt-query-digest
安装

Ubuntu下安装pt-query-digest工具

sudo apt-get install update //更新源
sudo apt-get install percona-toolkit  //安装,pt-query-digest工具是percona-toolkit工具集合中的一个

关于percona-toolkit包含工具有哪些
官网:https://www.percona.com/doc/percona-toolkit/3.0/index.html


比较详细的pt-query-digest参数说明

https://blog.csdn.net/seteor/article/details/24017913

所使用版本所有参数

pt-query-digest --help
输出方式:
  • 输出到文件
    pt-query-digest slowlog > slow_log.report;

  • 输出到数据库表
    pt-query-digest slow.log -review
    h=127.0.0.1,D=‘数据库名’,p=用户名,u=密码,t=表名
    –create-review-table
    –review-history t=表名

例子
查询日志

pt-query-digest 慢日志位置 | more

pt-query-digest查询结果内容介绍

第一部分:总体统计结果
Orverall:总共多少条查询
unique:唯一查询量,对查询条件进行参数化后总共多少条查询
Time range:查询时间范围
total min max avg(平均数) 95%(重点,类似去掉最小最大值) median(中位数,排序取中)
exec time 查询时间
lock time 锁定时间
rows sent 发送行数
rows examine 扫描行数
query size 查询行数

第二部分:查询分组统计结果。
对查询进行参数化并分分组,分析。结果按总执行时长,从大到小排序
Rank:所有语句的排名,默认按查询时间降序排列,通过–order-by指定
Query ID :语句的ID,(去掉多余空格和文本字符,计算hash值)
Response time:响应时间,占所有响应时间的百分比
Calls:查询执行次数
R/Call:平均响应时间
V/M:响应时间Variance-to-mean的比率
Item:查询语句一部分

第三部分:每一条查询的详细分析结果
最上面的表格列出了执行次数、最大、最小、平均、95%等各项目的统计。
Databases: 库名
Users: 各个用户执行的次数(占比)
Query_time distribution : 查询时间分布, 长短体现区间占比
Tables: 查询中涉及到的表
Explain: 示例

二、如何通过分析工具查出具体哪条SQL语句有问题?

1.查询次数多且每次查询占用时间长的SQL

通常为pt-query-digest分析的前几条

2. IO大的SQL

注意pt-query-digest分析中农的Rows examine项。扫描行数越多IO越大

3. 未命中索引的SQL

注意pt-query-digest分析中Rows examineRows Send的对比。如果扫描行数远远大于发送行数,说明索引的命中率并不高,判定基本使用了索引扫描或者是表扫描的方式进行查询。

三、如何对找到性能不好的SQL语句进行分析?

(一)使用explain从句进行分析,执行计划解读↓

例如:explain select customer_id,first_name,last_name from customer;

(二)explain执行计划结果介绍

  • table:显示这一行的数据是关于哪张表的
  • type:这是重要的列,显示连接使用了何种类型。从最好—>最差的连接类型是:const、eq_reg、ref、range、index和ALL
null:优化过程中就已经得到结果,不再访问表或索引
system:系统查询
const:常量查询,一般为主键、唯一索引查找
eq_reg:一般主键或者唯一索引的范围查找。常见关联主键查询
ref:查找条件列使用了索引但不是主键或唯一索引。常见连接查询。
range:有范围的索引扫描,相对于index有范围限制
index:另一种形式的全表扫描,只不过它的扫描顺序是按照索引的顺序。
all:全表扫描
  • possible_keys:显示可能应用在这张表中的索引。
  • key:实际使用的索引。如果为NULL,则没有使用索引
  • key_len:使用的索引的长度,在不损失精确性的情况下,长度越短越好。
  • ref:显示索引的哪一列被使用了,如果可能的话,是一个常数
  • rows:MySQL认为必须检查的用来返回请求数据的行数
  • extra:包含不适合在其他列中显示但十分重要的额外信息。
  • Only index:这意味着信息只用索引树中的信息检索出的,这比扫描整个表要快。
  • using where: 使用上了where限制,表示MySQL服务器在存储引擎受到记录后进行“后过滤”(Post-filter),如果查 询未能使用索引,Using where的作用只是提醒我们MySQL将用where子句来过滤结果集。
  • impossible where:表示用不着where,一般就是没查出来啥。
出现下面其中一条信息一般说明sql是需要优化的。
  • Using filesort:(MySQL中无法利用索引完成的排序操作称为“文件排序”)当我们试图对一个没有索引的字段进行排 序时,就是filesoft。它根据连接类型以及存储排序和匹配条件的全部行的行指针来排序全部行。
  • Using temporary:(表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询)WHERE和ORDER BY的索引经常无 法兼顾,如果按照WHERE来确定索引,那么在ORDER BY时,就必然会引起 Using filesort,这就要看是先过滤再排序划 算,还是先排序再过滤划算。

四、SQL语句具体优化

Count和Max的优化方法
1、Max查找日期支付日期最大值(覆盖索引)
//未使用索引连接类型为all
explain select max(payment_date) from payment;
type:all

//创建索引
create index idx_padate on payment(payment_date);

//使用索引后,连接类型为null直接得到结果
explain select max(payment_date) from payment;
type:null
覆盖索引:SQL只需要通过索引就可以返回查询所需要的数据,索引包含了查询所有的列,而不必通过二级索引查到主键之后再去查询数据。当SQL查询频率高,查询所包含的列比较少,则通过覆盖索引方式进行优化
2、Count同时查找2006和2007年电影的数量

Count(*)计数包括Null列,而Count(列名)当此列值为null会跳过不计数

Select count(release_year='2006' or null) as '2006',count(release_year='2007' or null) as '2007' from film;
3、子查询的优化

通常情况下,需要把子查询优化成join查询(假如数据大的话或者分布式则另说),但在优化时要注意关联键是否有一对多的关系,要注意重复数据。

一对多使用子查询结果不包括重复,而使用join则会返回重复数据(可用instinct去除)

4、group by的优化

前:

explain select actor.first_name,actor.last_name,count(*)
from sakila.film_action
inner join sakila.actor using(actor_id)
group by film_actor.actor;

此条语句将对表扫描,因为没有使用where限制条件,同时会使用临时表temporary和文件排序filesort方式。

后:

没有建立临时表,节省了大量的IO。一对多的关系,演员表姓名唯一,而电影表存在多个姓名。
explain select a.first_name,a.last_name,c.cnt
from actor join(
    select actor_id,count(*) as cnt from film_actor group by actor_id
) as c
using(actor_id);
5、limit优化

limit常用于分页,时常会伴随order by从句使用,因此大多会使用filesort文件排序,造成大量的IO问题
前:

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

优化步骤1:使用有索引的列或主键进行Order by操作
改用主键film_id

select film_id,description
from film
order by film_id
limit 50,5;

优化步骤2.1:针对连续id

select film_id,description
from film
where film_id>55
and film_id<=60
order by film_id;

优化步骤2.2:针对不连续id(贴合实际)

select film_id,description
from film f1
inner join
(select film_id from film limit 55,5) f2
using(film_id)

五、索引的建立与优化

最左前缀原则:顾名思义,就是最左优先。当我们创建了(a,b,c)多列索引,相当于创建了单列索引(a)联合索引(a,b)以及联合索引(a,b,c)。


当where c=1时和此索引均不匹配。在where and查询中可以是:a,ab,abc。因为数据库的多列索引是先按a->b->c排序的。

当where a=1 and b>2 and c = 3时,只会走(a,b)索引,因为b>2时整个排序已被打乱。此时使用就算继续索引c也没意义了,因为索引就是按照顺序查找的。更多在下面

(一)索引优化选择
  1. 在where从句,group by从句,order by从句,on从句中出现的列
  2. 索引字段越小越好
  3. 离散度大的列放到联合索引的前面(唯一值越多,离散值越高,可选择性越高)
  sselect * 
  from payment
  where staff_id = 2
  and customer_id = 584;
  例如去重后staff_id计数为2而customer_id为584则
  index(customer_id,staff_id)
(二)索引的维护

建立索引可以优化查询效率,但是会降低写入效率(写改删)

1、重复索引

主键约束创建后一定包含唯一索引约束,则不需要对此列建立唯一索引

2、冗余索引

多个索引的前缀列相同,或是联合索引中包含了主键的索引(innoDB特性会在每个索引后面附加主键)

3、查找重复及冗余索引
(1)手动查找
//切换数据库,因为会用到此库的一些表
use information_schema;

SELECT a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, 
a.INDEX_NAME AS 'index1', b.INDEX_NAME AS 'index2'
FROM information_schema.STATISTICS a 
JOIN information_schema.STATISTICS b 
ON a.TABLE_SCHEMA = b.TABLE_SCHEMA    
AND a.TABLE_NAME = b.TABLE_NAME 
AND a.SEQ_IN_INDEX = b.SEQ_IN_INDEX   
AND a.COLUMN_NAME = b.COLUMN_NAME 
WHERE a.SEQ_IN_INDEX = 1 AND a.INDEX_NAME <> b.INDEX_NAME
(2)pt-duplicate-key-checker工具查找(percona工具集已装)

//此工具会找出重复和冗余索引并提供更改的SQL语句建议

pt-duplicate-key-checker -uroot -p '密码' -h 127.0.0.1
4、删除不用到的索引(mysql没有关于index的统计表,智能通过慢查询加pt-index-usage工具分析)

//过程比较慢

pt-index-usage -uroot -p '密码' -h 127.0.0.1
5、使用索引时失效的情况
(1)对索引列进行运算导致索引失效,我所指的对索引列进行运算包括(+,-,*,/,! 等)

错误的例子:select * from test where id-1=9;
正确的例子:select * from test where id=10;

(2)以下使用会使索引失效,应避免使用;

a. 使用 <> 、not in 、not exist、!=
b. like “%_” 百分号在前(可采用在建立索引时用reverse(columnName)这种方法处理)
c. 单独引用复合索引里非第一位置的索引列.应总是使用索引的第一个列,如果索引是建立在多个列上, 只有在它的第一个列被where子句引用时,优化器才会选择使用该索引。
d. 字符型字段为数字时在where条件里不添加引号.
e. 当变量采用的是times变量,而表的字段采用的是date变量时.或相反情况。

https://blog.csdn.net/ustcyy91/article/details/81001669

六、数据库结构上的优化

(一)选择合适的数据类型
  1. 使用可以存下数据的最小的数据类型。例如存储时间数据类型大小:int=timestamp<varchar
  2. 使用简单的数据类型。例如在mysql中int要比varchar
  3. 尽可能使用Not null约束字段。因innoDB存储特性,当为非not null约束字段可能需要额外字段进行存储,增加I/O开销。
  4. 尽量少使用text类型,非用不可时组好考略分表(这样当查询非字段时查询速度不会被大数据字段影响)

例如:
使用int来存储日期类型,利用from_unixtime将int类型时间戳转换为日期时间格式,而unix_timestamp将日期格式转换成int类型

//建表
create table(
    id int auto_increment not null,
    timestr int,
    primary key(id)
);
//插入
insert into test(timestr) values(unix_timestamp('2019-07-04 11:11:11'));
//查询
select from_unixtime(timestr) from test;
(二)表的范式化和反范式化
(1)范式化
范式化是指数据库设计的规范,目前说道的范式化一般是指第三范式,也就是要求数据表中不存在非关键字段对任意候选字段的传递函数依赖则符合第三范式

例如:

分表前

在这里插入图片描述

上图的关键字段为商品名称,而后面两个字段存在以下传递函数依赖关系:
(商品名称)->(分类)->(分类描述)

不符合第三范式要求的表存在下列问题:
  1. 数据冗余(例如上图中,分类和分类描冗余字段,关于描述字段往往比较长,一般单独分一张表则只需一条描述记录)
  2. 数据的插入异常(例如上图,每次都需要插入一次分类和分类描述)
  3. 数据的更新异常(例如上图,单独更新商品描述信息同时则需要更新所有商品对应的描述)
  4. 数据的删除异常(例如上图,当删除所有商品时,对应的分类和描述也随之删除。此分类也就不复存在)
分表后:

在这里插入图片描述

(2)反范式化
为符合第三范式一般采取分表的方式。但在现代网站开发中,连表查询往往效率低下。甚至当出现分库情况时还可能需要跨库查询。所以采用反范式化适当的增加冗余,采取以空间(表的数据冗余)换取时间(查询时间)的方式优化查询效率。

例如:

修改前

在这里插入图片描述

上图虽然符合范式化,但是查询性能十分低下,查询订单信息需要采用多表查询

在这里插入图片描述

适当冗余后:

在这里插入图片描述

上图采用反范式化设计,查询订单信息不再需要连表查询,单表操作即可完成。

在这里插入图片描述

(三)表的拆分
(1)垂直拆分(表的宽度问题,一般在表设计时就)

所谓的垂直拆分,就是把原来一个有很多列的表拆分成多个表,这解决了表的宽度问题。通常垂直拆分可以按照以下原则进行:

  1. 把不常用的字段存放在一个表中
  2. 把存储数据量大的字段独立存放在一个表中。
  3. 把经常联合使用的字段放在一起。

例如一张表有字段:
id、name、score、question、answer
其中question和answer为比较大字段,其余为小字段
则可分为:
表1:id、name、score
表2:id、question、answer
这样拆分的好处:当只查询分数时,表扫描则不会扫描到大字段,提高了查询速度

垂直拆分的优点
  • 简单明了,规则明确
  • 应用程序模块清晰明确,整合容易
  • 数据维护方便易行,容易定位
垂直拆分的缺点
  • 部分表关联无法再库级别完成,需在程序中完成
  • 对访问及其频繁且数据量超大的表仍然存在性能瓶颈
  • 事物处理相对更为复杂
  • 拆分达到一定程度之后,扩展性会遇到限制
  • 过度切分可能会带来系统过渡复杂而难以维护
(2)水平拆分(表的数据量问题)

表的水平拆分是未了解决单标的数据量过大的问题,水平拆分的表的每一个表的结构都是完全一致的。
例如有一张10亿的用户表表:
拆分成user0-user9或者user0-user99表

水平拆分的优点:
  • 表关联基本能够在数据库端全部完成;
  • 不会存在某些超大型数据量和高负载的表遇到瓶颈的问题
  • 应用程序端整体架构改动相对较少
  • 事务处理相对简单
  • 只要切分规则能够定义好,基本上较难遇到扩展性限制
水平拆分的缺点:
  • 切分规则相对更为复杂,很难抽象出一个能够满足整个数据库的切分规则
  • 后期数据的维护难度有所增加,人为手工定位数据更困难
  • 应用系统各模块耦合度较高,可能会对后面数据的迁移拆分造成一定的困难

七、数据库系统配置的优化

(一)操作系统配置优化

数据库是基于操作系统的,目前大多数的MySQL都是安装在linux系统之上,所以对于操作系统的一些参数配置也影响到MySQL的性能,下面就列出一些常用到的系统配置。

网络方面的配置,要修改/etc/sysctl.conf文件

#增加tcp支持的队列数
net.ipv4.tcp_max_syn_backlog=65535

#减少断开连接时,资源的回收
net.ipv4.tcp_max_tw_buckets=8000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_fin_timeout=10

打开文件数的限制,可以使用ulimit -a 查看目录的各位闲置,可以修改
/etc/security/limits.conf文件,增加以下内容以修改打开文件数量的限制

*soft nofile 65535
*hard nofile 65535

除此之外在大型网站开发时最好使用硬件防火墙,而关闭iptables,selinux等防火墙软件,因为软件防火墙也会影响性能。

更多系统优化参数 https://blog.csdn.net/qq_33936481/article/details/61194213
(二)数据库配置优化
手动配置
配置文件位置,如果存在多个位置存在配置文件,则后面的会覆盖前面的

查找所有配置文件命令:

/usr/sbin/mysqld --verbose --help |grep -A 1 'Default options '
常用innoDB参数项说明:
  • innodb_buffer_pool_size
    非常重要的参数,用于配置innodb的缓冲池如果数据库中只有innodb表,则推荐配置量为总内存的75%。缓冲池最小配置应大于所有innoDB表及索引的合。innodb_buffer_pool_size >=Total MB
//检测语句,检测出每一种引擎表总的大小是多少:
select engine,round(sum(data_length + index_length)/1024/1024,1) as 'Total MB' 
from information_schema.tables where table_schema not in ('information_schema',
'performance_schema') group by engine;
  • innodb_buffer_pool_instances
    MySQL5.5中新增参数,可以控制缓冲池的个数,默认情况下只有一个。
    有些资源是需要独占使用的,如果只有一个缓冲池可能会增加阻塞的频率。如存成多个可以增加查询的并发性。
    例如配成4或5个,等于缓冲池总大小/4||5。

  • innodb_log_buffer_size
    innodb log缓冲的大小,由于日志最长每秒钟就会刷新所以一般不用太大

  • innodb_flush_log_at_trx_commit
    关键参数,决定了数据库多长时间将变更刷新到磁盘。
    对innodb的IO效率影响比较大,默认值为1,可以取0,1,2三个值,
    一般建议设为2,但如果数据安全性要求比较高则使用默认值1。

  • innodb_read_io_threads
    innodb_write_io_threads
    以上两个参数决定了innodb读写IO进程数,默认4。根据cpu的核数来分别调整读和写的数量。

  • innodb_file_per_table
    关键参数,控制innodb中每一个表使用独立的表空间,默认为OFF,也就是所有表都会建立在共享表空间。建议设为on
    (1)如果所有的innoDB表都使用共享表空间,则共享表空间的I/O会成为一个瓶颈,并发写入时效率大大降低。
    (2)innoDB的共享表空间无法单独收缩,如果删除一个很大的日志或不使用的表,则需要导出数据再进行收缩
    (3)开启后可以快速回收磁盘空间。同时多文件可提高并发的读写效率。

  • innodb_stats_on_metadata
    决定了MySQL在什么情况下会刷新innodb表的统计信息。过高会影响性能。

八、服务器硬件的优化

1、如何选择CPU?
选择单核更快的CPU而不是核数更多的CPU
(1)MySQL有些工作只能使用到单核CPU。

Replicate,SQL等

(2)MySQL5.5版对CPU核数的支持不超过32核,并不是越多越好。
2、Disk IO优化

一般建议数据库使用RAID1+0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章