在日常工作中你会从哪些维度进行 MySQL 性能优化呢?
所谓的性能优化,一般针对的是 MySQL 查询的优化。既然是优化查询,
我们自然要先知道查询操作要经过哪些环节,然后思考可以在哪些环节
进行优化。
我用一张图展示查询操作需要经历的基本环节。
SQL 查询的环节
下面从 5 个角度介绍一下 MySQL 优化的一些策略。
1. 连接配置优化
处理连接是 MySQL 客户端和 MySQL 服务端亲热的第一步,第一步都
迈不好,也就别谈后来的故事了。
既然连接是双方的事情,我们自然从服务端和客户端两个方面来进行优
化喽。
1.1 服务端配置
服务端需要做的就是尽可能地多接受客户端的连接,或许你遇到过
error 1040: Too many connections 的错误?就是服务端的胸怀不够宽
广导致的,格局太小!
我们可以从两个方面解决连接数不够的问题:
. 增加可用连接数,修改环境变量 max_connections,默认情况下
服务端的最大连接数为 151 个
mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 151 |
+-----------------+-------+1 row in set (0.01 sec)
. 及时释放不活动的连接,系统默认的客户端超时时间是 28800 秒
(8 小时),我们可以把这个值调小一点
mysql> show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout | 28800 |
+---------------+-------+1 row in set (0.01 sec)
MySQL 有非常多的配置参数,并且大部分参数都提供了默认值,默认值是
MySQL 作者经过精心设计的,完全可以满足大部分情况的需求,不建议在不
清楚参数含义的情况下贸然修改。
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-slave 这种具有侵犯性的词汇(因为他们认为
会联想到种族歧视、黑人奴隶等),所以发起了一项更名运动。
受此影响 MySQL 也会逐渐停用 master、slave 等术语,转而用 source 和
replica 替代,大家碰到的时候明白即可。
使用集群必然面临一个问题,就是多个节点之间怎么保持数据的一致性。
毕竟写请求只往 master 节点上发送了,只有 master 节点的数据是最新
数据,怎么把对 master 节点的写操作也同步到各个 slave 节点上呢?
binlog 是实现 MySQL 主从复制功能的核心组件。master 节点会将所有
的写操作记录到 binlog 中,slave 节点会有专门的 I/O 线程读取 master
节点的 binlog,将写操作同步到当前所在的 slave 节点。
主从复制
这种集群的架构对减轻主数据库服务器的压力有非常好的效果,但是随
着业务数据越来越多,如果某张表的数据量急剧增加,单表的查询性能
就会大幅下降,而这个问题是读写分离也无法解决的,毕竟所有节点存
放的是一模一样的数据啊,单表查询性能差,说的自然也是所有节点性
能都差。
这时我们可以把单个节点的数据分散到多个节点上进行存储,这就是分
库分表。
2.3 分库分表
分库分表中的节点的含义比较宽泛,要是把数据库作为节点,那就是分
库;如果把单张表作为节点,那就是分表。
大家都知道分库分表分成垂直分库、垂直分表、水平分库和水平分表,
但是每次都记不住这些概念,我就给大家详细说一说,帮助大家理解。