MySQL性能优化
性能优化是指通过某些有效的方法提升MySQL数据库的性能。性能优化的目的是使MySQL数据运行速度更快、占用的磁盘空间更小。性能优化有很多方面,如优化查询速度、优化更新速度和优化MySQL服务器等。当表中的记录很少时,MySQL的性能差别很小,只有在大量用户使用、记录很大、运行时间很长时,MySQL的性能差别才开始体现出来。若用户在查询或者添加数据时因为MySQL的性能很差而导致响应速度变慢,用户很难喜欢这个网站。因此为了提高MySQL的性能,就要对MySQL进行性能优化。如若大量用户想要进行查询操作,就要对查询语句进行优化;若大量用户想要进行更新操作,就要对更新语句进行优化。下面介绍各种情况如何进行优化。
优化概述
分析MySQL数据库的性能
数据库管理员可以使用SHOW STATUS语句查询MySQL数据库的性能,语法如下。
SHOW STATUS LIKE 'value 参数';
其中,value参数是常用的几个统计参数,具体介绍如下。
- Connections:连接MySQL服务器的次数。
- Uptime:MySQL服务器的上线时间。
- Show_queries:慢查询次数。
- Com_select:查询操作的次数。
- Com_insert:插入操作的次数。
- Com_delete:删除操作的次数。
MySQL中存在查询InnoDB类型的表的一些参数。例如,
Innodb_rows_read
参数表示SELECT语句查询的记录数;Innodb_rows_inserted
参数表示INSERT语句的插入数;Innodb_rows_updated
参数表示UPDATE语句更新的记录数;Innodb_rows_deleted
参数表示DELETE语句删除的记录数。
如我们查询MySQL服务器的连接数。
show status like 'Connections';
![运行结果](/assets/image-20221207182134033-fABqHj58.png)
运行结果如上图,通过这些结果,我们可以知晓什么语句使用得多,如使用SELECT语句的次数大于INSERT语句的次数,我们就可以通过多使用索引来提升查询的速度,而因为插入表的次数少,也不会有太大影响。
使用profile工具分析语句消耗性能
在MySQL命令窗口中,使用SELECT语句查询到记录后,最后一行会显示查询的时间,精度为秒,小数点后两位,若要知晓精度更高的查询速度,可以通过开启profile工具实现。MySQL8.0安装后profile工具是默认不开启的。MySQL是通过profilig参数标记profile工具是否开启的,查看方法如下。
SHOW VARIABLES LIKE '%pro%';
运行结果如下图,可以看到,现在的profile工具处于关闭状态。
![运行结果](/assets/image-20221207231407489-nnvrQCTZ.png)
开启profile工具的语法如下。
SET profiling=1;
运行结果如下。
![运行结果](/assets/image-20221207231548447-cZ3f3d2v.png)
开启profile工具后,我们就可以通过先执行SELECT语句,再使用profile工具查看执行此语句消耗的时间。语法如下。
select * from db_stu.tb_stu;
show profiles;
![运行结果](/assets/image-20221207231850930-Fi9HtcJk.png)
红色框内就是此语句执行的时间,字段Duration表示本次查询耗费的时间,单位为秒。
优化查询
分析查询语句
可使用关键字EXPLAIN分析查询语句,语法如下。
EXPLAIN SELECT 语句;
其中SELECT 语句
就是我们平时使用的查询语句,例如下面的例子。
explain select * from db_stu.tb_stu;
![运行结果](/assets/image-20221207232422633-eeYr6X15.png)
其中主要的字段代表意义如下。
id
:指出在整个查询中SELECT的位置。table
:查找的表名。type
:连接类型,该列中存储许多值,范围从const到ALL。possible_keys
:指出为了提高查找速度,可以使用的索引。keys
:指出实际使用的键。rows
:指出MySQL需要在相应表中返回查询结果所检验的行数,为了得到总行数,MySQL必须扫描处理整个查询,再乘以每个表的行值。Extra
:包含一些其他信息,如MySQL如何设计查询。
MySQL也支持使用关键字DESCRIBE分析查询,语法就是把EXPLAIN换成DECRIBE,且DESCRIBE可简写成DESC。
索引对查询速度的影响
下面我们通过使用索引和不使用索引,分别分析两次查询然后对比来观察查询的优化情况。
首先我们先查看一下数据表的定义语句以及数据。
![运行结果](/assets/image-20221208122812814-VTk4vWsB.png)
可以看到表中字段price加了索引,其他列无索引。下面开始对比。
首先分析不使用索引的查询情况,我们查询bookname='MySQL从入门到精通'
的记录,语法如下。
explain select * from tb_bookinfo where bookname = 'MySQL从入门到精通';
![运行结果](/assets/image-20221208122925352-7ezOypJ1.png)
可以看到表中总共查询了6行记录,接下来我们为字段bookname添加索引,并且再查询一次,语法如下。
create index index_bookname on tb_bookinfo;
explain select * from tb_bookinfo where bookname = 'MySQL从入门到精通';
![运行结果](/assets/image-20221208123208294-aGiOuLTV.png)
![运行结果](/assets/image-20221208123304488-cz9lCNeX.png)
可以看到此时只查询了两行,比不使用索引少查询了4 行,所以在查询操作时使用索引不仅可以提高查询效率,还可以降低服务器开销。
使用索引查询
虽然索引可以提高查询的速度,但并不能充分发挥其作用,因此可以通过关键字或者其他方式优化查询处理。
应用关键字like优化索引查询
应用EXPLAIN语句执行以下查询。
explain select * from tb_bookinfo where bookname like '%Java Web';
![运行结果](/assets/image-20221208124033453-XGiAVIpF.png)
可以看到查询的行数仍为6条,这是因为若匹配字符串第一个字符就是百分号“%”,则索引不会被使用,若“%”不在匹配字符串的第一位,则索引会被正常使用。
如我们再次使用查询,语法如下。
explain select * from tb_bookinfo where bookname like 'Java Web%';
![运行结果](/assets/image-20221208124049484-owMVKyZG.png)
查询语句中使用多列索引
多列索引是在表的多个字段上创建一个索引。只有在查询条件中使用了这些字段中的一个字段时,索引才会被正常使用。
应用多列索引在表的多个字段中创建一个索引,其命令如下。
CREATE INDEX index_name ON table_name (col_name1,[col_name2...]);
如拿此语句:
create index index_name on table_name (c1,c2);
来说,在应用字段c2时,索引不能正常使用。这就意味着索引并未在MySQL优化中起到任何作用,故必须使用第一字段c1,索引才可以被正常使用。具体可看下图。运行结果
查询语句中使用关键字OR
在MySQL中,查询语句包含OR关键字时,要求查询的两个字段必须同为索引,若所搜索的字段有一个不为索引,则在查询中不会应用索引进行查询。下面我们通过一个实例来对比使用索引和不使用索引的性能差异。
首先查看tb_bookinfo表的结构。我们发现字段price有索引,我们把此索引删除。
运行结果 然后我们使用EXPLAIN语句分析使用OR关键字查询,语法如下。
explain select * from tb_bookinfo where bookname = 'MySQL从入门到精通' or price = 89;
运行结果 可以看到它扫描了整个表(6行),并且一个索引也没有使用。
我们重新添加索引,然后再次查找,语法如下。
create index index_price on tb_bookinfo (price); explain select * from tb_bookinfo where bookname = 'MySQL从入门到精通' or price = 89\G
运行结果 可以看到它成功使用了两个索引,且共扫描了4行。
优化数据库结构
数据库结构是否合理,需要考虑是否存在冗余,对表的查询和更新速度如何、表中字段的数据类型是否合理等多方面的内容。本节将介绍优化数据库结构的方法。
将字段很多的表分解成多个表
有些表在设计时设置了就包含很多字段,其中有的字段使用频率很低,那么当这个表的数据量很大时,查询数据的速度就会变慢,对于这种字段特别多的表,就可以将使用频率低的字段分解成多个新的表。
增加中间表
有的时候经常查询两个表中的好几个字段,若经常进行连表查询,会降低MySQL的查询速度。这种情况下,可以建立中间表来提高查询速度。我们需要先分析最可能同时查询表中的哪几个字段,然后将这些字段建立为一个中间表,并将原来的几个数据插入中间表中,之后就可以使用中间表进行查询和统计。
如我们查看以下学生表tb_student和班级表tb_classes的结构,结果如图。
![运行结果](/assets/image-20221208232255404-emiitNqQ.png)
实际应用中,我们常常查询学号、姓名和班级。根据这个情况,可以创建一个temp_student表,temp_student表中存储三个字段,分别是id、name和classname。创建以及插入记录的语句如下。
create table temp_student(
id int not null,
name varchar(50) not null,
classname varchar(45)
);
insert into temp_student select s.id,s.name,c.name from tb_student as s,tb_classes as c where s.class_id = c.id;
select * from temp_student;
![运行结果](/assets/image-20221209134556597-U2qr4ew8.png)
优化插入记录的速度
索引、唯一性检验都会影响插入数据的速度,是因为每次都要进行排列以及检验,而且一次插入和多次插入的速度是不一样的。
禁用索引
若索引开启,则插入数据的速度会变慢,若插入大量数据时应先关闭索引,待插入完成后再次开启索引,关闭索引的语法如下。
ALTER TABLE table_name DISABLE KEYS;
重新开启索引的方法如下。
ALTER TABLE table_name ENABLE KEYS;
对于新创建的表,可以先不创建索引,等到记录都导入以后再创建索引,这样可以提高插入数据的速度。
禁用唯一性检查
唯一性检查开启时,MySQL会对插入的数据进行检验,这也会降低插入记录的速度。可以在插入记录前禁用唯一性检查,待记录完成后再开启,禁用唯一性检查的语法如下。
SET UNIQUE_CHECKS = 0;
重新开启唯一性检查的语法如下。
SET UNIQUE_CHECKS = 1;
优化INSERT语句
在插入多条语句时,若要插入多条记录,则将这些数据放在一条INSERT语句中的方式要比多条INSERT语句,每条INSERT语句只包含一个记录的方式要快。如下面。
insert into tb values (v1),(v2),... -- 方式一
insert into tb values v1; -- 方式二
insert into tb values v2;
...
在上面的例子中,方式一就比方式二快,因为方式一不需和数据库进行多次连接。
当插入大量数据时,建议使用一个INSERT语句插入多条记录的方式。而且若能够使用LOAD DATA INFILE语句就尽量使用该语句,因为该语句比INSERT语句更快。
分析表、检查表和优化表
分析表的主要作用是分析关键字的分布。检查表的主要作用是检查表是否存在错误。优化表的主要作用是消除删除或更新操作带来的空间浪费。
分析表
MySQL中使用ANALYZE TABLE语句来分析表,该语句的语法如下。
ANALYZE TABLE table_name1[,table_name2,...];
使用ANALYZE TABLE语句分析表的过程中,数据库系统会给表添加一个只读锁。在分析期间,只能读取表中的记录,不能更新和插入记录。ALALYZE TABLE语句能分析InnoDB和MyISAM类型的表。如我们分析tb_bookinfo表,语法如下。
analyze table tb_bookinfo;
![运行结果](/assets/image-20221210184433093-BzvWeaxv.png)
其中Table字段表示表的名称;op是operation的缩写,代表执行的操作,analyze代表分析操作,check代表检查查找,optimize代表进行优化操作;Msg_type代表信息类型,其显示的值通常是状态、警告、错误、信息中的一个;Msg_text显示信息。
检查表和优化表之后也会出现这4列信息。
检查表
MySQL使用CHECK TABLE语句检查表。CHECK TABLE语句可以检查InnoDB、MyISAM类型的表是否存在错误,而且还可以检查视图。该语句的语法如下。
CHECK TABLE table_name1[,table_name2,...][option];
其中,参数option
有五个可选项:QUICK、FAST、CHANGED、MEDIUM和EXTENTED。这五个选项的执行效率依次降低。option选项只对MyISAM表有效,对InnoDB表无效。CHECK TABLE语句在执行时也会给表添加只读表。
优化表
MySQL中使用OPTIMIZE TABLE语句来优化表。OPTIMIZE TABLE语句对InnoDB和MyISAM表都有效。但是该语句只能优化表中的VARCHAR、BLOB和TEXT字段。OPTIMIZE TABLE语句的语法如下。
OPTIMIZE TABLE table_name1[,table_name2,...];
通过此语句可以消除删除和更新造成的磁盘碎片,从而减少空间的浪费。OPTIMIZE TABLE语句执行时也会给表添加只读表。
若一个表使用了TEXT或BLOB这样的数据类型,那么更新、删除等操作就会造成磁盘空间的浪费,因为更新和删除后,以前分配的磁盘空间不会自动收回。使用OPTIMIZE TABLE语句就可以将这些磁盘碎片整理出来,以便以后再次利用。
优化多表查询
在MySQL中,用户可以通过连接来实现多表查询,在查询过程中,用户将一个表中的一个或多个共同字段进行连接,定义查询条件,返回统一的查询结果。这通常用来建立RDBMS常规表的关系。在多表查询中,可以应用子查询来进行优化,即在SELECT语句中嵌套SELECT语句。采用子查询优化多表查询的好处有很多,其中可以将分步查询的结果整合为一个查询,这样就不需要进行多次查询,提高查询的效率。
下面通过一个实例演示如何优化多表查询,如我们查询属于“一年三班”的全部学生姓名,其中学生信息表在tb_student表中,班级名称在tb_classes表中。第一个查询语句是使用连接查询,第二个查询语句是使用子查询。
set profiling = 1;
select s.name from tb_student as s,tb_classes as c
where s.class_id = c.id and c.name = '一年三班';
select name from tb_student as s
where s.class_id = (select id from tb_classes where name = '一年三班');
show profiles\G
![运行结果](/assets/image-20221210221051892-b0DPau64.png)
可以看到,使用子查询的速度要比使用连接查询的速度快。
优化表设计
在MySQL数据库中,提高查询速度还需要思考其他方法。首先,在设计数据表时应优先考虑使用特定长度,后考虑使用变长字段,如在创建数据表时,考虑将某个字段类型设为VARCHAR且长度为255,但是在实际应用时所存储的数据并不能达到这个长度,就造成了过多列宽,不仅浪费资源,也会降低数据表的查询效率。适当调整列宽可以减少磁盘占用空间,还可以在数据处理时使数据的I/O过程减少。将字段尽可能地设计成其可能长度的最大值以充分优化查询效率。改善性能的另一个方法就是使用OPTIMIZE TABLE命令处理用户经常操作的表,频繁操作数据库中的特定表会产生许多磁盘碎片,降低MySQL的速度,故应经常处理经常操作的数据表。最后可以考虑某些表是否可以整合为一个表,若没有使用整合,则再查询过程中可以使用连接,这样若连接的列采用相同的数据类型和长度,同样可以达到优化表的目的。