SQL优化
优化概述
数据库性能取决于数据库级别的多个因素,例如表、查询和配置设置。这些软件构造会导致硬件级别的 CPU 和 I/O 操作,您必须将其最小化并尽可能高效。
典型用户的目标是从其现有软件和硬件配置中获得最佳数据库性能。高级用户寻找机会改进MySQL软件本身,或开发自己的存储引擎和硬件设备来扩展MySQL生态系统。
(1)在数据库级别进行优。
(2)在硬件级别进行优化。
(3)平衡便携性和性能。
优化器成本
MySQL 优化器主要针对 IO 和 CPU 会计算语句的成本;可能不会按照分析的原理来执行语句。
成本分析步骤:
(1)找出所有可能需要使用到的索引。
(2)计算全表扫描的代价。
(3)计算不同索引执行查询的代价。
(4)对比找出代价最小的执行方案。
EXPLAIN
用来查看 SQL 语句的具体执行过程。
原理:模拟优化器执行 SQL 查询语句,从而知道 MySQL 是如何处理 SQL 语句的。
执行计划
Column | Meaning |
---|---|
id | The SELECT identifier (查询id) |
select_type | The SELECT type (查询类型) |
table | The table for the output row (输出结果集的表) |
partitions | The matching partitions (匹配的分区) |
type | The join type (表的连接类型) |
possible_keys | The possible indexes to choose(可能使用的索引) |
key | The index actually chosen (实际使用的索引) |
key_len | The length of the chosen key (索引字段的长度) |
ref | The columns compared to the index (列与索引的比较) |
rows | Estimate of rows to be examined (预估扫描行数) |
filtered | Percentage of rows filtered by tablecondition (按表条件过滤的行百分比) |
extra | Additional information (额外信息,如是否使用索引覆盖) |
示例:
DROP TABLE IF EXISTS `covering_index_t`;
CREATE TABLE `covering_index_t` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) DEFAULT NULL,
`cid` INT(11) DEFAULT NULL,
`age` SMALLINT DEFAULT 0,
`score` SMALLINT DEFAULT 0,
PRIMARY KEY (`id`),
KEY `name_cid_idx` (`name`, `cid`)
)ENGINE = INNODB AUTO_INCREMENT=0 DEFAULT CHARSET = utf8;
INSERT INTO `covering_index_t` (`name`, `cid`, `age`, `score`)
VALUES
('FLY', 10001, 12, 99),
('fly', 10002, 13, 98),
('cc', 10003, 14, 97),
('qq', 10004, 15, 100);
EXPLAIN SELECT * FROM `covering_index_t` WHERE `name` = 'mark';
id
select 查询的序列号,包含一组数字,表示查询中执行select 子句或者操作表的顺序。
id 号分为三种情况:
- id 相同,那么执行顺序从上到下。
- id 不同,id 越大越先执行。
- id 有相同的也有不同的,id 相同的按 1 执行,id 不同的按 2 执行。
select_type
主要用来分辨查询的类型,是普通查询还是联合查询还是子查询。
select_type value | Meaning |
---|---|
SIMPLE | 简单查询-没有联合查询和子查询 |
PRIMARY | 最外层select |
UNION | 若第二个select出现在union之后,则被标记为union |
DEPENDENT UNION | union或union all联合而成的结果会受外部表影响 |
UNION RESULT | 从union表获取结果的select |
SUBQUERY | select或者where列表中包含子查询 |
DEPENDENT SUBQUERY | subquery的子查询要受到外部表查询的影响 |
DERIVED | from子句中出现的子查询,也叫做派生表 |
UNCACHEABLE SUBQUERY | 一个子查询,其结果不能被缓存,必须为外部查询的每一行重新求值。表示使用子查询的结果不能被缓存。 |
UNCACHEABLE UNION | 表示union的查询结果不能被缓存:sql语句未验证 |
table
对应行正在访问哪一个表,表名或者别名,可能是临时表或者union 合并结果集。
- 具体表名或者表的别名,从具体的物理表中获取数据。
- 表明为 derivedN 的形式,表示 id 为 N 的查询产生的衍生表。
- 当有 union result 的时候,表名是 union n1,n2 等的形式,n1,n2 表示参与 union的 id。
type
type 显示访问类型;采用怎么样的方式来访问数据;效率从好到坏依次为:
system > const > eq_ref > ref > fulltext > ref_or_null >index_merge > unique_subquery > index_subquery > range> index > ALL
type value | meaning |
---|---|
ALL | 全表扫描;如果数据量大则需要进行优化 |
index | 全索引扫描这个比 ALL 的效率要好,主要有两种情况,一种是当前的查询是覆盖索引,即需要的数据在索引中就可以索取,或者是使用了索引进行排序,这样就避免数据的重排序。 |
range | 表示利用索引查询的时候限制了范围,在指定范围内进行查询,这样避免了 index 的全索引扫描,适用的操作符:= , <> , > , >= , < , <= , IS NULL , BETWEEN , LIKE , or IN() |
index_subquery | 利用索引来关联子查询,不再扫描全表 |
unique_subquery | 该连接类型类似与 index_subquery,使用的是唯一索引 |
index_merge | 在查询过程中需要多个索引组合使用 |
ref_or_null | 对于某个字段即需要关联条件,也需要 null值的情况下,查询优化器会选择这种访问方式 |
ref | 使用了非唯一性索引进行数据的查找 |
eq_ref | 使用唯一性索引进行数据查找 |
const | 这个表至多有一个匹配行 |
system | 表只有一行记录(等于系统表),这是 const 类型的特例 |
possible_keys
查询涉及到字段的索引,则这些索引都会列举出来,但是不一定采纳。
key
实际使用的索引,如果为 NULL,则没有使用索引。
key_len
表示索引中使用的字节数;查询中使用的索引长度;在不损失精度的情况下长度越短越好。
ref
显示索引的哪一列被使用了,如果可能的话,是一个常数。
rows
大致估算出找出所需记录需要读取的行数,反映了sql找了多少条数据,该值越小越好。
extra
额外信息。
extra value | meaning |
---|---|
using filesort | 使用了文件排序 |
using temporary | 建立临时表来保存中间结果,查询完成之后把临时表删除 |
using index | 采用覆盖索引,直接从索引中读取数据,而不用访问数据表。如果同时出现 using where 表明索引被用来执行索引键值的查找;如果没有,表明索引被用来读取数据,而不是真的查找 |
using index condition | 采用索引下推,减少回表次数 |
using where | 使用 where 进行条件过滤 |
using join buffer | 使用连接缓存 |
impossible where | where 语句的结果总是 false |
优化器选择过程
优化器根据解析树可能会生成多个执行计划,然后选择最优的的执行计划。
SHOW VARIABLES LIKE 'optimizer_trace';
-- 启用优化器的追踪
SET optimizer_trace='enabled=on';
-- 执行一条查询语句
SELECT * FROM information_schema.optimizer_trace;
-- 用完关闭
SET optimizer_trace="enabled=off";
SHOW VARIABLES LIKE 'optimizer_trace';
慢日志查询
慢查询日志由执行时间超过 long_query_time 秒且至少需要检查 min_examined_row_limit 行的 SQL 语句组成。慢查询日志可用于查找需要很长时间才能执行的查询,因此是优化的候选项。但是,检查较长的慢查询日志可能是一项耗时的任务。为了简化此操作,您可以使用 mysqldumpslow 命令来处理慢查询日志文件并汇总其内容。
慢查询日志参数
long_query_time的最小值和默认值分别为 0 和 10。可以将该值指定为微秒的分辨率。
缺省情况下,不记录管理语句,也不记录不使用索引进行查找的查询。可以使用log_slow_admin_statements和log_queries_not_using_indexes更改此行为,默认情况下,慢查询日志处于禁用状态。
开启
(1)查看:
SHOW GLOBAL VARIABLES LIKE 'slow_query%';
SHOW GLOBAL VARIABLES LIKE 'long_query%';
(1)设置:
SET GLOBAL slow_query_log = ON; -- on 开启 off,关闭
SET GLOBAL long_query_time = 4; -- 单位秒;默认 10s;此时设置为4s
或者修改配置:
slow_query_log = ON
long_query_time = 4
slow_query_log_file = D:/mysql/mysql57-slow.log
mysqldumpslow
查找最近10条慢查询日志:
mysqldumpslow -s t -t 10 -g 'select' D:/mysql/mysql57-slow.log
总结
当出现SQL比较慢时,需要进行如下步骤进行优化:
(1)找到SQL语句。通过show processlist和开启慢查询日志。
(2)分析SQL语句。
a. 查看where、group by、order by里面的字段是否创建了索引。
b. in 优化成联合查询,减少联合查询等。