慢sql优化方式
慢 SQL 优化是我们工作和面试中经常遇到的问题。怎么优化网上有很多,但大多数都是基于 explain,去优化 SQL 比较片面也无法解决全部问题。个人对慢 SQL 优化,做了以下这些总结,根据优先级排序。
索引:
索引是最快优化 SQL 的方式,并且基本不需要修改业务代码。我们可以根据 SQL 查询条件的频次给对应的表加上索引,可以通过 explain
查看 SQL 执行的效果来观察添加索引的效果。但需要注意的是,如果索引加的过多,也可能导致数据写入性能下降(插入、更新、删除),存储空间占用增加的问题。
explain需要关注的核心指标有
select_type、type、possible_keys、key、rows、Extra
- select_type:表示查询中每个select子句的类型,是简单查询还是联合查询还是子查询:
select_type的值 | 解释 |
---|---|
SIMPLE | 简单查询(不使用关联查询或子查询) |
PRIMARY | 如果包含关联查询或者子查询,则最外层的查询部分标记primary |
UNION | 联合查询(UNION)中第二个及后面的查询 |
DEPENDENT UNION | UNION中的第二个或后面的SELECT语句,取决于外面的查询 |
UNION RESULT | UNION的结果,union语句中第二个select开始后面所有select |
SUBQUERY | 字查询中的第一个查询 |
DEPENDENT SUBQUERY | 子查询中的第一个查询,并且依赖外部查询 |
DERIVED | 派生表的SELECT, FROM子句的子查询 |
MATERIALIZED | 被物化的子查询 |
UNCACHEABLE SUBQUERY | 一个子查询的结果不能被缓存,必须重新评估外链接的第一行 |
- type:表示MySQL在表中查找所需数据的方式。
type的值 | 解释 |
---|---|
system | 查询对象表只有一行数据,且只能用于MyISAM和Memory引擎的表,这是最好的情况 |
const | 基于主键或唯一索引查询,最多返回一条结果 |
eq_ref | 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件 |
ref | 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值 |
fulltext | 全文检索 |
ref_or_null | 表连接类型是ref,但进行扫描的索引列中可能包含NULL值 |
index_merge | 利用多个索引 |
unique_subquery | 子查询中使用唯一索引 |
index_subquery | 子查询中使用普通索引 |
range | 只检索给定范围的行,使用一个索引来选择行 |
index | Full Index Scan,index与ALL区别为index类型只遍历索引树 |
ALL | Full Table Scan, MySQL将遍历全表以找到匹配的行 |
- possible_keys:应该或建议使用的索引
- key:实际使用的索引,没有的情况下为NULL
- rows:预估扫描了了多少行。
- Extra:包含一些额外的信息,如是否使用了临时表、是否进行了文件排序、是否进行了优化等。
冗余:
冗余是指存储冗余数据,将原来多个表的数据抽取到一张表,以减少联接和复杂查询的需求,从而提高查询性能。但抽取的表可能会数据过多或者行数据过多导致查询慢,需要根据数据量和业务酌情考虑。也可以将这些数据抽取到列数据库或者 ES 中。
批量处理:
批量写入将多个操作组合成单个事务,而不是执行单个插入或更新操作。这可以减少开销,提高整体吞吐量,特别是处理大型数据集时 。
减小锁粒度:
MySQL在使用InnoDB
引擎的情况下 可以采用以下方式减少锁的粒度和时间
- 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
- 合理设计索引,尽量缩小锁的范围
- 尽可能较少检索条件,避免间隙锁
- 尽量控制事务大小,减少锁定资源量和时间长度
- 尽可能低级别事务隔离
减少复杂查询:
通过将复杂查询拆分为更小,可以简化SQL查询并提高性能。涉及优化联接、减少子查询的数量,或编写查询以更有效地利用索引等。
核心思想是尽可能减少:(行记录扫描)和(临时表)。
常用手段:
•层级合并 减少临时表个数
•条件下推 减少检索行数&临时表大小
•join优化 减少检索行数&临时表大小
•子查询删除 减少临时表个数
•子查询与join的相互转换 减少检索行数
事务处理:
可以采用编程式事务更精准的控制事务进而提高并发性。这包括仔细考虑事务隔离级别、事务边界和事务设计,进而平衡数据一致性和性能。
提升硬件性能:
可以根据负载情况,升级硬件CPU、内存、存储和网络,可以显著提升数据库性能,并注意需要将数据库和应用隔离部署避免相互影响 。
读写分离:
分离读和写可以有效提高可伸缩性。通过将读从库,主库可以专注于处理写入操作,而不会被读请求压垮。并且可以通过扩展从库提高数据库的读 QPS。但要注意读写分离带来的主从延迟,并且无法解决写入性能和数据过多带来的查询慢的问题。
分库:
分三种情况
- 垂直分库
根据业务把不同业务的数据,根据数据库隔离存储,降低不同业务之间的影响,可以解决非核心业务影响核心业务。
2.读写分离
每个库数据完全一致,采用读写分离 可以解决减缓数据库读 qps过高,数据库连接不足 和磁盘io瓶颈 无法解决写入性能和数据过多带来的性能的问题
- 数据分片 分库
根据自定义算法根据数据分片,每个分片包含数据的子集,可以实现并行处理并可以再提供读写分离提高读性能。可以解决减缓数据库读写 QPS 过高,数据库连接不足和磁盘 IO 瓶颈以及数据过多带来的读写性能问题。
分库分表-具体内容可以参考本人写的下列文章
分库分表详解 上-基础概念
分库分表详解 中- 数据迁移
分库分表详解 下-分库分表方案模版