美團點評開源 SQL 優化工具 SQLAdvisor 測試報告

1、SQLAdvisor 介紹

SQLAdvisor 是由美團點評公司北京 DBA 團隊開發維護的 SQL 優化工具:輸入 SQL,輸出索引優化建議。它基於 MySQL 原生詞法解析,再結合 SQL 中的 where 條件以及字段選擇度、聚合條件、多表 Join 關係等最終輸出最優的索引優化建議。目前 SQLAdvisor 在美團公司內部大量使用,較爲成熟、穩定。html

SQLAdvisor 的優勢mysql

  • 基於 MySQL 原生詞法解析,充分保證詞法解析的性能、準肯定以及穩定性;
  • 支持常見的 SQL(Insert/Delete/Update/Select);
  • 支持多表 Join 並自動邏輯選定驅動表;
  • 支持聚合條件 Order by 和 Group by;
  • 過濾表中已存在的索引。

 

2、SQLAdvisor 原理

SQLAdvisor 架構流程圖:c++

SQLAdvisor 包含了以下的處理方式:Join 處理、where 處理、計算區分度、添加備選索引、Group 與 Order 處理、驅動表選擇、添加被驅動表備選索引、輸出建議,具體的流程圖能夠參考:美團點評 SQL 優化工具 SQLAdvisor 開源git

 

3、SQLAdvisor 測試

3.1  SQLAdvisor 安裝

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 gcc gcc-c++

# 因 yum 安裝 Percona-Server-shared-56 失敗,故使用 rpm 包安裝,\
# 具體參考 https://github.com/Meituan-Dianping/SQLAdvisor/issues/12
yum 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
cmake -DBUILD_CONFIG=mysql_release -DCMAKE_BUILD_TYPE=debug \
-DCMAKE_INSTALL_PREFIX=/usr/local/sqlparser ./
make && make install

3.1.4 安裝 SQLAdvisor 源碼
cd sqladvisor/
cmake -DCMAKE_BUILD_TYPE=debug ./
make
cp sqladvisor /usr/local/bin
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  導入測試數據

爲了隱私考慮,線上表名屏蔽,以 tableA 和 tableB 代替。脫敏處理的表結構以下:github

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'

導入數據量以下:sql

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  執行測試

測試語句:服務器

/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

測試結果微信

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結束!

 

4、結論

美團點評開源 SQL 優化工具 SQLAdvisor 獲得的優化建議比較滿意,建議在線上試用一段時間。這個工具的成本在於須要在線上 DB 安裝相關依賴,若是確認採用,能夠考慮在初始化 DB 服務器時部署此工具。架構

原文地址:https://dbarobin.com/2017/03/16/test-report-of-sqladvisor/工具

官方地址:https://github.com/Meituan-Dianping/SQLAdvisor

爲了方便你們交流,本人開通了微信公衆號和QQ羣,QQ羣:291519319,喜歡技術的一塊兒來交流吧

相關文章
相關標籤/搜索