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)行處理,不在這些語法單元的注釋會被忽略
Mycat2支持注釋控制Mycat2對SQL的處理過程,在分庫分表型數(shù)據(jù)庫中,注釋的生效的地方有三處地方
本文描述第一種注釋,它控制SQL編譯器生成執(zhí)行器
注釋在作用范圍上習(xí)慣上分為兩大類
定位的方法
于是有下面的具體的定位方法.
參考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é)點下的兩個表是否為travelrecord
和user
即可定位該JOIN
節(jié)點就是應(yīng)用USE_HASH_JOIN
注釋的對象.
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)部的.
該注釋支持QB_NAME(別名)定位參數(shù)也支持表名直接指定,注釋不生效不會報錯
use_xxx_join(QB_NAME,QB_NAME)
第一個QB_NAME是代表左表的關(guān)系算子,第二個QB_NAME是代表右表的關(guān)系算子
/*+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` = ?))
...
/*+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` = ?))
...
/*+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`)
...
禁用生成HashJoin
更多建議: