【面试】数据库总结4(MySQL优化思路)

点击链接查看数据库其他内容:

【面试】数据库总结1(MySQL介绍+体系结构)

【面试】数据库总结2(MySQL索引)

【面试】数据库总结3(MySQL事务与锁机制)

【面试】数据库总结4(MySQL优化思路)


MySQL优化思路 目录

5 MySQL数据库优化思路

5.1 优化层次

5.1.1 连接——对数据库的配置优化(目标都是硬件本身的优化)

5.1.2 缓存——架构优化

5.2 优化器——SQL语句分析与优化

5.2.1 慢查询日志  Slow query log

5.1.2慢日志分析

5.2 SHOW PROFILE

5.2.1查看是否开启

5.2.2查看profile 统计(命令最后带一个s)

5.2.3其他系统命令

5.3 EXPLAIN 执行计划

5.3.1 id

5.3.2 select type 语句类型

5.3.3  type连接类型

5.4 存储引擎与表结构优化

5.4.1存储引擎的选择

5.4.2字段定义

5.5 优化总结


5 MySQL数据库优化思路

5.1 优化层次

5.1.1 连接——对数据库的配置优化(目标都是硬件本身的优化)

第一个环节是客户端连接到服务端

连接这一块有可能会出现什么样的性能问题?有可能是服务端连接数不够导致应用程序获取不到连接。

比如报了一个Mysql: error1040: Too many connections的错误。

可以从两个方面来解决连接数不够的问题:

1、从服务端来说,我们可以增加服务端的可用连接数。
如果有多个应用或者很多请求同时访问数据库,连接数不够的时候,我们可以:

(1)修改配置参数增加可用连接数,修改 max_connections的大小:
show variables like 'max_connections'; --修改最大连接数,当有多个应用连接的时候

(2)或者,或者及时释放不活动的连接

交互式和非交互式的客户端的默认超时时间都是28800秒,8小时,我们可以把这个值调小。

show global variables like 'wait_timeout';--及时释放不活动的连接,注意不要释放连接池还在使用的连接


2、从客户端来说,可以减少从服务端获取的连接数。

如果我们想要不是每一次执行SQL都创建一个新的连接,可以引入连接池(如C3P0),实现连接的重用。

对于一个客户端,只要维护一个连接池就可以了。

5.1.2 缓存——架构优化

(1)缓存

在应用系统的并发数非常大的情况下,如果没有缓存,会造成两个问题:一方面是会给数据库带来很大的压力。另一方面,从应用的层面来说,操作数据的速度也会受到影响。

我们可以用第三方的缓存服务来解决这个问题,例如Redis。

(2)集群,主从复制

如果单台数据库服务满足不了访问需求,那我们可以做数据库的集群方案。

做了主从复制的方案之后,我们只把数据写入master节点,而读的请求可以分担到slave节点。我们把这种方案叫做基于主从复制的读写分离


读写分离可以一定程度低减轻数据库服务器的访问压力,但是需要特别注意主从数据一致性的问题。会有一定程度的延迟。

(3)分库分表

垂直分库,减少并发压力。水平分表,解决存储瓶颈。
垂直分库的做法,把一个数据库按照业务拆分成不同的数据库:

水平分表:把单张表的数据按照一定规则分布到多个数据库。 

5.2 优化器——SQL语句分析与优化

5.2.1 慢查询日志  Slow query log

(1)打开慢日志开关

因为开启慢查询日志是有代价的(跟bin log. optimizer-trace一样),所以它默认是关闭的:

show variables like 'slow_qucry%';

除了这个开关,还有一个参数,控制执行超过多长时间的SQL才记录到慢日志,默认是10秒。如果改成0秒的话就是记录所有的SQL。

show variables like "%long_qucry"%';

可以直接动态修改参数(重启后失效)。

set a aglobal.slow_query_log=l;--1开启,О关闭,重启后失效
set (a aglobal.long_query_timc=3; --mysql 默认的慢查询时间是10秒,另开一个窗口后才会查到最新值
show variables like "%long_query%';
show variables like "%slow_qucry%";

或者修改配置文件 my.cnf。
以下配置定义了慢查询日志的开关、慢查询的时间、日志文件的存放路径。

slow_query_log = ON
long_query_time=2
slow_query_log_ file =/var/lib/mysql/localhost-slow.log

模拟慢查询:

select sleep( 10);

查询user_innodb表的500万数据(没有索引)。

SELECT * FROM "user_innodb' where phone ='136';

5.1.2慢日志分析

(1)日志内容

show global status like 'slow_qucries"; --查看有多少慢查询
show variables like "%slow_query%': --获取幔日志目录
cat /var/ib/mysql localhost-slow.log
#Time : 2019-12-28T12:35:52.281391z
#User@Host: root[root] @[192.168.8.1] Id:64
#guery_time: 6.524766 Lock_time: 0.000145 Rows_sent: 1 Rows_examined: 5000000

SET timestamp=1577536552;
select * from user_innodb where name='青山' ;

(2)mysqldumpslow

https://dev.mysql.com/doc/refman/5.7/en/mysqldumpslow.html

MySQL提供了mysqldumpslow 的工具,在MySQL的bin目录下。

mysqldumpslow --help

例如:查询用时最多的10条慢SQL:

mysqldumpslow -s t -t 10 -g "select'/var/lib/mysqllocalhost-slow.log


Count代表这个SQL执行了多少次;

Time 代表执行的时间,括号里面是累计时间;Lock表示锁定的时间,括号是累计;

Rows表示返回的记录数,括号是累计。

5.2 SHOW PROFILE


SHOW PROFILE是谷歌高级架构师Jeremy Cole贡献给MySQL社区的,

可以查看SQL语句执行的时候使用的资源,比如CPU、IO的消耗情况。

在SQL中输入help profile可以得到详细的帮助信息。

5.2.1查看是否开启

select@@profiling:
set @@profiling=1;

5.2.2查看profile 统计(命令最后带一个s)

可以查到所有语句的执行详细过程信息:

show profiles;

查看最后一个SQL的执行详细信息,从中找出耗时较多的环节(没有s)。
 

show profile:


6.2E-5,小数点左移5位,代表0.000062秒。

也可以根据ID查看执行详细信息,在后面带上for query + ID。

show profile for query 1:

5.2.3其他系统命令

分析Server层的运行信息,可以用show status。
show status服务器运行状态
SHow STATUS用于查看MySQL服务器运行状态(重启后会清空)。

SHOW GLOBAL STATUS ;

可以用like带通配符过滤,例如查看select语句的执行次数。

SHOW GLOBAL STATUS LIKE 'com_select";--查看select 次数

show processlist运行线程
如果要分析服务层的连接信息,可以用show processlist:
https://dev.mysql.com/doc/refman/5.7/en/show-processlist.html

show processlist;

这是很重要的一个命令,用于显示用户运行线程。
如果说其中的某个线程有问题,可以根据id号kill线程。
也可以查表,效果一样:

select * from information_schema.processlist;


show engine存储引擎运行信息

show engine 用来显示存储引擎的当前运行信息,包括事务持有的表锁、行锁信息;事务的锁等待情况;线程信号量等待;文件IO请求; buffer pool统计信息。

例如查看InnoDB:

show engine innodb status;

现在我们已经知道哪些SQL慢了,为什么慢呢?慢在哪里?

MySQL提供了一个执行计划的工具(在架构中我们有讲到,优化器最终生成的就是一个执行计划),其他数据库,例如Oracle 也有类似的功能。

通过EXPLAIN我们可以模拟优化器执行SQL查询语句的过程,来知道MySQL是
怎么处理一条SQL语句的。通过这种方式我们可以分析语句或者表的性能瓶颈。

5.3 EXPLAIN 执行计划

官方链接:https://dev.mysq!.com/doc/refman/5.7/en/explain-output.html

我们先创建三张表。一张课程表,一张老师表,一张老师联系方式表(没有任何索引).

DROP TABLEF EXISTS course:CREATE TABLE'course(
cid int(3) DEFAULTNULL.
cnamevarchar(20) DEFAULT NULL,
"tid int(3) DEFAULTNULL
)ENGINE-InnoDB DEFAULT CHARSET=utf8mb4:
DROP TABLE IF EXISTS teacher:
CREATE TABLE "tcachcr(
tid" int(3y DEFAULT NULL.
"tnamevarchar(20) DEFAULT NULL.
"tcid int(3) DEFAULT NULL
)ENGINE-InnoDB DEFAULT CHARSET-utf8mb4;
DROP TABLEIF EXISTS teacher_contact;
CREATE TABLE‘teacher_contact(
"tcid int(3)DEFAULT NULL.
`phone varchar(200) DEFAULT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4:
INSERT INTO courseVALUES ('1', 'mysql'. ' 1 '):INSERT INTO "coursc’ VALUES('2'. 'jvm". ' 1');
INSERT INTO course VALUES ("': 'mysql s....一师INSERT INTO 'course’ VALUES ('4', 'spring'. "3):

INSERT INTO ‘teacher’VALUES ('1', 'qingshan', "1');
INSERT INTO teacher’ VALUES ('2', 'jack". '2'):INSERT INTO"teacher VALUES ('3' , 'mic", "3');
INSERT INTO'teacher_contact’VALUES (1","13688888888');INSERT INTO'tcacher_eontact VALUES(2",'I8166669999'y;INSERT INTO  teacher_contact'VALUES ("3",17722225555');

5.3.1 id

id
select type
type
possible_key、key
key_len
rows
ref
Extra

id是查询序列编号,每张表都是单独访问的,一个SELECT 就会有一个序号。
 (1) id值不同的时候,先查询id值大的(先大后小)。

(2)id值相同时,查询从上往下(受数据量影响)

id值相同时,表的查询顺序是从上往下顺序执行。例如这次查询的id都是1 (说明子查询被优化器转换成了连接查询) , 查询的顺序是teachert (3条)一coursec (4条)一-teacher_ contacttc (3 条)。

在连接查询中,先查询的叫做驱动表,后查询的叫做被驱动表,我们肯定要把小表放在前面查询,因为它的中间结果最少。

既有相同也有不同:如果ID有相同也有不同,就是ID不同的先大后小,ID 相同的从上往下。

5.3.2 select type 语句类型

SIMPLE:不包含子查询和关联查询的简单查询
PRIMARY: 子查询SQL语句中的主查询,也就是最外面的那层查询。
SUBQUERY:子查询中所有的内层查询都是SUBQUERY类型的。
DERIVED:派生查询,表示在得到最终查询结果之前会用到临时表。例如:
对于关联查询,先执行右边的table (UNION) ,再执行左边的table
UNION:用到了UNION查询(UNION 会用到内部的临时表)。
UNION ALL不需要去重,因此不用临时表。
UNION RESULT:主要是显示哪些表之间存在UNION查询。<union2,3> 代表id=2和id=3的查询存在UNION。

5.3.3  type连接类型

const:主键索引或者唯一索引与常数进行等值匹配,只能查到一条数据的SQL。
system:system是const的一种特例,只有一行满足条件, 对于MyISAM、Memory的表,
              只查询到一条记录,也是system。
              例如:只有一条数据的系统表。
eq_ref:通常出现在多表的join 查询,被驱动表通过唯一性索引(UNIQUE 或PRIMARY KEY)进行访问,此时被驱动表的访问方式就是eq. ref.   eq_ ref是除const之外最好的访问类型。

ref:查询用到了非唯一索引(普通索引)
range:对索引进行范围扫描
index:查询全部索引中的数据(比不走索引要快)
all:全表扫描(没有索引或是没有用到索引)
possible_key、key:

可能用到的索引和实际用到的索引。如果是NULL就代表没有用到索引。
possible_ key可以有一个或者多个,比如查询多个字段上都有索引,或者一个字段同时有单列索引和联合索引。
能用到的索引并不是越多越好。可能用到索引不代表一定用到索引。
如果通过分析发现没有用到索引,就要检查SQL或者创建索引。
 key_ len:
索引的长度(使用的字节数)。跟索引字段的类型、长度有关。
表.上有联合索引: KEY 'comidx. name_ phone' ( name' , phone')

 rows:
MySQL认为扫描多少行(数据或者索引)才能返回请求的数据,是一个预估值。一般来说行数越少越好。

filtered:
这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,它是一一个百分比。
如果比例很低,说明存储引擎层返回的数据需要经过大量过滤,这个是会消耗性能的,需要关注。

ref:使用哪个列或者常数和索引一起从表中筛选数据。

Extra:执行计划给出的额外信息说明。

using index:属于覆盖索引,不需要回表。
using where:使用了where过滤,表示存储引擎返回的记录并不是所有的都满足查询条件,需要在server层进行过滤。
using index condition:索引下推
using filesort:不能使用索引来排序,用到了额外的排序(跟磁盘或文件没有关系)需要优化
using temporary:在查询的时候,需要做去重、排序之类的工作的时候,可能会用到临时表。(如distinct、group by、使用join时group任意列)。需要优化、例如创建复合索引。

模拟优化器执行SQL查询语句的过程,来知道MySQL是怎么处理一条SQL语句的。
通过这种方式我们可以分析语句或者表的性能瓶颈。
如果需要具体的cost信息,可以用:
EXPLAIN FORMAT= JSON。
如果觉得EXPLAIN还不够详细,可以用开启optimizer trace.

5.4 存储引擎与表结构优化

5.4.1存储引擎的选择

为不同的业务表选择不同的存储引擎,例如:查询插入操作多的业务表,用MyISAM。
临时数据用Memeroy。常规的并发大更新多的表用InnoDB.

5.4.2字段定义

原则:使用可以正确存储数据的最小数据类型。
为每一-列选择合适的字段类型。
(1)整数类型
tinyint
smallint
mediumint
integer
bigint
bit

INT有8种类型,不同的类型的最大存储范围是不一样的。

性别?用TINYINT,因为ENUM也是整数存储。
(2)字符类型
变长情况下,varchar更节省空间,但是对于varchar字段,需要一个字节来记录长度。
固定长度的用char,不要用varchar.
(3)不要用外键、触发器、视图
降低了可读性; .
影响数据库性能,应该把把计算的事情交给程序,数据库专心做存储;
数据的完整性应该在程序中检查。
(4)大文件存储
不要用数据库存储图片(比如base64编码)或者大文件;
把文件放在NAS.上,数据库只需要存储URI (相对路径),在应用中配置NAS服务器地址。
(5)表拆分或字段冗余
将不常用的字段拆分出去,避免列数过多和数据量过大。
比如在业务系统中,要记录所有接收和发送的消息,这个消息是XML格式的,用blob或者text存储,用来追踪和判断重复,可以建立- -张表专用来存储报文。
 

5.5 优化总结

一、分析查询基本情况
1、涉及到表结构,字段的索引情况、每张表的数据量、查询的业务含义。
这个非常重要,因为有的时候你会发现SQL根本没必要这么写,或者表设计是有问
题的。
 

二、找出慢的原因
1、查看执行计划,分析SQL的执行情况,了解表访问顺序、访问类型、索引、扫描行
数等信息。
2、如果总体的时间很长,不确定哪一个因素影响最大, 通过条件的增减,顺序的调整,
找出引起查询慢的主要原因,不断地尝试验证。
找到原因:比如是没有走索引引起的,还是关联查询引起的,还是order by引起的。

找到原因之后:
 

三、对症下药
1、创建索引或者联合索引
2、改写SQL,这里需要平时积累经验,例如:
1)使用小表驱动大表
2)用join来代替子查询
3) not exist转换为left join IS NULL
4) or 改成union
4)使用UNION ALL代替UNION,如果结果集允许重复的话
5)大偏移的limit,先过滤再排序。
如果SQL本身解决不了了,就要上升到表结构和架构了。
3、表结构(冗余、拆分、not null等)、架构优化。
4、业务层的优化,必须条件是否必要。
如果没有思路,调优就是抓瞎,肯定没有任何头绪。
 


 

猜你喜欢

转载自blog.csdn.net/paranior/article/details/115095669