首页 > 代码库 > mysql sql优化
mysql sql优化
前言
有人反馈之前几篇文章过于理论缺少实际操作细节。这篇文章就多一些可操作性的内容吧。
注:这篇文章是以 MySQL 为背景,非常多内容同一时候适用于其它关系型数据库,须要有一些索引知识为基础。
优化目标
1.降低 IO 次数
IO永远是数据库最easy瓶颈的地方,这是由数据库的职责所决定的,大部分数据库操作中超过90%的时间都是 IO 操作所占用的,降低 IO 次数是 SQL 优化中须要第一优先考虑。当然,也是收效最明显的优化手段。
2.减少 CPU 计算
除了 IO 瓶颈之外,SQL优化中须要考虑的就是 CPU 运算量的优化了。
order by, group by,distinct … 都是消耗 CPU 的大户(这些操作基本上都是 CPU 处理内存中的数据比較运算)。
当我们的 IO 优化做到一定阶段之后,减少 CPU 计算也就成为了我们 SQL 优化的重要目标
优化方法
改变 SQL 运行计划
明白了优化目标之后,我们须要确定达到我们目标的方法。
对于 SQL 语句来说,达到上述2个目标的方法事实上仅仅有一个,那就是改变 SQL 的运行计划,让他尽量“少走弯路”。尽量通过各种“捷径”来找到我们须要的数据。以达到 “降低 IO 次数” 和 “降低 CPU 计算” 的目标
常见误区
1.count(1)和count(primary_key) 优于 count(*)
非常多人为了统计记录条数,就使用 count(1) 和 count(primary_key) 而不是 count(*) ,他们觉得这样性能更好,事实上这是一个误区。对于有些场景,这样做可能性能会更差,应为数据库对 count(*) 计数操作做了一些特别的优化。
2.count(column) 和 count(*) 是一样的
这个误区甚至在非常多的资深project师或者是 DBA 中都普遍存在。非常多人都会觉得这是理所当然的。
实际上。count(column) 和 count(*) 是一个全然不一样的操作。所代表的意义也全然不一样。
count(column) 是表示结果集中有多少个column字段不为空的记录
count(*) 是表示整个结果集有多少条记录
3.select a,b from … 比 select a,b,c from … 能够让数据库訪问更少的数据量
这个误区主要存在于大量的开发者中,主要原因是对数据库的存储原理不是太了解。
实际上,大多数关系型数据库都是依照行(row)的方式存储,而数据存取操作都是以一个固定大小的IO单元(被称作 block 或者 page)为单位,一般为4KB,8KB… 大多数时候。每一个IO单元中存储了多行,每行都是存储了该行的全部字段(lob等特殊类型字段除外)。
所以。我们是取一个字段还是多个字段。实际上数据库在表中须要訪问的数据量事实上是一样的。
当然,也有例外情况,那就是我们的这个查询在索引中就能够完毕,也就是说当仅仅取 a,b两个字段的时候。不须要回表,而c这个字段不在使用的索引中。须要回表取得其数据。
在这种情况下。二者的IO量会有较大差异。
4.order by 一定须要排序操作
我们知道索引数据实际上是有序的,假设我们的须要的数据和某个索引的顺序一致,并且我们的查询又通过这个索引来运行,那么数据库通常会省略排序操作,而直接将数据返回,由于数据库知道数据已经满足我们的排序需求了。
实际上。利用索引来优化有排序需求的 SQL,是一个很重要的优化手段
延伸阅读:MySQL ORDER BY 的实现分析,MySQL 中 GROUP BY 基本实现原理以及 MySQL DISTINCT 的基本实现原理这3篇文章中有更为深入的分析。尤其是第一篇
5.运行计划中有 filesort 就会进行磁盘文件排序
有这个误区事实上并不能怪我们。而是由于 MySQL 开发人员在用词方面的问题。
filesort 是我们在使用 explain 命令查看一条 SQL 的运行计划的时候可能会看到在 “Extra” 一列显示的信息。
实际上,仅仅要一条 SQL 语句须要进行排序操作。都会显示“Using filesort”,这并不表示就会有文件排序操作。
基本原则
1.尽量少 join
MySQL 的优势在于简单,但这在某些方面事实上也是其劣势。MySQL 优化器效率高,可是因为其统计信息的量有限,优化器工作过程出现偏差的可能性也就很多其它。对于复杂的多表 Join,一方面因为其优化器受限,再者在 Join 这方面所下的功夫还不够,所以性能表现离 Oracle 等关系型数据库前辈还是有一定距离。
但假设是简单的单表查询。这一差距就会极小甚至在有些场景下要优于这些数据库前辈。
2.尽量少排序
排序操作会消耗较多的 CPU 资源,所以降低排序能够在缓存命中率高等 IO 能力足够的场景下会较大影响 SQL 的响应时间。
对于MySQL来说,降低排序有多种办法。比方:
上面误区中提到的通过利用索引来排序的方式进行优化
降低參与排序的记录条数
非必要不正确数据进行排序
…
3.尽量避免 select *
非常多人看到这一点后认为比較难理解,上面不是在误区中刚刚说 select 子句中字段的多少并不会影响到读取的数据吗?
是的,大多数时候并不会影响到 IO 量,可是当我们还存在 order by 操作的时候,select 子句中的字段多少会在非常大程度上影响到我们的排序效率,这一点能够通过我之前一篇介绍 MySQL ORDER BY 的实现分析的文章中有较为具体的介绍。
此外,上面误区中不是也说了,仅仅是大多数时候是不会影响到 IO 量。当我们的查询结果仅仅仅仅须要在索引中就能找到的时候。还是会极大降低 IO 量的。
4.尽量用 join 取代子查询
尽管 Join 性能并不佳,可是和 MySQL 的子查询比起来还是有非常大的性能优势。MySQL 的子查询运行计划一直存在较大的问题,尽管这个问题已经存在多年。可是到眼下已经公布的全部稳定版本号中都普遍存在,一直没有太大改善。尽管官方也在非常早就承认这一问题。而且承诺尽快解决。可是至少到眼下为止我们还没有看到哪一个版本号较好的攻克了这一问题。
5.尽量少 or
当 where 子句中存在多个条件以“或”并存的时候。MySQL 的优化器并没有非常好的解决其运行计划优化问题,再加上 MySQL 特有的 SQL 与 Storage 分层架构方式,造成了其性能比較低下,非常多时候使用 union all 或者是union(必要的时候)的方式来取代“or”会得到更好的效果。
6.尽量用 union all 取代 union
union 和 union all 的差异主要是前者须要将两个(或者多个)结果集合并后再进行唯一性过滤操作。这就会涉及到排序,添加大量的 CPU 运算,加大资源消耗及延迟。所以当我们能够确认不可能出现反复结果集或者不在乎反复结果集的时候,尽量使用 union all 而不是 union。
7.尽量早过滤
这一优化策略事实上最常见于索引的优化设计中(将过滤性更好的字段放得更靠前)。
在 SQL 编写中相同能够使用这一原则来优化一些 Join 的 SQL。比方我们在多个表进行分页数据查询的时候,我们最好是能够在一个表上先过滤好数据分好页,然后再用分好页的结果集与另外的表 Join,这样能够尽可能多的降低不必要的 IO 操作,大大节省 IO 操作所消耗的时间。
8.避免类型转换
这里所说的“类型转换”是指 where 子句中出现 column 字段的类型和传入的參数类型不一致的时候发生的类型转换:
人为在column_name 上通过转换函数进行转换
直接导致 MySQL(实际上其它数据库也会有相同的问题)无法使用索引,假设非要转换,应该在传入的參数上进行转换
由数据库自己进行转换
假设我们传入的数据类型和字段类型不一致,同一时候我们又没有做不论什么类型转换处理,MySQL 可能会自己对我们的数据进行类型转换操作,也可能不进行处理而交由存储引擎去处理,这样一来,就会出现索引无法使用的情况而造成运行计划问题。
9.优先优化高并发的 SQL,而不是运行频率低某些“大”SQL
对于破坏性来说,高并发的 SQL 总是会比低频率的来得大,由于高并发的 SQL 一旦出现故障。甚至不会给我们不论什么喘息的机会就会将系统压跨。而对于一些尽管须要消耗大量 IO 并且响应非常慢的 SQL,由于频率低,即使遇到。最多就是让整个系统响应慢一点,但至少可能撑一会儿,让我们有缓冲的机会。
10.从全局出发优化。而不是片面调整
SQL 优化不能是单独针对某一个进行,而应充分考虑系统中全部的 SQL,尤其是在通过调整索引优化 SQL 的运行计划的时候。千万不能顾此失彼,因小失大。
11.尽可能对每一条执行在数据库中的SQL进行 explain
优化 SQL。须要做到心中有数。知道 SQL 的运行计划才干推断是否有优化余地。才干推断是否存在运行计划问题。
在对数据库中运行的 SQL 进行了一段时间的优化之后,非常明显的问题 SQL 可能已经非常少了,大多都须要去发掘。这时候就须要进行大量的 explain 操作收集运行计划。并推断是否须要进行优化。
mysql sql优化