MySQL 的优化

MySQL 的优化一般是针对查询操作来说的，首先我们得知道 SQL 查询需要经过哪些操作。

一、连接配置优化

1.1 服务端优化

修改默认的服务端的最大连接数（系统默认一般是 151）和默认的客户端超时时间（默认的一般是 8 小时），当然若无必要不需要修改。以下是查询它们的系统默认值的方式：

1.2 客户端优化

对于客户端的优化，我们是不希望客户频繁的创建和销毁连接的，这时我们就需要用数据库连接池来复用连接以减少数据库资源的开销。当前最流行的数据库连接池就是 Druid 和 HikariCP。DBCP 和 C3P0 已经。。。

当然我们需要注意连接池大小的设置并不是越大越好。因为每个连接都需要一个线程去维护，当线程数超过 CPU 个数时，就需要频繁的切换线程的上下文从而导致性能的开销变大。

二、架构优化

2.1. 使用缓存

在查询和数据库之间加缓存，放一些不会经常发生变化的数据。

2.2 读写分离

这种方式主要是降低服务器的压力。使用服务器集群，其中一台服务器作为 master 节点，其他服务器作为 slave 节点。当主服务器的数据改变之后，其他的服务器也需要进行同步，master 节点只负责增删改的操作，slave 节点负责查询。这种方式很好的降低了服务器的压力。

2.3 分库分表

2.3.1 垂直分库

在单体数据库的基础上垂直切几刀，按照业务逻辑将不同的表分到不同的数据库中，并且这些数据库是在不同的服务器上面，这就是垂直分库。

2.3.2 垂直分表

垂直分表就是在单表的基础上垂直切一刀（或几刀），将一个表的多个字段拆成若干个小表。这种操作需要根据具体业务来进行判断，通常会把经常使用的字段（热字段）分成一个表，不经常使用或者不立即使用的字段（冷字段）分成一个表。这不仅解决了单一表数据量过大的问题而且提升了查询速度。

拿上图举例：通常情况下商品的详情信息都比较长，且查看商品列表时往往不需要立即展示商品详情（一般都是点击详情按钮才会进行显示），而是会将商品更重要的信息（价格等) 展示出来。按照这个业务逻辑，我们将原来的商品表做了垂直分表。

2.3.3 水平分库

经过垂直分库后，数据库性能问题得到一定程度的解决。可随着业务量的增长，单体数据库中存储的数据已经超出预估，单台服务器已经无法支撑，但从业务角度来看，此时已经无法垂直分库了。这时就可以尝试按照一定的规则水平分库，比如说（将 id 为单数的和 id 为双数的商品信息分别放在两个库中）。

2.3.4 水平分表

把单张表的数据按照一定的规则（行话叫分片规则）保存到多个数据表上，横着给数据表来一刀（或几刀），就是水平分表了。

2.3.5 总结

水平分表，主要是为了解决存储的瓶颈；垂直分表，主要是为了减轻并发压力。

2.4 消息队列消峰

通常情况下，用户的请求会直接访问数据库，如果同一时刻在线用户数量非常庞大，极有可能压垮数据库（参考明星出轨或公布恋情时微博的状态）。

这种情况下可以通过使用消息队列降低数据库的压力，不管同时有多少个用户请求，先存入消息队列，然后系统有条不紊地从消息队列中消费请求。

三、优化器 ——SQL 分析与优化

到了这一步就已经来到了解析器和优化器的地盘了。一般来说，你的 SQL 语法没有问题，解析器就不会出现问题。此外，为了防止你写的 SQL 运行效率低，优化器会自动做一些优化，但如果实在是太烂，优化器也救不了你了，只能眼睁睁地看着你的 SQL 查询沦为慢查询。

3.1 慢查询

顾名思义，慢查询就是很慢的查询。使用以下命令可以查看 mysql 的满查询状态：

输出以下的命令查看满的标准：

3.2 查看运行中的线程

运行 show full processlist 查看 mysql 运行的所有线程

其中，

Id： 线程的唯一标志，可以使用 Id 杀死指定线程；

User： 启动这个线程的用户，普通账户只能查看自己的线程；

Host： 哪个 ip 和端口发起的连接；

db： 线程操作的数据库；

Command： 线程的命令；

Time： 操作持续时间，单位秒；

State： 线程的状态；

Info： SQL 语句的前 100 个字符。

3.3 查看服务器的运行状态

使用SHOW STATUS查看 MySQL 服务器的运行状态，有session和global两种作用域

以上代码是查看 select 的次数

3.4 查看存储引擎运行信息

Copy
SHOW ENGINE INNODB STATUS;

3.5 EXPLAIN 执行计划

所谓的计划就是我们的 MySQL 中的优化器会不会帮我们把我们写的 sql 语句优化（比如外连接改内连接查询，子查询优化为连接查询...），优化器针对此条 SQL 的执行对哪些索引进行了成本估算，并最终决定采用哪个索引（或者最终选择不用索引，而是全表扫描），优化器对单表执行的策略是什么，等等等等。

explain 执行计划以前通常用在查询语句上，不过现在也可以用在增删改的语句中了

3.6 SQL 与索引优化

3.6.1 SQL 优化

使用小表驱动大表；用 join 改写子查询（嵌套查询）；或者改成 union；

连接查询中，尽量减少驱动表的扇出（记录数），访问被驱动表的成本要尽量低，尽量在被驱动表的连接列上建立索引，降低访问成本；被驱动表的连接列最好是该表的主键或者是唯一二级索引列，这样被驱动表的成本会降到更低；

大偏移量的 limit，先过滤再排序。

3.6.2 索引优化

1. 能不回表就不回表，如果回表就减少回表次数（回表是有代价的，有可能会从外存中加载数据页）；

2. 使用索引覆盖。

四、存储结构与表结构

4.1 选择存储引擎

建议根据不同的业务选择不同的存储引擎，例如：

查询操作、插入操作多的业务表，推荐使用MyISAM；

临时表使用Memory；

并发数量大、更新多的业务选择使用InnoDB；

不知道选啥直接默认。

4.2 优化字段

原则：使用可以正确存储数据的最小的数据类型。

4.2.1 整数类型

MySQL 提供了 6 种整数类型，分别是

tinyint

smallint

mediumint

int

integer

bigint

4.2.2 字符类型

如果是不确定字段长度，无脑选 varchar，因为 varchar 会有额外的空间记录该字段的长度，但是如果字段长度固定的，尽量选取 char，这会节省不少内存空间。

4.2.3 非空

非空字段尽量设置成NOT NULL，并提供默认值，或者使用特殊值代替NULL。因为NULL类型的存储和优化都会存在性能不佳的问题。

4.2.4 不要使用外键、触发器和视图功能（特别是大数据量时，碰一个就 G）

这也是「阿里巴巴开发手册」中提到的原则。原因有三个：

降低了可读性，检查代码的同时还得查看数据库的代码；

把计算的工作交给程序 (业务层)，数据库只做好存储的工作，并把这件事情做好；

一般大数据量的时候为了解决存储瓶颈，会水平分表；

触发器可以理解为及时捕捉数据的变化，并做相应的处理，设计初衷非常好，但是劣势掩盖了优势，增删改操作会频繁的触发触发器，导致资源消耗特别大；

数据的完整性校验的工作应该由开发者完成，而不是依赖于外键，一旦用了外键，你会发现测试的时候随便删点垃圾数据都变得异常艰难（外键约束，会导致表结构非常混乱，还可能会循环约束）。

4.2.5 图片、音频、视频存储

存储它们的地址即可。

4.2.6 大字段拆分和数据冗余

大字段拆分其实就是前面说过的垂直分表，把不常用的字段或者数据量较大的字段拆分出去，避免列数过多和数据量过大，尤其是习惯编写SELECT *的情况下，列数多和数据量大导致的问题会被严重放大！

字段冗余原则上不符合数据库设计范式，但是却非常有利于快速检索。比如，合同表中存储客户 id 的同时可以冗余存储客户姓名，这样查询时就不需要再根据客户 id 获取用户姓名了。因此针对业务逻辑适当做一定程度的冗余也是一种比较好的优化技巧。（一般用于查询多修改少的场景）

五、业务优化

严格来说，业务方面的优化已经不算是 MySQL 调优的手段了，但是业务的优化却能非常有效地减轻数据库访问压力，这方面一个典型例子就是淘宝，下面举几个简单例子给大家提供一下思路：

预售分流：以往都是双 11 当晚开始买买买的模式，最近几年双 11 的预售战线越拉越长，提前半个多月就开始了，而且各种定金红包模式丛出不穷，这种方式叫做预售分流。这样做可以分流客户的服务请求，不必等到双十一的凌晨一股脑地集体下单；

降级策略：双十一的凌晨你或许想查询当天之外的订单，但是却查询失败；甚至支付宝里的小鸡的口粮都被延迟发放了，这是一种降级策略，集结不重要的服务的计算资源，用来保证当前最核心的业务；

双十一的时候支付宝极力推荐使用花呗支付，而不是银行卡支付，虽然一部分考量是提高软件粘性，但是另一方面，使用余额宝实际使用的阿里内部服务器，访问速度快，而使用银行卡，需要调用银行接口，相比之下操作要慢了许多。