概述
面试官如果问你:你会从哪些维度进行MySQL性能优化?你会怎么回答?
所谓的性能优化,一般针对的是MySQL查询的优化。既然是优化查询,我们自然要先知道查询操作要经过哪些环节,然后思考可以在哪些环节进行优化。
我用一张图展示查询操作需要经历的基本环节。
下面从5个角度介绍一下MySQL优化的一些策略。
1. 连接配置优化
处理连接是MySQL客户端和MySQL服务端亲热的第一步,第一步都迈不好,也就别谈后来的故事了。
既然连接是双方的事情,我们自然从服务端和客户端两个方面来进行优化喽。
1.1 服务端配置
服务端需要做的就是尽可能地多接受客户端的连接,或许你遇到过error 1040: Too many connections
的错误?就是服务端的胸怀不够宽广导致的,格局太小!
我们可以从两个方面解决连接数不够的问题:
1、增加可用连接数,修改环境变量max_connections
,默认情况下服务端的最大连接数为151
个
mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 151 |
+-----------------+-------+
1 row in set (0.01 sec)
登录后复制
2、及时释放不活动的连接,系统默认的客户端超时时间是28800秒(8小时),我们可以把这个值调小一点
mysql> show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout | 28800 |
+---------------+-------+
1 row in set (0.01 sec)
登录后复制
1.2 客户端优化
客户端能做的就是尽量减少和服务端建立连接的次数,已经建立的连接能凑合用就凑合用,别每次执行个SQL语句都创建个新连接,服务端和客户端的资源都吃不消啊。
解决的方案就是使用连接池来复用连接。
常见的数据库连接池有DBCP
、C3P0
、阿里的Druid
、Hikari
,前两者用得很少了,后两者目前如日中天。
但是需要注意的是连接池并不是越大越好,比如Druid
的默认最大连接池大小是8,Hikari
默认最大连接池大小是10,盲目地加大连接池的大小,系统执行效率反而有可能降低。为什么?
对于每一个连接,服务端会创建一个单独的线程去处理,连接数越多,服务端创建的线程自然也就越多。而线程数超过CPU个数的情况下,CPU势必要通过分配时间片的方式进行线程的上下文切换,频繁的上下文切换会造成很大的性能开销。
Hikari官方给出了一个PostgreSQL
数据库连接池大小的建议值公式,CPU核心数*2+1
。假设服务器的CPU核心数是4,把连接池设置成9就可以了。这种公式在一定程度上对其他数据库也是适用的,大家面试的时候可以吹一吹。
2. 架构优化
2.1 使用缓存
系统中难免会出现一些比较慢的查询,这些查询要么是数据量大,要么是查询复杂(关联的表多或者是计算复杂),使得查询会长时间占用连接。
如果这种数据的实效性不是特别强(不是每时每刻都会变化,例如每日报表),我们可以把此类数据放入缓存系统中,在数据的缓存有效期内,直接从缓存系统中获取数据,这样就可以减轻数据库的压力并提升查询效率。
2.2 读写分离(集群、主从复制)
项目的初期,数据库通常都是运行在一台服务器上的,用户的所有读写请求会直接作用到这台数据库服务器,单台服务器承担的并发量毕竟是有限的。
针对这个问题,我们可以同时使用多台数据库服务器,将其中一台设置为为小组长,称之为master
节点,其余节点作为组员,叫做slave
。用户写数据只往master
节点写,而读的请求分摊到各个slave
节点上。这个方案叫做读写分离。给组长加上组员组成的小团体起个名字,叫集群。
使用集群必然面临一个问题,就是多个节点之间怎么保持数据的一致性。毕竟写请求只往master
节点上发送了,只有master
节点的数据是最新数据,怎么把对master
节点的写操作也同步到各个slave
节点上呢?
主从复制技术来了!我在之前的文章中粗浅地介绍了一下binlog日志,我直接搬过来了。
binlog
是实现MySQL主从复制功能的核心组件。master
节点会将所有的写操作记录到binlog中,slave
节点会有专门的I/O线程读取master
节点的binlog,将写操作同步到当前所在的slave
节点。
这种集群的架构对减轻主数据库服务器的压力有非常好的效果,但是随着业务数据越来越多,如果某张表的数据量急剧增加,单表的查询性能就会大幅下降,而这个问题是读写分离也无法解决的,毕竟所有节点存放的是一模一样的数据啊,单表查询性能差,说的自然也是所有节点性能都差。
这时我们可以把单个节点的数据分散到多个节点上进行存储,这就是分库分表。
2.3 分库分表
分库分表中的节点的含义比较宽泛,要是把数据库作为节点,那就是分库;如果把单张表作为节点,那就是分表。
大家都知道分库分表分成垂直分库、垂直分表、水平分库和水平分表,但是每次都记不住这些概念,我就给大家详细说一说,帮助大家理解。
2.3.1 垂直分库
在单体数据库的基础上垂直切几刀,按照业务逻辑拆分成不同的数据库,这就是垂直分库啦。
2.3.2 垂直分表
垂直分表就是在单表的基础上垂直切一刀(或几刀),将一个表的多个字短拆成若干个小表,这种操作需要根据具体业务来进行判断,通常会把经常使用的字段(热字段)分成一个表,不经常使用或者不立即使用的字段(冷字段)分成一个表,提升查询速度。
拿上图举例:通常情况下商品的详情信息都比较长,而且查看商品列表时往往不需要立即展示商品详情(一般都是点击详情按钮才会进行显示),而是会将商品更重要的信息(价格等)展示出来,按照这个业务逻辑,我们将原来的商品表做了垂直分表。
2.3.3 水平分表
把单张表的数据按照一定的规则(行话叫分片规则)保存到多个数据表上,横着给数据表来一刀(或几刀),就是水平分表了。
2.3.4 水平分库
水平分库就是对单个数据库水平切一刀,往往伴随着水平分表。
2.3.5 总结
水平分,主要是为了解决存储的瓶颈;垂直分,主要是为了减轻并发压力。
2.4 消息队列削峰
通常情况下,用户的请求会直接访问数据库,如果同一时刻在线用户数量非常庞大,极有可能压垮数据库(参考明星出轨或公布恋情时微博的状态)。
这种情况下可以通过使用消息队列降低数据库的压力,不管同时有多少个用户请求,先存入消息队列,然后系统有条不紊地从消息队列中消费请求。
3. 优化器——SQL分析与优化
处理完连接、优化完缓存等架构的事情,SQL查询语句来到了解析器和优化器的地盘了。在这一步如果出了任何问题,那就只能是SQL语句的问题了。
只要你的语法不出问题,解析器就不会有问题。此外,为了防止你写的SQL运行效率低,优化器会自动做一些优化,但如果实在是太烂,优化器也救不了你了,只能眼睁睁地看着你的SQL查询沦为慢查询。
3.1 慢查询
慢查询就是执行地很慢的查询(这句话说得跟废话似的。。。),只有知道MySQL中有哪些慢查询我们才能针对性地进行优化。
因为开启慢查询日志是有性能代价的,因此MySQL默认是关闭慢查询日志功能,使用以下命令查看当前慢查询状态
mysql> show variables like 'slow_query%';
+---------------------+--------------------------------------+
| Variable_name | Value |
+---------------------+--------------------------------------+
| slow_query_log | OFF |
| slow_query_log_file | /var/lib/mysql/9e74f9251f6c-slow.log |
+---------------------+--------------------------------------+
2 rows in set (0.00 sec)
登录后复制
slow_query_log
表示当前慢查询日志是否开启,slow_query_log_file
表示慢查询日志的保存位置。
除了上面两个变量,我们还需要确定“慢”的指标是什么,即执行超过多长时间才算是慢查询,默认是10S
,如果改成0
的话就是记录所有的SQL。
mysql> show variables like '%long_query%';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set (0.00 sec)
登录后复制
3.1.1 打开慢日志
有两种打开慢日志的方式
1、修改配置文件my.cnf
此种修改方式系统重启后依然有效
# 是否开启慢查询日志
slow_query_log=ON
#
long_query_time=2
slow_query_log_file=/var/lib/mysql/slow.log
登录后复制
2、动态修改参数(重启后失效)
mysql> set @@global.slow_query_log=1;
Query OK, 0 rows affected (0.06 sec)
mysql> set @@global.long_query_time=2;
Query OK, 0 rows affected (0.00 sec)
登录后复制
3.1.2 慢日志分析
MySQL不仅为我们保存了慢日志文件,还为我们提供了慢日志查询的工具mysqldumpslow
,为了演示这个工具,我们先构造一条慢查询:
mysql> SELECT sleep(5);
登录后复制
然后我们查询用时最多的1条慢查询:
[root@iZ2zejfuakcnnq2pgqyzowZ ~]# mysqldumpslow -s t -t 1 -g 'select' /var/lib/mysql/9e74f9251f6c-slow.log
Reading mysql slow query log from /var/lib/mysql/9e74f9251f6c-slow.log
Count: 1 Time=10.00s (10s) Lock=0.00s (0s) Rows=1.0 (1), root[root]@localhost
SELECT sleep(N)
登录后复制
其中,
- Count:表示这个SQL执行的次数
- Time:表示执行的时间,括号中的是累积时间
- Locks:表示锁定的时间,括号中的是累积时间
- Rows:表示返回的记录数,括号中的是累积数
最后
以上就是朴实摩托为你收集整理的MySQL如何进行优化?从5个维度聊聊性能优化的全部内容,希望文章能够帮你解决MySQL如何进行优化?从5个维度聊聊性能优化所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复