日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

SQL解析在美团的应用

發(fā)布時間:2024/7/5 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SQL解析在美团的应用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

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

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

現(xiàn)狀與場景

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

  • 美團(tuán)開源的SQLAdvisor。它基于MySQL原生態(tài)詞法解析,結(jié)合分析SQL中的where條件、聚合條件、多表Join關(guān)系給出索引優(yōu)化建議。
  • 去哪兒開源的Inception。側(cè)重于根據(jù)內(nèi)置的規(guī)則,對SQL進(jìn)行審核。
  • 阿里的Cloud DBA。根據(jù)官方文檔介紹,其也是提供SQL優(yōu)化建議和改寫。

上述產(chǎn)品都有非常合適的應(yīng)用場景,在業(yè)界也被廣泛使用。但是SQL解析的應(yīng)用場景遠(yuǎn)遠(yuǎn)沒有被充分發(fā)掘,比如:

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

因此為了讓所有有需要的業(yè)務(wù)都能方便的使用SQL解析功能,我們認(rèn)為應(yīng)該具有如下特性。

  • 直接暴露SQL解析接口,使用盡量簡單。比如,輸入SQL,則輸出表名、特征和優(yōu)化建議。
  • 接口的使用不依賴于特定的語言,否則維護(hù)和使用的代價太高。比如,以HTTP等方式提供服務(wù)。

千里之行,始于足下。下面我先介紹下SQL的解析原理。

原理

SQL解析與優(yōu)化是屬于編譯器范疇,和C等其他語言的解析沒有本質(zhì)的區(qū)別。其中分為,詞法分析、語法和語義分析、優(yōu)化、執(zhí)行代碼生成。對應(yīng)到MySQL的部分,如下圖

詞法分析

SQL解析由詞法分析和語法/語義分析兩個部分組成。詞法分析主要是把輸入轉(zhuǎn)化成一個個Token。其中Token中包含Keyword(也稱symbol)和非Keyword。例如,SQL語句 select username from userinfo,在分析之后,會得到4個Token,其中有2個Keyword,分別為select和from:

關(guān)鍵字非關(guān)鍵字關(guān)鍵字非關(guān)鍵字
selectusernamefromuserinfo

通常情況下,詞法分析可以使用Flex來生成,但是MySQL并未使用該工具,而是手寫了詞法分析部分(據(jù)說是為了效率和靈活性,參考此文)。具體代碼在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,有興趣的同學(xué)可以下載源碼研究。

語法分析

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

語法分析樹

SQL語句:

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

會生成如下語法樹。

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

MySQL語法分析樹生成過程

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

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

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

select /*select語句入口*/:select_init{LEX *lex= Lex;lex->sql_command= SQLCOM_SELECT;};select_init:SELECT_SYM /*select 關(guān)鍵字*/ select_init2| '(' select_paren ')' union_opt;select_init2:select_part2{LEX *lex= Lex;SELECT_LEX * sel= lex->current_select;if (lex->current_select->set_braces(0)){my_parse_error(ER(ER_SYNTAX_ERROR));MYSQL_YYABORT;}if (sel->linkage == UNION_TYPE &&sel->master_unit()->first_select()->braces){my_parse_error(ER(ER_SYNTAX_ERROR));MYSQL_YYABORT;}}union_clause; select_part2:{LEX *lex= Lex;SELECT_LEX *sel= lex->current_select;if (sel->linkage != UNION_TYPE)mysql_init_select(lex);lex->current_select->parsing_place= SELECT_LIST;}select_options select_item_list /*解析列名*/{Select->parsing_place= NO_MATTER;}select_into select_lock_type;select_into:opt_order_clause opt_limit_clause {}| into| select_from /*from 字句*/| into select_from| select_from into; select_from:FROM join_table_list /*解析表名*/ where_clause /*where字句*/ group_clause having_clauseopt_order_clause opt_limit_clause procedure_analyse_clause{Select->context.table_list=Select->context.first_name_resolution_table=Select->table_list.first;}| FROM DUAL_SYM where_clause opt_limit_clause/* oracle compatibility: oracle always requires FROM clause,and DUAL is system table without fields.Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ?Hmmm :) */;where_clause:/* empty */ { Select->where= 0; }| WHERE{Select->parsing_place= IN_WHERE;}expr /*各種表達(dá)式*/{SELECT_LEX *select= Select;select->where= $3;select->parsing_place= NO_MATTER;if ($3)$3->top_level_item();};/* all possible expressions */ expr:| expr and expr %prec AND_SYM{/* See comments in rule expr: expr or expr */Item_cond_and *item1;Item_cond_and *item3;if (is_cond_and($1)){item1= (Item_cond_and*) $1;if (is_cond_and($3)){item3= (Item_cond_and*) $3;/*(X1 AND X2) AND (Y1 AND Y2) ==> AND (X1, X2, Y1, Y2)*/item3->add_at_head(item1->argument_list());$$ = $3;}else{/*(X1 AND X2) AND Y ==> AND (X1, X2, Y)*/item1->add($3);$$ = $1;}}else if (is_cond_and($3)){item3= (Item_cond_and*) $3;/*X AND (Y1 AND Y2) ==> AND (X, Y1, Y2)*/item3->add_at_head($1);$$ = $3;}else{/* X AND Y */$$ = new (YYTHD->mem_root) Item_cond_and($1, $3);if ($$ == NULL)MYSQL_YYABORT;}}

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

核心數(shù)據(jù)結(jié)構(gòu)及其關(guān)系

在SQL解析中,最核心的結(jié)構(gòu)是SELECT_LEX,其定義在sql/sql_lex.h中。下面僅列出與上述例子相關(guān)的部分。

上面圖示中,列名username、ismale存儲在item_list中,表名存儲在table_list中,條件存儲在where中。其中以where條件中的Item層次結(jié)構(gòu)最深,表達(dá)也較為復(fù)雜,如下圖所示。

SQL解析的應(yīng)用

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

無用條件去除

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

  • 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)

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

SQL特征生成

為了確保數(shù)據(jù)庫,這一系統(tǒng)基礎(chǔ)組件穩(wěn)定、高效運(yùn)行,業(yè)界有很多輔助系統(tǒng)。比如慢查詢系統(tǒng)、中間件系統(tǒng)。這些系統(tǒng)采集、收到SQL之后,需要對SQL進(jìn)行歸類,以便統(tǒng)計信息或者應(yīng)用相關(guān)策略。歸類時,通常需要獲取SQL特征。比如SQL:

select username, ismale from userinfo where age > 20 and level > 5;``` SQL特征為: ```sql select username, ismale from userinfo where age > ? and level > ?

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

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

  • a) 生成Token數(shù)組
  • b) 根據(jù)Token數(shù)組,生成SQL特征

首先回顧在詞法解析章節(jié),我們介紹了SQL中的關(guān)鍵字,并且每個關(guān)鍵字都有一個16位的整數(shù)對應(yīng),而非關(guān)鍵字統(tǒng)一用ident表示,其也對應(yīng)了一個16位整數(shù)。如下表:

標(biāo)識selectfromwhere>?andident
整數(shù)728448878463893272476

將一個SQL轉(zhuǎn)換成特征的過程:

原SQLselectusernamefromuserinfowhereage>20
SQL特征selectident:length:valuefromident:length:valuewhereident:length:value>?

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

原始SQLpt-query-digest生成的特征SQL解析器生成的特征
select * from email_template2 where id = 1select * from mail_template? where id = ?select * from email_template2 where id = ?
REPLACE INTO a VALUES(‘INSERT INTO foo VALUES (1),(2)’)replace into a values(\‘insert into foo values(?+)replace into a values (?)

因此可以看出SQL解析的優(yōu)勢是很明顯的。

學(xué)習(xí)建議

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

  • 首先,閱讀相關(guān)圖書書籍。圖書能給我們系統(tǒng)認(rèn)識解析器和優(yōu)化器的角度。但是針對MySQL的此類圖書市面上很少,目前中文作品可以看一看《數(shù)據(jù)庫查詢優(yōu)化器的藝術(shù):原理解析與SQL性能優(yōu)化》。
  • 其次,要閱讀源碼,但是最好以某個版本為基礎(chǔ),比如MySQL5.6.23,因為SQL解析、優(yōu)化部分的代碼在不斷變化。尤其是在跨越大的版本時,改動力度大。
  • 再次,多使用GDB調(diào)試,驗證自己的猜測,檢驗閱讀質(zhì)量。

最后,需要寫相關(guān)代碼驗證,只有寫出來了才能算真正的掌握。

作者簡介

  • 廣友,美團(tuán)到店綜合事業(yè)群MySQL DBA專家,2012年畢業(yè)于中國科學(xué)技術(shù)大學(xué),2017年加入美團(tuán),長期致力于MySQL及周邊工具的研究。
  • 金龍,2014年加入美團(tuán),主要從事相關(guān)的數(shù)據(jù)庫運(yùn)維、高可用和相關(guān)的運(yùn)維平臺建設(shè)。對運(yùn)維高可用與架構(gòu)相關(guān)感興趣的同學(xué)可以關(guān)注個人微信公眾號“自己的設(shè)計師”,定期推送運(yùn)維相關(guān)原創(chuàng)內(nèi)容。
  • 邢帆,美團(tuán)到店綜合事業(yè)群MySQL DBA,2017年研究生畢業(yè)后加入美團(tuán),目前已經(jīng)對MySQL運(yùn)維有一定經(jīng)驗,并編寫了一些自動化腳本。

招聘信息

美團(tuán)DBA團(tuán)隊招聘各類DBA人才,base北京上海均可。我們致力于為公司提供穩(wěn)定、可靠、高效的在線存儲服務(wù),打造業(yè)界領(lǐng)先的數(shù)據(jù)庫團(tuán)隊。這里有基于Redis Cluster構(gòu)建的大規(guī)模分布式緩存系統(tǒng)Squirrel,也有基于Tair進(jìn)行大刀闊斧改進(jìn)的分布式KV存儲系統(tǒng)Cellar,還有數(shù)千各類架構(gòu)的MySQL實例,每天提供萬億級的OLTP訪問請求。真正的海量、分布式、高并發(fā)環(huán)境。歡迎各位朋友推薦或自薦至jinlong.cai#dianping.com。

總結(jié)

以上是生活随笔為你收集整理的SQL解析在美团的应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。