mysql 优化大师执行计划_阿里云数据库挑战赛quot;SQL优化大师quot;获奖案例

本文主要是介绍mysql 优化大师执行计划_阿里云数据库挑战赛quot;SQL优化大师quot;获奖案例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、前言

2017/07在阿里云举办的第一届“阿里云数据库挑战赛第一季“慢SQL性能优化赛”期间,我得到知数堂叶老师的鼎力相助,成功突破重围,过关斩将,获得“SQL优化大师”荣誉称号!

阿里云数据库挑战赛

第一季“SQL优化大师”

通过这次挑战赛的实践,加上中间叶老师的指导,让我增进了对SQL优化的认识。

在此,分享下我的SQL优化过程,希望能给各位提供一些SQL优化方面的思路,大家共同交流进步。

二、优化过程

1、优化前

原始SQL

select a.seller_id,a.seller_name,b.user_name,c.state

from a,b,c

where a.seller_name=b.seller_name and

b.user_id=c.user_id and

c.user_id=17 and

a.gmt_create BETWEEN DATE_ADD(NOW(), INTERVAL - 600 MINUTE)

AND DATE_ADD(NOW(), INTERVAL 600 MINUTE)

order by a.gmt_create

原始表结构

create table a(

id int auto_increment,

seller_id bigint,

seller_name varchar(100) collate utf8_bin ,

gmt_create varchar(30),

primary key(id)) character set utf8;

create table b (

id int auto_increment,

seller_name varchar(100),

user_id varchar(50),

user_name varchar(100),

sales bigint,

gmt_create varchar(30),

primary key(id)) character set utf8;

create table c (

id int auto_increment,

user_id varchar(50),

order_id varchar(100),

state bigint,

gmt_create varchar(30),

primary key(id)) character set utf8;

2、优化前的SQL执行计划

explain select a.seller_id,a.seller_name,b.user_name,c.state from a,b,c

where a.seller_name=b.seller_name and b.user_id=c.user_id

and c.user_id=17 and

a.gmt_create BETWEEN DATE_ADD(NOW(),

INTERVAL - 600 MINUTE) AND DATE_ADD(NOW(), INTERVAL 600 MINUTE)

order by a.gmt_create

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: a

partitions: NULL

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 16109

filtered: 11.11

Extra: Using where; Using temporary; Using filesort

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: b

partitions: NULL

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 16174

filtered: 100.00

Extra: Using where; Using join buffer (Block Nested Loop)

*************************** 3. row ***************************

id: 1

select_type: SIMPLE

table: c

partitions: NULL

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 359382

filtered: 1.00

Extra: Using where; Using join buffer (Block Nested Loop)

3、优化后

先看下经过优化后的终版SQL执行计划

mysql> explain select a.seller_id, a.seller_name,b.user_name,

c.state from a left join b

on (a.seller_name=b.seller_name)

left join c on (b.user_id=c.user_id)

where c.user_id='17'

and a.gmt_create BETWEEN DATE_ADD(NOW(), INTERVAL - 600 MINUTE)

AND DATE_ADD(NOW(), INTERVAL 600 MINUTE);

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: b

partitions: NULL

type: ref

possible_keys: i_seller_name,i_user_id

key: i_user_id

key_len: 3

ref: const

rows: 1

filtered: 100.00

Extra: Using where

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: c

partitions: NULL

type: ref

possible_keys: i_user_id

key: i_user_id

key_len: 3

ref: const

rows: 1

filtered: 100.00

Extra: Using index condition

*************************** 3. row ***************************

id: 1

select_type: SIMPLE

table: a

partitions: NULL

type: ref

possible_keys: i_seller_name

key: i_seller_name

key_len: 25

ref: test1.b.seller_name

rows: 1

filtered: 11.11

Extra: Using where

优化完后这个SQL毫秒级出结果(看下方profiling截图)

ecf47c429bd0350545f6bd9292408d33.png

4、优化思路

硬件&系统环境

硬盘:SSD(pcie)

内存:16G

CPU:8核

操作系统:选择Centos7系统,xfs文件系统

内核参数做些调整:

vm.swappiness = 5 #建议设置5-10

io schedule选择 deadline/noop 之一

MySQL 版本选择

推荐MySQL 5.6以上的版本,最好是MySQL 5.7。

MySQL 5.6优化器增加了ICP、MRR、BKA等特性,5.7在性能上有更多提升。

MySQL参数调整

innodb_buffer_pool_size #物理内存的50% - 70%

innodb_flush_log_at_trx_commit = 1

innodb_max_dirty_pages_pct = 50 #建议不高于50

innodb_io_capacity = 5000 #SSD盘

#大赛要求关闭QC

query_cache_size = 0

query_cache_type = 0

SQL调优过程详解

首先,我们看到原来的执行计划中3个表的查询都是全表扫描(type = ALL),所以先把关联查询字段以及WHERE条件中的字段加上索引。

1、添加索引

alter table a add index i_seller_name(seller_name);

alter table a add index i_seller_id(seller_id);

alter table b add index i_seller_name(seller_name);

alter table b add index i_user_id(user_id);

alter table c add index i_user_id(user_id);

alter table c add index i_state(state);

添加完索引后,再看下新的执行计划:

explain select a.seller_id,

a.seller_name,b.user_name ,c.state from a

left join b on (a.seller_name=b.seller_name)

left join c on( b.user_id=c.user_id ) where c.user_id='17'

and a.gmt_create BETWEEN DATE_ADD(NOW(),

INTERVAL - 600 MINUTE) AND

DATE_ADD(NOW(), INTERVAL 600 MINUTE)\G

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: b

partitions: NULL

type: ref

possible_keys: i_user_id

key: i_user_id

key_len: 53

ref: const

rows: 1

filtered: 100.00

Extra: NULL

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: c

partitions: NULL

type: ref

possible_keys: i_user_id

key: i_user_id

key_len: 53

ref: const

rows: 1

filtered: 100.00

Extra: NULL

*************************** 3. row ***************************

id: 1

select_type: SIMPLE

table: a

partitions: NULL

type: ref

possible_keys: i_seller_name

key: i_seller_name

key_len: 303

ref: func

rows: 947

filtered: 11.11

Extra: Using index condition; Using where

我们注意到执行计划中3个表的key_len列都太大了,最小也有53字节,最大303字节,要不要这么夸张啊~

2、修改字符集、修改字段数据类型

默认字符集是utf8(每个字符最多占3个字节),因为该表并不存储中文,因此只需要用latin1字符集(最大占1个字节)。

除此外,我们检查3个表的字段数据类型,发现有些varchar(100)的列实际最大长度并没这么大,有些实际存储datetime数据的却采用varchar(30)类型,有些用bigint/int就足够的也采用varchar类型,真是醉了。于是分别把这些数据类型改为更合适的类型。

修改表字符集和调整各个列数据类型很重要的作用是可以减小索引的key_len,从而减少关联的字段的字节,减少内存消耗。

优化后的表结构

CREATE TABLE `a` (

`id` int NOT NULL AUTO_INCREMENT,

`seller_id` int(6) DEFAULT NULL,

`seller_name` char(8) DEFAULT NULL,

`gmt_create` datetime DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `i_seller_id` (`seller_id`),

KEY `i_seller_name` (`seller_name`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1

CREATE TABLE `b` (

`id` int NOT NULL AUTO_INCREMENT,

`seller_name` char(8) DEFAULT NULL,

`user_id` smallint(5) DEFAULT NULL,

`user_name` char(10) DEFAULT NULL,

`sales` int(11) DEFAULT NULL,

`gmt_create` datetime DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `i_seller_name` (`seller_name`),

KEY `i_user_id` (`user_id`),

KEY `i_user_name` (`user_name`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1

CREATE TABLE `c` (

`id` int NOT NULL AUTO_INCREMENT,

`user_id` smallint(5) DEFAULT NULL,

`order_id` char(10) DEFAULT NULL,

`state` int(11) DEFAULT NULL,

`gmt_create` datetime DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `i_user_id` (`user_id`),

KEY `i_state` (`state`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1

以上是我在阿里云数据库挑战赛中的获奖案例,感谢在比赛过程中叶老师对我的提点和帮助,同时非常感谢知数堂教授SQL优化技能!

最后,我想说的是,只要掌握SQL优化的几个常规套路,你也可以完成绝大多数的SQL优化工作滴!

附录:3个表数据初始化

insert into a (seller_id,seller_name,gmt_create) values (100000,'uniqla','2017-01-01');

insert into a (seller_id,seller_name,gmt_create) values (100001,'uniqlb','2017-02-01');

insert into a (seller_id,seller_name,gmt_create) values (100002,'uniqlc','2017-03-01');

insert into a (seller_id,seller_name,gmt_create) values (100003,'uniqld','2017-04-01');

...重复N次写入

insert into b (seller_name,user_id,user_name,sales,gmt_create) values ('niqla','1','a',1,now());

insert into b (seller_name,user_id,user_name,sales,gmt_create) values ('niqlb','2','b',3,now());

insert into b (seller_name,user_id,user_name,sales,gmt_create) values ('niqlc','3','c',1,now());

insert into b (seller_name,user_id,user_name,sales,gmt_create) values ('niqld','4','d',4,now());

...重复N次写入

insert into c (user_id,order_id,state,gmt_create) values( 21,1,0 ,now() );

insert into c (user_id,order_id,state,gmt_create) values( 22,2,0 ,now() );

insert into c (user_id,order_id,state,gmt_create) values( 33,3,0 ,now() );

insert into c (user_id,order_id,state,gmt_create) values( 43,4,0 ,now() );

...重复N次写入

原文发布时间为:2017-09-30

本文作者:田帅萌

本文来自云栖社区合作伙伴“老叶茶馆”,了解相关信息可以关注“老叶茶馆”微信公众号

这篇关于mysql 优化大师执行计划_阿里云数据库挑战赛quot;SQL优化大师quot;获奖案例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/716407

相关文章

mysql索引四(组合索引)

单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引;组合索引,即一个索引包含多个列。 因为有事,下面内容全部转自:https://www.cnblogs.com/farmer-cabbage/p/5793589.html 为了形象地对比单列索引和组合索引,为表添加多个字段:    CREATE TABLE mytable( ID INT NOT NULL, use

mysql索引三(全文索引)

前面分别介绍了mysql索引一(普通索引)、mysql索引二(唯一索引)。 本文学习mysql全文索引。 全文索引(也称全文检索)是目前搜索引擎使用的一种关键技术。它能够利用【分词技术】等多种算法智能分析出文本文字中关键词的频率和重要性,然后按照一定的算法规则智能地筛选出我们想要的搜索结果。 在MySql中,创建全文索引相对比较简单。例如:我们有一个文章表(article),其中有主键ID(

mysql索引二(唯一索引)

前文中介绍了MySQL中普通索引用法,和没有索引的区别。mysql索引一(普通索引) 下面学习一下唯一索引。 创建唯一索引的目的不是为了提高访问速度,而只是为了避免数据出现重复。唯一索引可以有多个但索引列的值必须唯一,索引列的值允许有空值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该使用关键字UNIQUE,把它定义为一个唯一索引。 添加数据库唯一索引的几种

mysql索引一(普通索引)

mysql的索引分为两大类,聚簇索引、非聚簇索引。聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引则不同。聚簇索引能够提高多行检索的速度、非聚簇索引则对单行检索的速度很快。         在这两大类的索引类型下,还可以降索引分为4个小类型:         1,普通索引:最基本的索引,没有任何限制,是我们经常使用到的索引。         2,唯一索引:与普通索引

关于如何更好管理好数据库的一点思考

本文尝试从数据库设计理论、ER图简介、性能优化、避免过度设计及权限管理方面进行思考阐述。 一、数据库范式 以下通过详细的示例说明数据库范式的概念,将逐步规范化一个例子,逐级说明每个范式的要求和变换过程。 示例:学生课程登记系统 初始表格如下: 学生ID学生姓名课程ID课程名称教师教师办公室1张三101数学王老师101室2李四102英语李老师102室3王五101数学王老师101室4赵六103物理陈

数据库期末复习知识点

A卷 1. 选择题(30') 2. 判断范式(10') 判断到第三范式 3. 程序填空(20') 4. 分析填空(15') 5. 写SQL(25') 5'一题 恶性 B卷 1. 单选(30') 2. 填空 (20') 3. 程序填空(20') 4. 写SQL(30') 知识点 第一章 数据库管理系统(DBMS)  主要功能 数据定义功能 (DDL, 数据定义语

【服务器运维】MySQL数据存储至数据盘

查看磁盘及分区 [root@MySQL tmp]# fdisk -lDisk /dev/sda: 21.5 GB, 21474836480 bytes255 heads, 63 sectors/track, 2610 cylindersUnits = cylinders of 16065 * 512 = 8225280 bytesSector size (logical/physical)

给数据库的表添加字段

周五有一个需求是这样的: 原来数据库有一个表B,现在需要添加一个字段C,我把代码中增删改查部分进行了修改, 比如insert中也添入了字段C。 但没有考虑到一个问题,数据库的兼容性。因为之前的版本已经投入使用了,再升级的话,需要进行兼容处理,当时脑子都蒙了,转不过来,后来同事解决了这个问题。 现在想想,思路就是,把数据库的表结构存入文件中,如xxx.sql 实时更新该文件: CREAT

SQL Server中,查询数据库中有多少个表,以及数据库其余类型数据统计查询

sqlserver查询数据库中有多少个表 sql server 数表:select count(1) from sysobjects where xtype='U'数视图:select count(1) from sysobjects where xtype='V'数存储过程select count(1) from sysobjects where xtype='P' SE

SQL Server中,always on服务器的相关操作

在SQL Server中,建立了always on服务,可用于数据库的同步备份,当数据库出现问题后,always on服务会自动切换主从服务器。 例如192.168.1.10为主服务器,12为从服务器,当主服务器出现问题后,always on自动将主服务器切换为12,保证数据库正常访问。 对于always on服务器有如下操作: 1、切换主从服务器:假如需要手动切换主从服务器时(如果两个服务