成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SQL解析在美團點評中是如何應用的?

數據庫 MySQL
如何更多的依賴技術手段,來提高效率,越來越受到重視。業界已有不少基于MySQL源碼開發的SQL審核、優化建議等工具,極大的減輕了DBA的SQL審核負擔。那么我們能否繼續擴展MySQL的源碼,來輔助DBA和研發人員來進一步提高效率呢?

導讀

數據庫作為核心的基礎組件,是需要重點保護的對象。任何一個線上的不慎操作,都有可能給數據庫帶來嚴重的故障,從而給業務造成巨大的損失。為了避免這種損失,一般會在管理上下功夫。比如為研發人員制定數據庫開發規范;新上線的SQL,需要DBA進行審核;維護操作需要經過領導審批等等。而且如果希望能夠有效地管理這些措施,需要有效的數據庫培訓,還需要DBA細心的進行SQL審核。很多中小型創業公司,可以通過設定規范、進行培訓、完善審核流程來管理數據庫。

隨著美團點評的業務不斷發展和壯大,上述措施的實施成本越來越高。如何更多的依賴技術手段,來提高效率,越來越受到重視。業界已有不少基于MySQL源碼開發的SQL審核、優化建議等工具,極大的減輕了DBA的SQL審核負擔。那么我們能否繼續擴展MySQL的源碼,來輔助DBA和研發人員來進一步提高效率呢?比如,更全面的SQL優化功能;多維度的慢查詢分析;輔助故障分析等。要實現上述功能,其中最核心的技術之一就是SQL解析。

現狀與場景

SQL解析是一項復雜的技術,一般都是由數據庫廠商來掌握,當然也有公司專門提供SQL解析的API。由于這幾年MySQL數據庫中間件的興起,需要支持讀寫分離、分庫分表等功能,就必須從SQL中抽出表名、庫名以及相關字段的值。因此像Java語言編寫的Druid,C語言編寫的MaxScale,Go語言編寫的Kingshard等,都會對SQL進行部分解析。而真正把SQL解析技術用于數據庫維護的產品較少,主要有如下幾個:

  • 美團點評開源的SQLAdvisor。它基于MySQL原生態詞法解析,結合分析SQL中的where條件、聚合條件、多表Join關系給出索引優化建議。
  • 去哪兒開源的Inception。側重于根據內置的規則,對SQL進行審核。
  • 阿里的Cloud DBA。根據官方文檔介紹,其也是提供SQL優化建議和改寫。

上述產品都有非常合適的應用場景,在業界也被廣泛使用。但是SQL解析的應用場景遠遠沒有被充分發掘,比如:

  • 基于表粒度的慢查詢報表。比如,一個Schema中包含了屬于不同業務線的數據表,那么從業務線的角度來說,其希望提供表粒度的慢查詢報表。
  • 生成SQL特征。將SQL語句中的值替換成問號,方便SQL歸類。雖然可以使用正則表達式實現相同的功能,但是其Bug較多,可以參考pt-query-digest。比如pt-query-digest中,會把遇到的數字都替換成“?”,導致無法區別不同數字后綴的表。
  • 高危操作確認與規避。比如,DBA不小心Drop數據表,而此類操作,目前還無有效的工具進行回滾,尤其是大表,其后果將是災難性的。
  • SQL合法性判斷。為了安全、審計、控制等方面的原因,美團點評不會讓研發人員直接操作數據庫,而是提供RDS服務。尤其是對于數據變更,需要研發人員的上級主管進行業務上的審批。如果研發人員,寫了一條語法錯誤的SQL,而RDS無法判斷該SQL是否合法,就會造成不必要的溝通成本。

因此為了讓所有有需要的業務都能方便的使用SQL解析功能,我們認為應該具有如下特性:

  • 直接暴露SQL解析接口,使用盡量簡單。比如,輸入SQL,則輸出表名、特征和優化建議。
  • 接口的使用不依賴于特定的語言,否則維護和使用的代價太高。比如,以HTTP等方式提供服務。
  • 千里之行,始于足下,下面我先介紹下SQL的解析原理。

原理

SQL解析與優化是屬于編譯器范疇,和C等其他語言的解析沒有本質的區別。其中分為,詞法分析、語法和語義分析、優化、執行代碼生成。對應到MySQL的部分,如下圖: 

SQL解析在美團點評中是如何應用的?

圖1 SQL解析原理

詞法分析

SQL解析由詞法分析和語法/語義分析兩個部分組成。詞法分析主要是把輸入轉化成一個個Token。其中Token中包含Keyword(也稱symbol)和非Keyword。例如,SQL語句 select username from userinfo,在分析之后,會得到4個Token,其中有2個Keyword,分別為select和from:通常情況下,詞法分析可以使用Flex來生成,但是MySQL并未使用該工具,而是手寫了詞法分析部分(據說是為了效率和靈活性,參考此文)。具體代碼在sql/lex.h和sql/sql_lex.cc文件中。 

MySQL中的Keyword定義在sql/lex.h中,如下為部分Keyword:

{ "&&",               SYM(AND_AND_SYM)},

{ "<",                SYM(LT)},

{ "<=",               SYM(LE)},

{ "<>",               SYM(NE)},

{ "!=",               SYM(NE)},

{ "=",                SYM(EQ)},

{ ">",                SYM(GT_SYM)},

{ ">=",               SYM(GE)},

{ "<<",               SYM(SHIFT_LEFT)},

{ ">>",               SYM(SHIFT_RIGHT)},

{ "<=>",              SYM(EQUAL_SYM)},

{ "ACCESSIBLE",       SYM(ACCESSIBLE_SYM)},

{ "ACTION",           SYM(ACTION)},

{ "ADD",              SYM(ADD)},

{ "AFTER",            SYM(AFTER_SYM)},

{ "AGAINST",          SYM(AGAINST)},

{ "AGGREGATE",        SYM(AGGREGATE_SYM)},

{ "ALL",              SYM(ALL)},

詞法分析的核心代碼在sql/sql_lex.c文件中的,MySQLLex→lex_one_Token,有興趣的同學可以下載源碼研究。

語法分析

語法分析就是生成語法樹的過程。這是整個解析過程中最精華,最復雜的部分,不過這部分MySQL使用了Bison來完成。即使如此,如何設計合適的數據結構以及相關算法,去存儲和遍歷所有的信息,也是值得在這里研究的。

a)語法分析樹

SQL語句: 

  1. select username, ismale from userinfo where age > 20 and level > 5 and 1 = 1 

會生成如下語法樹: 

圖2 語法樹

對于未接觸過編譯器實現的同學,肯定會好奇如何才能生成這樣的語法樹。其背后的原理都是編譯器的范疇,可以參考維基百科的一篇文章,以及該鏈接中的參考書籍。本人也是在學習MySQL源碼過程中,閱讀了部分內容。由于編譯器涉及的內容過多,本人經歷和時間有限,不做過多探究。從工程的角度來說,學會如何使用Bison去構建語法樹,來解決實際問題,對我們的工作也許有更大幫助。下面我就以Bison為基礎,探討該過程。

b)MySQL語法分析樹生成過程

全部的源碼在sql/sql_yacc.yy中,在MySQL5.6中有17K行左右代碼。這里列出涉及到SQL: 

  1. select username, ismale from userinfo where age > 20 and level > 5 and 1 = 1 

解析過程的部分代碼摘錄出來。其實有了Bison之后,SQL解析的難度也沒有想象的那么大。特別是這里給出了解析的脈絡之后。 

  1. select /*select語句入口*/:  
  2.           select_init  
  3.           {  
  4.             LEX *lex= Lex;  
  5.             lex->sql_command= SQLCOM_SELECT;  
  6.           }  
  7.         ;  
  8. select_init:  
  9.           SELECT_SYM /*select 關鍵字*/ select_init2  
  10.  
  11.         | '(' select_paren ')' union_opt  
  12.  
  13.         ;  
  14.  
  15.  
  16. select_init2:  
  17.           select_part2  
  18.           { 
  19.  
  20.             LEX *lex= Lex;  
  21.             SELECT_LEX * sel= lex->current_select;  
  22.             if (lex->current_select->set_braces(0))  
  23.             {  
  24.               my_parse_error(ER(ER_SYNTAX_ERROR));  
  25.               MYSQL_YYABORT;  
  26.             }  
  27.             if (sel->linkage == UNION_TYPE &&  
  28.                 sel->master_unit()->first_select()->braces)  
  29.             {  
  30.               my_parse_error(ER(ER_SYNTAX_ERROR));  
  31.               MYSQL_YYABORT;  
  32.             }  
  33.           }  
  34.           union_clause  
  35.         ; 
  36.  
  37. select_part2:  
  38.           {  
  39.             LEX *lex= Lex;  
  40.             SELECT_LEX *sel= lex->current_select;  
  41.             if (sel->linkage != UNION_TYPE)  
  42.               mysql_init_select(lex);  
  43.             lex->current_select->parsing_place= SELECT_LIST;  
  44.           }  
  45.  
  46.           select_options select_item_list /*解析列名*/  
  47.           {  
  48.             Select->parsing_place= NO_MATTER;  
  49.           }  
  50.           select_into select_lock_type  
  51.         ;  
  52.  
  53. select_into:  
  54.           opt_order_clause opt_limit_clause {}  
  55.         | into  
  56.         | select_from /*from 字句*/  
  57.         | into select_from  
  58.         | select_from into  
  59.         ;  
  60. select_from:  
  61.           FROM join_table_list /*解析表名*/ where_clause /*where字句*/ group_clause having_clause  
  62.           opt_order_clause opt_limit_clause procedure_analyse_clause  
  63.           {  
  64.             Select->context.table_list=  
  65.               Select->context.first_name_resolution_table=  
  66.                 Select->table_list.first 
  67.           }  
  68.         | FROM DUAL_SYM where_clause opt_limit_clause  
  69.           /* oracle compatibility: oracle always requires FROM clause,  
  70.              and DUAL is system table without fields.  
  71.              Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ?  
  72.           Hmmm :) */  
  73.         ;  
  74.  
  75. where_clause:  
  76.           /* empty */  { Select->where= 0; }  
  77.         | WHERE  
  78.           {  
  79.             Select->parsing_place= IN_WHERE;  
  80.           }  
  81.           expr /*各種表達式*/  
  82.           {  
  83.             SELECT_LEX *selectSelect 
  84.             select->where= $3;  
  85.             select->parsing_place= NO_MATTER;  
  86.             if ($3)  
  87.               $3->top_level_item();  
  88.           }  
  89.         ;  
  90.  
  91. /* all possible expressions */  
  92. expr:  
  93.            | expr and expr %prec AND_SYM  
  94.           {  
  95.             /* See comments in rule expr: expr or expr */  
  96.             Item_cond_and *item1;  
  97.             Item_cond_and *item3;  
  98.             if (is_cond_and($1))  
  99.             {  
  100.               item1= (Item_cond_and*) $1;  
  101.               if (is_cond_and($3))  
  102.               {  
  103.                 item3= (Item_cond_and*) $3;  
  104.                 /*  
  105.                   (X1 AND X2) AND (Y1 AND Y2) ==> AND (X1, X2, Y1, Y2)  
  106.                 */ 
  107.                  item3->add_at_head(item1->argument_list());  
  108.                 $$ = $3;  
  109.               }  
  110.               else  
  111.               {  
  112.                 /*  
  113.                   (X1 AND X2) AND Y ==> AND (X1, X2, Y)  
  114.                 */  
  115.                 item1->add($3);  
  116.                 $$ = $1;  
  117.               }  
  118.             }  
  119.             else if (is_cond_and($3))  
  120.             {  
  121.               item3= (Item_cond_and*) $3;  
  122.               /*  
  123.                 X AND (Y1 AND Y2) ==> AND (X, Y1, Y2)  
  124.               */  
  125.               item3->add_at_head($1);  
  126.               $$ = $3;  
  127.             }  
  128.             else  
  129.             {  
  130.               /* X AND Y */  
  131.               $$ = new (YYTHD->mem_root) Item_cond_and($1, $3);  
  132.               if ($$ == NULL 
  133.                 MYSQL_YYABORT;  
  134.             }  
  135.           }  

在大家瀏覽上述代碼的過程,會發現Bison中嵌入了C++的代碼。通過C++代碼,把解析到的信息存儲到相關對象中。例如表信息會存儲到TABLE_LIST中,order_list存儲order by子句里的信息,where字句存儲在Item中。有了這些信息,再輔助以相應的算法就可以對SQL進行更進一步的處理了。

c)核心數據結構及其關系

在SQL解析中,最核心的結構是SELECT_LEX,其定義在sql/sql_lex.h中。下面僅列出與上述例子相關的部分。 

圖3 SQL解析樹結構

上面圖示中,列名username、ismale存儲在item_list中,表名存儲在table_list中,條件存儲在where中。其中以where條件中的Item層次結構最深,表達也較為復雜,如下圖所示: 

圖4 where條件

SQL解析的應用

為了更深入的了解SQL解析器,這里給出2個應用SQL解析的例子。

無用條件去除

無用條件去除屬于優化器的邏輯優化范疇,可以僅僅根據SQL本身以及表結構即可完成,其優化的情況也是較多的,代碼在sql/sql_optimizer.cc文件中的remove_eq_conds函數。為了避免過于繁瑣的描述,以及大段代碼的粘貼,這里通過圖來分析以下四種情況:

a)1=1 and (m > 3 and n > 4)

b)1=2 and (m > 3 and n > 4)

c)1=1 or (m > 3 and n > 4)

d)1=2 or (m > 3 and n > 4) 

圖5 無用條件去除a 

圖6 無用條件去除b  

圖7 無用條件去除c 

圖8 無用條件去除d

如果對其代碼實現有興趣的同學,需要對MySQL中的一個重要數據結構Item類有所了解。因為其比較復雜,所以MySQL官方文檔,專門介紹了Item類。阿里的MySQL小組,也有類似的文章。如需更詳細的了解,就需要去查看源碼中sql/item_*等文件。

SQL特征生成

為了確保數據庫,這一系統基礎組件穩定、高效運行,業界有很多輔助系統。比如慢查詢系統、中間件系統。這些系統采集、收到SQL之后,需要對SQL進行歸類,以便統計信息或者應用相關策略。歸類時,通常需要獲取SQL特征。比如SQL: 

  1. select username, ismale from userinfo where age > 20 and level > 5; 

SQL特征為: 

  1. select username, ismale from userinfo where age > ? and level > ?  

業界著名的慢查詢分析工具pt-query-digest,通過正則表達式實現這個功能但是這類處理辦法Bug較多。接下來就介紹如何使用SQL解析,完成SQL特征的生成。

SQL特征生成分兩部分組成。

a) 生成Token數組

b) 根據Token數組,生成SQL特征

首先回顧在詞法解析章節,我們介紹了SQL中的關鍵字,并且每個關鍵字都有一個16位的整數對應,而非關鍵字統一用ident表示,其也對應了一個16位整數。如下表: 

將一個SQL轉換成特征的過程: 

在SQL解析過程中,可以很方便的完成Token數組的生成。而一旦完成Token數組的生成,就可以很簡單的完成SQL特征的生成。SQL特征被廣泛用于各個系統中,比如pt-query-digest需要根據特征對SQL歸類,然而其基于正則表達式的實現有諸多Bug。下面列舉幾個已知Bug: 

學習建議

最近,在對SQL解析器和優化器探索的過程中,從一開始的茫然無措到有章可循,也總結了一些心得體會,在這里跟大家分享一下。

首先,閱讀相關書籍,書籍能給我們一個系統的認識解析器和優化器的角度。但是該類針對MySQL的書籍市面上很少,目前中文作品可以看下《數據庫查詢優化器的藝術:原理解析與SQL性能優化》。

其次,要閱讀源碼,但是***以某個版本為基礎,比如MySQL5.6.23,因為SQL解析、優化部分的代碼在不斷變化。尤其是在跨越大的版本時,改動力度大。

再次,多使用GDB調試,驗證自己的猜測,檢驗閱讀質量。

***,需要寫相關代碼驗證,只有寫出來了才能算真正的掌握。

責任編輯:龐桂玉 來源: ITPUB
相關推薦

2017-02-20 19:23:13

2017-08-01 09:37:00

深度學習美團機器學習

2022-03-03 16:45:02

美團述職反饋

2019-08-23 13:10:39

美團點評Kubernetes集群管理

2019-05-29 10:42:06

大數據IT人工智能

2018-06-01 10:08:00

DBA美團SQL

2017-11-20 11:23:12

MySQLMyFlash閃回工具

2018-10-19 14:16:09

Flink數據倉庫數據系統

2015-10-08 10:09:16

2017-06-01 10:52:35

互聯網

2017-07-03 15:32:49

數據庫MySQL架構

2022-08-09 09:18:47

優化實踐

2015-11-03 11:03:08

騰訊美團

2017-03-24 14:29:23

互聯網

2025-03-25 12:00:00

@Value?Spring開發

2016-01-29 10:39:35

排序搜索美團

2017-12-29 08:54:58

高可用數據庫架構

2022-03-17 21:42:20

美團插件技術

2018-03-09 10:06:03

開源小程序前端

2015-10-08 10:01:27

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 色综久久 | 日本成人三级电影 | 久久午夜精品 | www久久久| 国产精品不卡一区 | 亚洲精品视频三区 | 亚洲国产成人一区二区 | 欧美黄色免费网站 | 日本特黄a级高清免费大片 国产精品久久性 | 欧美男人的天堂 | av一级在线观看 | 一级欧美 | 国产91在线播放精品91 | www,黄色,com | 国产视频h | 精品视频一区二区三区四区 | 国产人成精品一区二区三 | 色偷偷888欧美精品久久久 | 欧美综合久久久 | 91xxx在线观看 | 老司机免费视频 | 一区二区av在线 | 亚洲精品自在在线观看 | 黄色片视频免费 | 毛色毛片免费看 | 日本中文在线视频 | 久久高清精品 | 91精品国产91久久久久久密臀 | 久久精品综合网 | 国偷自产av一区二区三区 | 国产精品免费在线 | 日韩久久中文字幕 | 亚洲精品福利在线 | 国产精品久久久久久福利一牛影视 | 在线免费观看毛片 | 亚洲精品一区二区三区在线 | 久久久久国产精品午夜一区 | 亚洲国产精品久久久久久 | 91精品91久久久 | 天天干天天操天天爽 | 国产一区日韩在线 |