美团点评开源 SQL 优化工具 SQLAdvisor 测试报告

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
全局流量管理 GTM,标准版 1个月
简介:

0x00 目录


日期 作者 文档概要 版本 更新历史
2017/03/14 温国兵 美团点评开源 SQL 优化工具 SQLAdvisor 测试报告 v1.0 文档初稿

0x01 SQLAdvisor 介绍


SQLAdvisor 是由美团点评公司北京 DBA 团队开发维护的 SQL 优化工具:输入 SQL,输出索引优化建议。它基于 MySQL 原生词法解析,再结合 SQL 中的 where 条件以及字段选择度、聚合条件、多表 Join 关系等最终输出最优的索引优化建议。目前 SQLAdvisor 在美团公司内部大量使用,较为成熟、稳定。[^1]

SQLAdvisor 的优点

  • 基于 MySQL 原生词法解析,充分保证词法解析的性能、准确定以及稳定性;

  • 支持常见的 SQL(Insert/Delete/Update/Select);

  • 支持多表 Join 并自动逻辑选定驱动表;

  • 支持聚合条件 Order by 和 Group by;

  • 过滤表中已存在的索引。

0x02 SQLAdvisor 原理


SQLAdvisor 架构流程图:

SQLAdvisor Structure

SQLAdvisor 包含了如下的处理方式:Join 处理、where 处理、计算区分度、添加备选索引、Group 与 Order 处理、驱动表选择、添加被驱动表备选索引、输出建议,具体的流程图可以参考:美团点评 SQL 优化工具 SQLAdvisor 开源

0x03 SQLAdvisor 测试


3.1 SQLAdvisor 安装


参考 [^2].

3.1.1 拉取最新代码

git clone https://github.com/Meituan-Dianping/SQLAdvisor.git

3.1.2 安装依赖项

yum install -y cmake libaio-devel libffi-devel glib2 glib2-devel bison# 因 yum 安装 Percona-Server-shared-56 失败,故使用 rpm 包安装,\# 具体参考 https://github.com/Meituan-Dianping/SQLAdvisor/issues/12yum install -y --enablerepo=Percona56 Percona-Server-shared-56
yum install -y Percona-Server-server-56 Percona-Server-client-56

rpm -ivh Percona-Server-shared-56-5.6.25-rel73.1.el6.x86_64.rpm# 设置软链cd /usr/lib64/
ls -l libperconaserverclient_r.so.18
ln -s libperconaserverclient_r.so.18 libperconaserverclient_r.so

3.1.3 编译依赖项 sqlparser

1. cmake -DBUILD_CONFIG=mysql_release -DCMAKE_BUILD_TYPE=debug \-DCMAKE_INSTALL_PREFIX=/usr/local/sqlparser ./
2. make && make install

3.1.4 安装 SQLAdvisor 源码

1. cd sqladvisor/
2. cmake -DCMAKE_BUILD_TYPE=debug ./
3. make
4. cp sqladvisor /usr/local/bin
5. sqladvisor --help
Usage:
  sqladvisor [OPTION...] sqladvisor

SQL Advisor Summary

Help Options:
  -?, --help              Show help options

Application Options:
  -f, --defaults-file     sqls file
  -u, --username          username
  -p, --password          password
  -P, --port              port
  -h, --host              host
  -d, --dbname            database name
  -q, --sqls              sqls
  -v, --verbose           1:output logs 0:output nothing

3.2 导入测试数据


注:测试环境 MySQL 版本为 5.5.24-log。

为了隐私考虑,线上表名屏蔽,以 tableA 和 tableB 代替。脱敏处理的表结构如下:

CREATE TABLE `tableA` ( \
  `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, \
  `CATE` varchar(32) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `E_ID` char(18) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `RD` varchar(32) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `RU` varchar(32) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `MO` int(10) NOT NULL DEFAULT '0' COMMENT 'xxxx', \
  `LID` varchar(32) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `LEVEL` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'xxxx', \
  `GI` varchar(15) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `GT` datetime DEFAULT NULL COMMENT 'xxxx', \
  `CL` varchar(32) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `ST` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'xxxx', \
  `RES` varchar(64) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  PRIMARY KEY (`ID`), \
  UNIQUE KEY `i_e_id` (`E_ID`), \
  KEY `i_lid` (`LID`), \
  KEY `i_rd_ru_mo` (`RD`,`RU`,`MO`) \) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='tableA'CREATE TABLE `tableB` ( \
  `ID` int(11) NOT NULL AUTO_INCREMENT, \
  `LID` varchar(32) NOT NULL COMMENT 'xxxx', \
  `NAME` varchar(45) NOT NULL COMMENT 'xxxx', \
  `CL` varchar(30) NOT NULL COMMENT 'xxxx', \
  `TIME` datetime DEFAULT NULL COMMENT 'xxxx', \
  `NUM` varchar(64) NOT NULL COMMENT 'xxxx', \
  `SOUR` varchar(32) NOT NULL COMMENT 'xxxx', \
  `GI` varchar(15) NOT NULL COMMENT 'xxxx', \
  `TYPE` tinyint(4) NOT NULL COMMENT 'xxxx', \
  `SID` int(11) NOT NULL COMMENT 'xxxx', \
  `SEID` int(11) NOT NULL COMMENT 'xxxx', \
  `NAME` varchar(20) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `ADD` varchar(255) NOT NULL COMMENT 'xxxx', \
  `PO` varchar(11) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `QQ` varchar(20) NOT NULL COMMENT 'xxxx', \
  `CATE` varchar(20) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `RU` varchar(32) NOT NULL DEFAULT '' COMMENT 'xxxx', \
  `SC` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'xxxx', \
  `LEVEL` varchar(32) DEFAULT '' COMMENT 'xxxx', \
  PRIMARY KEY (`ID`), \
  KEY `i_user` (`LID`,`CATE`), \
  KEY `i_num` (`NUM`), \
  KEY `i_cl` (`CL`) \) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='tableB'

导入数据量如下:

mysql> SELECT COUNT(*) FROM tableA;+----------+| COUNT(*) |+----------+|   122658 |+----------+1 row in set (0.06 sec)mysql> SELECT COUNT(*) FROM tableB;+----------+| COUNT(*) |+----------+|   979525 |+----------+1 row in set (0.23 sec)

3.3 执行测试


测试的语句如下:

a. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT * FROM tableA WHERE LID = 'xxxx' ORDER BY GT DESC" -v 1

b. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT E_ID, LID, MO, GT FROM tableA WHERE \ST != '1' OR RES != '1' LIMIT 100" -v 1

c. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT MAX(NUM) FROM tableB WHERE LID = 'xxxx' AND \CL='xxxx'" -v 1

d. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT * FROM tableB WHERE LID = 'xxxx' AND CL \LIKE 'xxx_%' LIMIT 1" -v 1

e. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT COUNT(*) FROM tableB WHERE  LID = 'xxxx' AND \CL = 'xxxx' AND SID = '1' AND TIME >= '2017-03-07 00:07:51'" -v 1

f. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT COUNT(1) AS NUM FROM tableB WHERE \LEFT(TIME, 10) = '2017-03-07' AND LID    = 'xxxx' AND SOUR = 'xxxx'" -v 1

g. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT COUNT(1) AS NUM FROM tableB WHERE \LEFT(TIME, 10) = '2017-03-07' AND LID    = 'xxxx' AND \SOUR = 'xxxx' AND RU = '_xxxx_'" -v 1

h. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT COUNT(1) AS NUM FROM tableB WHERE \LEFT(TIME, 10) = '2017-03-07' AND LID = 'xxxx' AND \SOUR = 'xxxx' AND CL = 'xxxx'" -v 1

i. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT TIME FROM tableB WHERE LID = 'xxxx' AND \SOUR = 'xxxx' AND CL = 'xxxx'" -v 1

j. /usr/local/bin/sqladvisor -u root -p 'xxxx' -P 3306 -h xxx.xxx.xxx.xxx -d databaseA \-q "SELECT LID, TIME, NUM, CL FROM tableB \WHERE LID = 'xxxx' AND SOUR = 'xxxx'" -v 1

其中 a 语句输出信息如下:

2017-03-14 12:30:51 1923 [Note] 第1步: 对SQL解析优化之后得到的SQL:\select `*` AS `*` from `databaseA`.`tableA` where (`LID` = 'xxxx') \order by `GT` desc
2017-03-14 12:30:51 1923 [Note] 第2步:开始解析where中的条件:(`LID` = 'xxxx')2017-03-14 12:30:51 1923 [Note] show index from tableA
2017-03-14 12:30:51 1923 [Note] show table ST like 'tableA'2017-03-14 12:30:51 1923 [Note] select count(*) from ( select `LID` from `tableA` \
    FORCE INDEX( i_E_ID ) order by E_ID DESC limit 10000) `tableA` \
    where (`LID` = 'xxxx')2017-03-14 12:30:51 1923 [Note] 第3步:表tableA的行数:122879,limit行数:10000,\得到where条件中(`LID` = 'xxxx')的选择度:10000
2017-03-14 12:30:51 1923 [Note] 第4步:开始解析order by 条件
2017-03-14 12:30:51 1923 [Note] 第5步:开始验证 字段GT是不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] show index from tableA where Key_name = 'PRIMARY' \and Column_name ='GT' and Seq_in_index = 1
2017-03-14 12:30:51 1923 [Note] 第6步:字段GT不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] 第7步:开始添加order by 字段
2017-03-14 12:30:51 1923 [Note] 第8步:开始验证 字段GT是不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] show index from tableA where Key_name = 'PRIMARY' \and Column_name ='GT' and Seq_in_index = 1
2017-03-14 12:30:51 1923 [Note] 第9步:字段GT不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] 第10步:开始验证 字段LID是不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] show index from tableA where Key_name = 'PRIMARY' \and Column_name ='LID' and Seq_in_index = 1
2017-03-14 12:30:51 1923 [Note] 第11步:字段LID不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] 第12步:开始验证 字段LID是不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] show index from tableA where Key_name = 'PRIMARY' \and Column_name ='LID' and Seq_in_index = 1
2017-03-14 12:30:51 1923 [Note] 第13步:字段LID不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] 第14步:开始验证表中是否已存在相关索引。表名:tableA, \字段名:LID, 在索引中的位置:1
2017-03-14 12:30:51 1923 [Note] show index from tableA where Column_name ='LID' \and Seq_in_index =1
2017-03-14 12:30:51 1923 [Note] 第15步:开始验证 字段GT是不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] show index from tableA where Key_name = 'PRIMARY' \and Column_name ='GT' and Seq_in_index = 1
2017-03-14 12:30:51 1923 [Note] 第16步:字段GT不是主键。表名:tableA
2017-03-14 12:30:51 1923 [Note] 第17步:开始验证表中是否已存在相关索引。\表名:tableA, 字段名:GT, 在索引中的位置:2
2017-03-14 12:30:51 1923 [Note] show index from tableA where \Column_name ='GT' and Seq_in_index =2
2017-03-14 12:30:52 1923 [Note] 第18步:开始输出表tableA索引优化建议:
2017-03-14 12:30:52 1923 [Note] Create_Index_SQL:alter table tableA add index \idx_LID_GT(LID,GT)2017-03-14 12:30:52 1923 [Note] 第19步: SQLAdvisor结束!

以上 10 个语句,平均 1s 就可以得到优化结果。

其中 alter table tableA add index idx_LID_GT(LID,GT) 就是优化建议,跟人工优化得到的结果一致。

另外,tableB 得到优化建议 alter table gamedaylottery20170210_gift_log add index idx_LID_CL(LID,CL),也是比较理想的索引建议。

0x04 结论


美团点评开源 SQL 优化工具 SQLAdvisor 得到的优化建议比较满意,建议在线上试用一段时间。这个工具的成本在于需要在线上 DB 安装相关依赖,如果确认采用,可以考虑在初始化 DB 服务器时部署此工具。

0x05 参考





本文转自 liqius 51CTO博客,原文链接:http://blog.51cto.com/szgb17/1924357,如需转载请自行联系原作者
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
8天前
|
SQL 存储 缓存
如何优化SQL查询性能?
【10月更文挑战第28天】如何优化SQL查询性能?
46 10
|
7天前
|
SQL 存储 缓存
SQL Server 数据太多如何优化
11种优化方案供你参考,优化 SQL Server 数据库性能得从多个方面着手,包括硬件配置、数据库结构、查询优化、索引管理、分区分表、并行处理等。通过合理的索引、查询优化、数据分区等技术,可以在数据量增大时保持较好的性能。同时,定期进行数据库维护和清理,保证数据库高效运行。
|
9天前
|
Web App开发 定位技术 iOS开发
Playwright 是一个强大的工具,用于在各种浏览器上测试应用,并模拟真实设备如手机和平板。通过配置 `playwright.devices`,可以轻松模拟不同设备的用户代理、屏幕尺寸、视口等特性。此外,Playwright 还支持模拟地理位置、区域设置、时区、权限(如通知)和配色方案,使测试更加全面和真实。例如,可以在配置文件中设置全局的区域设置和时区,然后在特定测试中进行覆盖。同时,还可以动态更改地理位置和媒体类型,以适应不同的测试需求。
Playwright 是一个强大的工具,用于在各种浏览器上测试应用,并模拟真实设备如手机和平板。通过配置 `playwright.devices`,可以轻松模拟不同设备的用户代理、屏幕尺寸、视口等特性。此外,Playwright 还支持模拟地理位置、区域设置、时区、权限(如通知)和配色方案,使测试更加全面和真实。例如,可以在配置文件中设置全局的区域设置和时区,然后在特定测试中进行覆盖。同时,还可以动态更改地理位置和媒体类型,以适应不同的测试需求。
16 1
|
12天前
|
编解码 人工智能 自然语言处理
迈向多语言医疗大模型:大规模预训练语料、开源模型与全面基准测试
【10月更文挑战第23天】Oryx 是一种新型多模态架构,能够灵活处理各种分辨率的图像和视频数据,无需标准化。其核心创新包括任意分辨率编码和动态压缩器模块,适用于从微小图标到长时间视频的多种应用场景。Oryx 在长上下文检索和空间感知数据方面表现出色,并且已开源,为多模态研究提供了强大工具。然而,选择合适的分辨率和压缩率仍需谨慎,以平衡处理效率和识别精度。论文地址:https://www.nature.com/articles/s41467-024-52417-z
37 2
|
1月前
|
Java 流计算
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
35 1
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
|
21天前
|
SQL 资源调度 分布式计算
如何让SQL跑快一点?(优化指南)
这篇文章主要探讨了如何在阿里云MaxCompute(原ODPS)平台上对SQL任务进行优化,特别是针对大数据处理和分析场景下的性能优化。
|
24天前
|
jenkins 测试技术 持续交付
提升软件测试效率的实用技巧与工具
【10月更文挑战第12天】 本文将深入探讨如何通过优化测试流程、引入自动化工具和持续集成等策略,来显著提高软件测试的效率。我们将分享一些实用的技巧和工具,帮助测试人员更高效地发现和定位问题,确保软件质量。
41 2
|
30天前
|
SQL 数据可视化 安全
微软SQL Server可视化工具与技巧
微软SQL Server不仅提供了强大的数据库管理功能,还集成了多种可视化工具,帮助用户更直观地理解和管理数据
|
30天前
|
SQL 监控 数据库
慢SQL对数据库写入性能的影响及优化技巧
在数据库管理系统中,慢SQL(即执行缓慢的SQL语句)不仅会影响查询性能,还可能对数据库的写入性能产生显著的不利影响
|
7天前
|
NoSQL 测试技术 Go
自动化测试在 Go 开源库中的应用与实践
本文介绍了 Go 语言的自动化测试及其在 `go mongox` 库中的实践。Go 语言通过 `testing` 库和 `go test` 命令提供了简洁高效的测试框架,支持单元测试、集成测试和基准测试。`go mongox` 库通过单元测试和集成测试确保与 MongoDB 交互的正确性和稳定性,使用 Docker Compose 快速搭建测试环境。文章还探讨了表驱动测试、覆盖率检查和 Mock 工具的使用,强调了自动化测试在开源库中的重要性。
下一篇
无影云桌面