应用系统正式上线后,随着生产数据量的急剧增长,数据库开始显露性能问题,对生产的影响也越来越大,因此我们必须对它们进行优化。如何优化 MySQL 数据库,比如优化查询速度、优化更新速度和优化 MySQL 服务器等。在讲解 MySQL 性能调优前,我们先了解 MySQL 的日志和分类。参考:http://c.biancheng.net/mysql/
MySQL 中有 4 种日志文件:
在 MySQL 所支持的日志文件里,除了二进制日志文件外,其它日志文件都是文本文件。
使用日志有优点也有缺点。启动日志后,虽然可以对 MySQL 服务器性能进行维护,但是会降低 MySQL 的执行速度。例如,一个查询操作比较频繁的 MySQL 中,记录通用查询日志和慢查询日志要花费很多的时间。日志文件还会占用大量的硬盘空间。对于用户量非常大、操作非常频繁的数据库,日志文件需要的存储空间甚至比数据库文件需要的存储空间还要大。因此,是否启动日志,启动什么类型的日志要根据具体的应用来决定。
这里主要讲解【慢查询日志】,通俗的说,MySQL 慢查询日志是排查问题的 SQL 语句,以及检查当前 MySQL 性能的一个重要功能。如果不是调优需要,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。不过相对于排查问题来说,这点性能问题不足挂齿。
慢查询命令:
# 慢查询命令 SHOW VARIABLES LIKE 'slow_query%'; # 查询超过多少秒才记录 SHOW VARIABLES LIKE 'long_query_time';
慢查询的日志在我们安装 MySQL 时指定的 data 目录:
# 启动/停止 慢查询 SET GLOBAL slow_query_log=ON/OFF;
然后修改 my.ini 的慢查询时间配置,如图,搜索【long_query_time】:修改时间为 0.01 秒,为了测试使用。
网上说的【set global long_query_time=1】 不靠谱,重启 MySQL 服务后就失效了,达不到我们的要求,还是老实点修改 my.ini 配置文件吧。
设置好之后重启 MySQL 服务器就生效了。
OK,我们定位了慢查询之后,就针对慢查询做一系列的调优了。
我们先在数据库里新增1万条测试数据,可以参考博客:https://blog.csdn.net/BiandanLoveyou/article/details/116280263
比如我们设置了慢查询的时间上限是 0.01 秒,超过0.01秒的数据库语句会被记录到慢查询日志:
select * from t_user where user_name like '%名%' group by user_name order by user_name;
然后打开慢查询日志可以看到类似如下信息,包括执行时间、执行的SQL:
# Time: 2021-04-30T05:08:13.779360Z # User@Host: root[root] @ localhost [127.0.0.1] Id: 6 # Query_time: 0.020624 Lock_time: 0.000085 Rows_sent: 10000 Rows_examined: 30044 SET timestamp=1619759293; select * from t_user where user_name like '%名%' group by user_name order by user_name;
OK,定位出慢查询后,我们就是要优化慢查询了。
我们在 select 关键字前,加上【explain】关键字,来分析慢查询。通过 explain 关键字知道查询效率低下的原因,从而改进我们的查询,让查询优化器能更好的工作。:
explain select * from t_user where user_name like '%名%' group by user_name order by user_name;
执行结果:
各项说明:
id 代表执行编号,select_type 代表本行是简单还是复杂查询,table 代表的是访问的哪张表, type 代表的是访问类型,这里的all是最坏的一种情况,代表全表扫描, possible_keys 表示哪一些索引有利于高效的查找,key代表mysql决定采用哪个索引来优化查询,key_len代表显示mysql在索引里使用的字节数, ref代表之前的表在key列记录的索引中查找值所用的列或常量,rows代表为了找到所需的行而需要读取的行数,估算值,不精确,filtered代表按表条件筛选的行的百分比,Extra代表额外信息
type 的其它类型:
重点关注 Extra 这一项,如果 Extra 出现以下2项内容说明没有用到索引: Using temporary; Using filesort
Using temporary:表示MySQL在对查询结果排序时使用临时表。常见于 order by 和 group by。【将排序的结果存储到一张临时表中,方便后面做各种查询使用】
Using filesort:表示 MySQL 会对结果使用一个外部索引排序,而不是从表里按索引次序读到相关内容。 可能在内存或磁盘上进行排序。MySQL无法利用索引完成的排序操作称为“文件排序”。
但是,我们怎么知道哪些字段创建索引合适呢?索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引。
# 给 user_name 字段创建索引 alter table t_user add index userNameIndex(user_name);
执行结果:
再次执行查询的分析语句:
说明:
①经常查询的字段要设置索引,使用索引比不使用索引的查询效率更高。
②like 关键字模糊查询时,如果匹配字符串的第一个字符为 % 时索引不会被使用。如果 % 不在第一个位置,索引就会被使用。因此上面的写法还可以优化。
③where条件有 or 关键字时,or 的每一项都要有索引时,才会使用索引,否则不会使用索引。
④经过普通运算或函数运算后的索引字段不会使用索引。比如:select * from users where YEAR(adddate)<2021; 这句 SQL 就不会使用索引。可以改为:select * from users where adddate<‘2021-01-01';
⑤使用连接查询代替子查询。因为在子查询时,MySQL 需要为内层查询语句的查询结果建立一个临时表。然后外层查询语句在临时表中查询记录。查询完毕后,MySQL需要撤销这些临时表。因此,子查询的速度会受到一定的影响。如果查询的数据量比较大,这种影响就会随之增大。而连接查询不需要建立临时表,其速度比子查询要快。
⑥如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引。
⑦联合索引的最左前缀匹配原则。如果在一张表中,存在联合索引的话,在根据条件查询的时候必须要加上第一个索引条件。比如创建两个列的联合索引(id、user_name),在where条件中,第一个条件需要使用有索引的列,一直到没有索引的列为止。可以参考此博客:https://blog.csdn.net/u013568373/article/details/93891531
⑧使用分表分库提高数据库查询效率。中间件会将此语句根据有n个子表,拆分成n条语句:如select * from user1,select * from user2,select * from user3等多条sql语句,去子表中查询。然后将结果返回给中间件,中间件进行汇总返回给客户端。而这些子语句是同时执行的,所以查询效率非常高。
⑨not in 和 <> 操作都不会使用索引,将进行全表扫描。not in 可以 not exists 代替,【id<>5】则可使用【 id>5 or id<5 】来代替。
⑩in 和 not in 也要慎用。对于连续的数值,能用 between 就不要用 in :select id from t_user where num between 1 and 5;
11、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连 接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
12、在硬件配置上,加大物理内存,提高文件系统性能。SSD硬盘代替SAS硬盘。或者做数据库集群。