阿里云数据库挑战赛"SQL优化大师"获奖案例

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 分享下我的SQL优化过程,希望能给各位提供一些SQL优化方面的思路,大家共同交流进步。

一、前言

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截图)


image


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
本文作者:田帅萌
本文来自云栖社区合作伙伴“老叶茶馆”,了解相关信息可以关注“老叶茶馆”微信公众号

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
2月前
|
存储 关系型数据库 分布式数据库
喜报|阿里云PolarDB数据库(分布式版)荣获国内首台(套)产品奖项
阿里云PolarDB数据库管理软件(分布式版)荣获「2024年度国内首版次软件」称号,并跻身《2024年度浙江省首台(套)推广应用典型案例》。
|
3月前
|
关系型数据库 分布式数据库 数据库
再获殊荣,阿里云PolarDB数据库蝉联SIGMOD最佳论文奖
内存池化技术新突破,阿里云PolarDB蝉联SIGMOD最佳论文奖
|
4月前
|
Cloud Native 关系型数据库 分布式数据库
阿里云PolarDB与沃趣科技携手打造一体化数据库解决方案,助推国产数据库生态发展
阿里云瑶池数据库与沃趣科技将继续深化合作,共同推动国产数据库技术的持续创新与广泛应用,为行业生态的繁荣注入更强劲的技术动力。
阿里云PolarDB与沃趣科技携手打造一体化数据库解决方案,助推国产数据库生态发展
|
2月前
|
SQL 人工智能 数据管理
阿里云瑶池数据库 Data Agent for Meta 正式发布,让 AI 更懂你的业务!
阿里云瑶池数据库推出 Data Agent for Meta,通过智能体技术实现数据管理自主化与智能化,解决 AI Agent 在企业落地中的“看不懂、找不到、不敢动”数据难题。它以业务语义理解为核心,提供资产盘点、语义搜索等功能,助力企业释放AI生产力,推动数据治理向智能决策升级。
|
2月前
|
关系型数据库 分布式数据库 数据库
阿里云PolarDB数据库蝉联SIGMOD最佳论文奖
阿里云PolarDB凭借全球首创基于CXL Switch的分布式内存池技术,在SIGMOD 2025上荣获工业赛道“最佳论文奖”,连续两年蝉联该顶会最高奖项。其创新架构PolarCXLMem打破传统RDMA技术瓶颈,性能提升2.1倍,并已落地应用于内存池化场景,推动大模型推理与多模态存储发展,展现CXL Switch在高速互联中的巨大潜力。
阿里云PolarDB数据库蝉联SIGMOD最佳论文奖
|
3月前
|
Cloud Native 关系型数据库 分布式数据库
客户说|知乎基于阿里云PolarDB,实现最大数据库集群云原生升级
近日,知乎最大的风控业务数据库集群,基于阿里云瑶池数据库完成了云原生技术架构的升级。此次升级不仅显著提升了系统的高可用性和性能上限,还大幅降低了底层资源成本。
|
4月前
|
人工智能 关系型数据库 分布式数据库
媒体声音|从亚太到欧美,阿里云瑶池数据库凭何成为中企出海的技术底气?
在中企出海的时代浪潮中,瑶池数据库正凭借其技术创新、场景化解决方案、智能化能力、全球化布局,成为企业跨越挑战、构建全球竞争力的关键伙伴;同时也以硬核的技术实力证明了中国数据库的国际竞争力。
|
4月前
|
安全 Apache 数据库
【倒计时3天】NineData x Apache Doris x 阿里云联合举办数据库技术Meetup,5月24日深圳见!
5月24日,NineData联合Apache Doris与阿里云在深圳举办数据库技术Meetup。活动聚焦「数据实时分析」与「数据同步迁移」两大领域,邀请行业专家分享技术趋势、产品实践及解决方案,助力企业构建高效安全的数据管理体系。时间:14:00-17:30;地点:深圳新一代产业园2栋20楼会议室。线下名额有限(80人),速报名参与深度交流!
89 1
|
4月前
|
SQL 关系型数据库 MySQL
阿里云《快速连接云数据库RDS》训练营,火热开营中!
快速连接云数据库 RDS 训练营开营啦!从 0 到 1 学习实战技能,涵盖 RDS MySQL 快速连接、DMS 数据管理及 SQL 实战案例。完成任务赢取专业飞盘、积木等好礼(限量 100 份)

热门文章

最新文章