Mycat2 注釋

2021-09-22 20:17 更新

注釋的解析語法

query:  [ '/*+' hint [, hint]* '*/' ] select
select:
      SELECT   [ '/*+' hint [, hint]* '*/' ]  [ ALL | DISTINCT ]
          { * | projectItem [, projectItem ]* }
      FROM tableExpression
      [ WHERE booleanExpression ]
      [ GROUP BY { groupItem [, groupItem ]* } ]
      [ HAVING booleanExpression ]
selectWithoutFrom:
      SELECT [ ALL | DISTINCT ]
          { * | projectItem [, projectItem ]* }
projectItem:
      expression [ [ AS ] columnAlias ]
  |   tableAlias . *
tableExpression:
      tableReference [, tableReference ]*
  |   tableExpression [ NATURAL ] [ ( LEFT | RIGHT | FULL ) [ OUTER ] ] JOIN tableExpression [ joinCondition ]
  |   tableExpression CROSS JOIN tableExpression
  |   tableExpression [ CROSS | OUTER ] APPLY tableExpression
joinCondition:
      ON booleanExpression
  |   USING '(' column [, column ]* ')'
|tableReference:
      tablePrimary
      [ [ AS ] alias [ '(' columnAlias [, columnAlias ]* ')' ] ]
|tablePrimary:
     | [ [ catalogName . ] schemaName . ] tableName
     | '(' TABLE [ [ catalogName . ] schemaName . ] tableName ')'
     tablePrimary [ '/*+' hint [, hint]* '*/' ]
|values:
      VALUES expression [, expression ]*
groupItem:
      expression
  |   '(' ')'
  |   '(' expression [, expression ]* ')'

  
SELECT UNION [ALL | DISTINCT] SELECT ...

特殊地,SQL頭部可以編寫注釋

可以留意到[ '/*+' hint [, hint]* '*/' ]在select關(guān)鍵字之后,tablePrimary(即表名)之后

在mycat2里,建議在SQL頭部寫注釋,其他語法單元寫注釋更多是為了輔助分析3

hint: 'MYCAT' ':'  HINT_TEXT

HINT_TEXT是一段文本,沒有固定格式

這里的HINT語法并非指注釋只能在上述的語法單元中編寫,而是說,Mycat2在這些語法單元捕獲這些注釋,進(jìn)行處理,不在這些語法單元的注釋會被忽略

HINT的處理組件

Mycat2支持注釋控制Mycat2對SQL的處理過程,在分庫分表型數(shù)據(jù)庫中,注釋的生效的地方有三處地方

  1. SQL優(yōu)化器
  2. 執(zhí)行器
  3. 存儲節(jié)點即MySQL

本文描述第一種注釋,它控制SQL編譯器生成執(zhí)行器

注釋應(yīng)用對象的定位

注釋在作用范圍上習(xí)慣上分為兩大類

  1. 全局的,
  2. 針對某個節(jié)點的(可能結(jié)合父子節(jié)點判斷).

定位的方法

  1. 路徑法,根據(jù)遍歷順序或者名稱路徑找到該節(jié)點
  2. 唯一名稱法,在SQL中標(biāo)記唯一的名稱,在遍歷時候找到帶有該名稱的節(jié)點.

于是有下面的具體的定位方法.

QB_NAME

參考MySQL的優(yōu)化器注釋Optimizer Hints,Mycat2也使用QB_NAME給表與查詢塊(Select語法元素)建立名稱.注釋命令通過該名稱定位生效的作用域.

格式

QB_NAME(name)

name為符合MySQL詞法的標(biāo)識符

表注釋

SELECT * FROM db1.travelrecord /*+MYCAT:QB_NAME(SEL$1)*/


SELECT * FROM db1.travelrecord /*+MYCAT:QB_NAME(SEL$2)*/ 
union all
SELECT * FROM db1.travelrecord /*+MYCAT:QB_NAME(SEL$4)*/ 


SELECT * FROM db1.travelrecord t where t.id in (SELECT id FROM db1.user /*+MYCAT:QB_NAME(SEL$4)*/  where user_id = 1)

查詢塊注釋

SELECT /*+MYCAT:QB_NAME(SEL$1)*/ * FROM db1.travelrecord 


SELECT /*+MYCAT:QB_NAME(SEL$1)*/  * FROM db1.travelrecord
union all
SELECT /*+MYCAT:QB_NAME(SEL$3)*/  * FROM db1.travelrecord /*+MYCAT:QB_NAME(SEL$4)*/ 


SELECT /*+MYCAT:QB_NAME(SEL$1)*/ * FROM db1.travelrecord t where t.id in (SELECT id FROM db1.user /*+MYCAT:QB_NAME(SEL$4)*/  where user_id = 1)

自動生成的QB_NAME注釋 Mycat2的SQL編譯器在處理語法樹的時候會在Select語法單元或表語法單元自動添加QB_NAME注釋,它們生成的參數(shù)是以SEL$為前綴,以1為開始值,Select語法單元或表語法單元出現(xiàn)的順序,當(dāng)表有別名的時候,則忽略該值,使用別名作為QB_NAME的參數(shù).

//以該無注釋的SQL為例
SELECT * FROM db1.travelrecord
union all
SELECT * FROM db1.travelrecord


//自動生成QB_NAME
SELECT /*+MYCAT:QB_NAME(SEL$1)*/  * FROM db1.travelrecord /*+MYCAT:QB_NAME(SEL$2)*/
union all
SELECT /*+MYCAT:QB_NAME(SEL$3)*/  * FROM db1.travelrecord /*+MYCAT:QB_NAME(SEL$4)*/ 


//當(dāng)有別名的時候
SELECT /*+MYCAT:QB_NAME(SEL$1)*/ * FROM db1.travelrecord t /*+MYCAT:QB_NAME(t)*/ where t.id in (SELECT /*+MYCAT:QB_NAME(SEL$3)*/ id FROM db1.user /*+MYCAT:QB_NAME(SEL$4)*/  where user_id = 1)

無需QB_NAME 上一段描述了QB_NAME的作用,實際上是為了定位注釋的參數(shù)是關(guān)系表達(dá)式中哪個對象.實際上QB_NAME不是一定需要的,比如

SELECT /*+MYCAT:USE_HASH_JOIN(travelrecord,user)* FROM db1.travelrecord t where t.id in (SELECT id FROM db1.user where user_id = 1)

這個情況下,我們只需探測join節(jié)點下的兩個表是否為travelrecorduser即可定位該JOIN節(jié)點就是應(yīng)用USE_HASH_JOIN注釋的對象.

SQL編譯器對執(zhí)行優(yōu)化器注釋(物理表達(dá)式注釋)的處理

SQL通過參數(shù)化處理得到帶有HINT的參數(shù)化SQL與不帶HINT的參數(shù)化SQL,前者用于生成執(zhí)行計劃,后者用在執(zhí)行計劃管理,起SQL模板的作用,滿足該SQL模板的SQL集合都可以使用該執(zhí)行計劃,

首先,帶有HINT的參數(shù)化SQL經(jīng)過SQL編譯器編譯得到邏輯關(guān)系表達(dá)式,并把SQL語法節(jié)點上的HINT綁定到邏輯關(guān)系表達(dá)式上.

其次,根據(jù)SQL的詞法作用域?qū)int進(jìn)行傳播,從父節(jié)點往子節(jié)點開始傳播,下節(jié)點會得到上節(jié)點的hint,并執(zhí)行hint的校驗函數(shù),判斷節(jié)點(關(guān)系表達(dá)式)是否適用該節(jié)點.這樣,我們就得到了帶有Hint的邏輯關(guān)系表達(dá)式.

然后,SQL優(yōu)化器會對邏輯關(guān)系表達(dá)式進(jìn)行優(yōu)化,最終轉(zhuǎn)換成物理關(guān)系表達(dá)式.其中過程非常復(fù)雜,最常見的就是關(guān)系表達(dá)式之間的上拉下推.所以Hint也有可能跟隨這些邏輯關(guān)系表達(dá)式進(jìn)行在關(guān)系表達(dá)式樹的位置中發(fā)生變化或者因為額外添加或者刪除關(guān)系表達(dá)式導(dǎo)致hint無法生效.所以一般來說,hint作用于關(guān)鍵節(jié)點,它不能影響語義,無法生效也不足為奇.

在分庫分表數(shù)據(jù)庫中,因為引入了View關(guān)系算子,它內(nèi)部仍然是邏輯關(guān)系表達(dá)式,它的父節(jié)點是可以是邏輯關(guān)系表達(dá)式也可以是物理關(guān)系表達(dá)式.已經(jīng)內(nèi)部的關(guān)系表達(dá)式不視為代價優(yōu)化器可以直接優(yōu)化的部分,它們是根據(jù)Mycat2的RBO規(guī)則生成的,雖然它們?nèi)匀粎⑴c代價計算.

但是考慮到Hint的作用域,考慮以下這一種情況.已經(jīng)成為View內(nèi)部的表節(jié)點帶有的QB_NAME,它們?nèi)匀灰鼙煌獠康腍ashJoin的Hint處理器檢查到,即Hint的處理過程是跨越View的外部和內(nèi)部的.

物理表達(dá)式注釋

Join物理算子注釋

該注釋支持QB_NAME(別名)定位參數(shù)也支持表名直接指定,注釋不生效不會報錯

格式

use_xxx_join(QB_NAME,QB_NAME)

第一個QB_NAME是代表左表的關(guān)系算子,第二個QB_NAME是代表右表的關(guān)系算子

use_nl_join(QB_NAME,QB_NAME)(1.18-2021-4-29后)

/*+MYCAT:use_nl_join(n,s) */select * from db1.travelrecord n join db1.company s on n.id = s.id and n.id = 1
plan
MycatNestedLoopJoin(condition=[=($0, $6)], joinType=[inner])
  MycatView(distribution=[[db1.travelrecord]])
  MycatMatierial
    MycatView(distribution=[[db1.company]])
Each(targetName=c0, sql=SELECT * FROM db1_0.travelrecord_0 WHERE (`id` = ?) union all SELECT * FROM db1_0.travelrecord_1 WHERE (`id` = ?))
Each(targetName=c1, sql=SELECT * FROM db1_1.travelrecord_0 WHERE (`id` = ?) union all SELECT * FROM db1_1.travelrecord_1 WHERE (`id` = ?))
Each(targetName=prototype, sql=SELECT * FROM db1.company WHERE (`id` = ?))
...

use_hash_join(QB_NAME,QB_NAME)(1.18-2021-4-29后)

/*+MYCAT:use_hash_join(n,s) */select * from db1.travelrecord n join db1.company s on n.id = s.id and n.id = 1
plan
MycatHashJoin(condition=[=($0, $6)], joinType=[inner])
  MycatView(distribution=[[db1.company]])
Each(targetName=c0, sql=SELECT * FROM db1_0.travelrecord_0 WHERE (`id` = ?) union all SELECT * FROM db1_0.travelrecord_1 WHERE (`id` = ?))
Each(targetName=c1, sql=SELECT * FROM db1_1.travelrecord_0 WHERE (`id` = ?) union all SELECT * FROM db1_1.travelrecord_1 WHERE (`id` = ?))
Each(targetName=prototype, sql=SELECT * FROM db1.company WHERE (`id` = ?))
...

use_merge_join(QB_NAME,QB_NAME)(1.18-2021-4-29后)

/*+MYCAT:use_merge_join(n,s) */select * from db1.travelrecord n join db1.company s on n.id = s.id and n.id = 1
plan
MycatSortMergeJoin(condition=[=($0, $6)], joinType=[inner])
  MycatMergeSort(sort0=[$0], dir0=[ASC])
    MycatView(distribution=[[db1.travelrecord]])
  MycatMergeSort(sort0=[$0], dir0=[ASC])
    MycatView(distribution=[[db1.company]])
Each(targetName=c0, sql=SELECT * FROM db1_0.travelrecord_0 WHERE (`id` = ?) ORDER BY (`id` IS NULL), `id`)
Each(targetName=c0, sql=SELECT * FROM db1_0.travelrecord_1 WHERE (`id` = ?) ORDER BY (`id` IS NULL), `id`)
Each(targetName=c1, sql=SELECT * FROM db1_1.travelrecord_0 WHERE (`id` = ?) ORDER BY (`id` IS NULL), `id`)
Each(targetName=c1, sql=SELECT * FROM db1_1.travelrecord_1 WHERE (`id` = ?) ORDER BY (`id` IS NULL), `id`)
Each(targetName=prototype, sql=SELECT * FROM db1.company WHERE (`id` = ?) ORDER BY (`id` IS NULL), `id`)
...

no_hash_join(QB_NAME,QB_NAME)(1.18-2021-4-29后)

禁用生成HashJoin

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號