mysql 优化

mysql query optimizer

mysql中有专门负责优化 select 语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为窗户端请求的query提供它认为最优的执行计划(不一定是DBA认为最优的,这部分最耗费时间)

当客户端向mysql发送一条query,命令解析器模块完成请求分类,区别出是select并转发给 mysql query optimizer时,mysql query optimizer首先会对整条query进行优化,处理掉一些常量表达式的预算,直接换算成常量值。并对query中的查询条件进行简化和调整,如去掉一些无用或显而易见的条件、结构调整等。然后分析query中的hint信息(如果有),看显示hint信息是否可以完全确定该query的执行计划。如果没有hint或hint信息还不足以完全确定执行计划,则会读取所涉及对象的统计信息,根据query进行写相应的计算分析,然后再得出最后的执行计划。

mysql 常见瓶颈

  1. CPU : CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据的时候
  2. IO : 磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
  3. 服务器硬件的性能瓶颈 : top,free,iostat和vmstat来查看系统的性能状态

explain

explain可以模拟优化器执行sql查询语句,从而知道mysql是如何处理sql语句的,分析查询或表结构的性能瓶颈

可以分析:

  1. 表的读取顺序
  2. 数据读取操作的操作类型
  3. 哪些索引可以使用
  4. 哪些索引被实际使用
  5. 表之间的引用
  6. 每张表有多少行被优化器查询

测试sql

-- 用户表
CREATE TABLE `user_info` (
  `id`   BIGINT(20)  NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL DEFAULT '',
  `age`  INT(11)              DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name_index` (`name`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

INSERT INTO user_info (name, age) VALUES ('xys', 20);
INSERT INTO user_info (name, age) VALUES ('a', 21);
INSERT INTO user_info (name, age) VALUES ('b', 23);
INSERT INTO user_info (name, age) VALUES ('c', 50);
INSERT INTO user_info (name, age) VALUES ('d', 15);
INSERT INTO user_info (name, age) VALUES ('e', 20);
INSERT INTO user_info (name, age) VALUES ('f', 21);
INSERT INTO user_info (name, age) VALUES ('g', 23);
INSERT INTO user_info (name, age) VALUES ('h', 50);
INSERT INTO user_info (name, age) VALUES ('i', 15);

-- 收货地址表
create table user_address(
	id bigint(20) not null auto_increment,
	user_id bigint(20),
	address varchar(50) not null default '',
	default_add int default 0,
	primary key (id)
)ENGINE = InnoDB DEFAULT CHARSET = utf8;

insert into user_address (user_id,address,default_add) values(1,'上海',1);
insert into user_address (user_id,address,default_add) values(1,'北京',0);
insert into user_address (user_id,address,default_add) values(1,'杭州',0);
insert into user_address (user_id,address,default_add) values(2,'上海',1);
insert into user_address (user_id,address,default_add) values(2,'郑州',0);

-- 商品表
create table goods(
	id bigint(20) not null auto_increment,
	price int not null default 0,
	gname varchar(50) not null default '',
	primary key (id)
)ENGINE = InnoDB DEFAULT CHARSET = utf8;

insert into goods(price,gname) values(23,'钢笔');
insert into goods(price,gname) values(14,'笔记本');
insert into goods(price,gname) values(2300,'电子琴');
insert into goods(price,gname) values(350,'足球');
insert into goods(price,gname) values(135,'电饭锅');

-- 订单表
CREATE TABLE `order_info` (
  `id`           BIGINT(20)  NOT NULL AUTO_INCREMENT,
  `user_id`      BIGINT(20)           DEFAULT NULL,
  goods_id bigint(20) default null,
	address_id bigint(20) default null,
  PRIMARY KEY (`id`)
)ENGINE = InnoDB DEFAULT CHARSET = utf8;

insert into order_info(user_id,goods_id,address_id) values(1,1,1);
insert into order_info(user_id,goods_id,address_id) values(1,2,2);
insert into order_info(user_id,goods_id,address_id) values(1,2,3);
insert into order_info(user_id,goods_id,address_id) values(2,4,4);
insert into order_info(user_id,goods_id,address_id) values(2,4,4);
insert into order_info(user_id,goods_id,address_id) values(2,5,5);

使用方法

explain 后面跟需要分析的 sql 语句

mysql> select * from user_info;
+----+------+------+
| id | name | age  |
+----+------+------+
|  1 | xys  |   20 |
|  2 | a    |   21 |
|  3 | b    |   23 |
|  4 | c    |   50 |
|  5 | d    |   15 |
|  6 | e    |   20 |
|  7 | f    |   21 |
|  8 | g    |   23 |
|  9 | h    |   50 |
| 10 | i    |   15 |
+----+------+------+
10 rows in set (0.00 sec)

mysql> explain select * from user_info;
+----+-------------+-----------+------+---------------+------+---------+------+------+-------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+------+-------+
|  1 | SIMPLE      | user_info | ALL  | NULL          | NULL | NULL    | NULL |   10 | NULL  |
+----+-------------+-----------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)

mysql> 

id

select 查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序

三种情况

  1. id相同,执行顺序由上至下
explain
select
	u. name as '用户名',
	ua.address as '收货地址',
	g.gname as '商品名称'
from
	user_info u
left join user_address ua on u.id = ua.user_id
left join order_info o on ua.id = o.address_id
left join goods g on o.goods_id = g.id
where
	u.id = 1;
+--------+----------+----------+
| 用户名 | 收货地址 | 商品名称 |
+--------+----------+----------+
| xys    | 上海     | 钢笔     |
| xys    | 北京     | 笔记本   |
| xys    | 杭州     | 笔记本   |
+--------+----------+----------+
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref              | rows | filtered | Extra                                              |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | u     | NULL       | const  | PRIMARY       | PRIMARY | 8       | const            |    1 |      100 | NULL                                               |
|  1 | SIMPLE      | ua    | NULL       | ALL    | NULL          | NULL    | NULL    | NULL             |    5 |      100 | Using where                                        |
|  1 | SIMPLE      | o     | NULL       | ALL    | NULL          | NULL    | NULL    | NULL             |    6 |      100 | Using where; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | g     | NULL       | eq_ref | PRIMARY       | PRIMARY | 8       | etest.o.goods_id |    1 |      100 | NULL                                               |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------+------+----------+----------------------------------------------------+
  1. id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
    在这里插入图片描述
  2. id相同不同,同时存在

id如果相同,可以认为是一组,从上往下顺序执行
在所有主中,id只越大,优先级越高,越先执行

在这里插入图片描述

select_type

参考:https://blog.csdn.net/guangzixin/article/details/8200296
主要用于区分查询的类型,如:普通查询、联合查询、子查询等复杂查询

  1. SIMPLE
    简单的SELECT,不实用UNION或者子查询
explain select * from user where uid=1;

在这里插入图片描述

  1. PRIMARY
    表示主查询,或者是最外面的查询语句
explain select * from (select * from user where uid=1)b

在这里插入图片描述
3. UNION
第二层,在SELECT之后使用了UNION

explain select * from user where uid=1 union select * from user where uid=2

在这里插入图片描述
4. DEPENDEN UNION
UNION语句中的第二个SELECT,依赖于外部子查询

explain select * from user x where uid in (select uid from user y union select uid from user z where uid<5)

在这里插入图片描述
5. UNION RESULT
UNION的结果

explain select * from user where uid=1 union select * from user where uid=2

在这里插入图片描述
6. SUBQUERY
子查询中的第一个SELECT

explain select * from groups where gid =(select gid from user where uid=1)

在这里插入图片描述
7. DEPENDENT SUBQUERY
子查询中的第一个SELECT,取决于外面的查询

explain select * fromuser where uid in (select uid from user where uid<4)

在这里插入图片描述
8. DERIVED
导出表的SELECT(FROM子句的子查询)

explain select * from (select * from user where uid=1)b

在这里插入图片描述

table

表名

type

参考:https://blog.csdn.net/it_0101/article/details/77868939
表示表的连接类型
八种情况【5.7有14种】?
在这里插入图片描述
从最好到最差依次是:
system>const>eq_ref>ref>range>index>All

  1. system
    表示只有一行记录(等于系统表),这是const类型的特例,平时不会出现,也可以忽略不计

  2. const
    如sql语句“WHERE user_id>=125 and sid = 377 and compact_state >= 1”当where条件中只要某一单一条件使用了主键(默认唯一索引)或者自建立的唯一索引并且参数在索引中有值(如sid=111,表中必须有sid为111的记录,若没有这么一条记录,Extra列会出现“no matching row in const table”,type为null表示为找到记录,如下图),则type为const.
    在这里插入图片描述
    优化点:若字段是数字 int bigint double 等数据类型,参数尽量不要加引号,而若是char varchar 等字符串类型,最好加上引号,如下两图对比。

其中compact_code为varchar类型,且建立了一个unique类型索引,图1可以看出,正常加上引号后,type为const(常量,效率最佳),并且用到了建立的unique索引idx_code,rows为1(代表预估算需要扫描多少条记录,这个估算并不准确);

图2,去掉引号后,type为all(全表扫描,效率最差),且并未用到索引,rows为135(代表估算需要扫描135条记录)
在这里插入图片描述
3. eq_ref
如图:其中a.sid为a表的主键,b表为a表的附表,compact_id对应a.sid,这种连表查询时,即先以主键a.sid查找a表记录,找到索引树上第一个值后,再去扫描b表全部,再接着第二遍…,所以a表为eq_reg(比const差一点(const是去直接找到那一条),比ref好一点(ref为普通索引,找到第一条后还要继续找)),b表为all(全表扫描)。
在这里插入图片描述

  1. ref
    使用到普通索引(非unique)查询非范围时出现,如compact_code = “123”,compact_code为普通索引。
    在这里插入图片描述
  2. range
    如sql语句“where 字段1 >= “a” and 字段2 >= 281 and 字段3 >= “1””,只要其中一个字段使用了索引(不管是unique、normal、full text)或者主键,查询某一范围的数据时出现,(当然,这里排除了const的情况,即满足不了const出现的条件)
    在这里插入图片描述
  3. index
    情况1、只查询索引列时出现(因为索引是已经排序好的,第二图已说明,实际数据并未排序好),不管索引是什么索引
    在这里插入图片描述
    情况2、order by 主键(只能是主键,即使是其他unique索引也不行,第二图已说明)
    在这里插入图片描述
  4. All
    全表扫描,从头到尾扫描整张表查找行,效率最差

possible_keys

key

key_len

ref

rows

Extra

done

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值