Mycat2的執(zhí)行計(jì)劃管理主要作用是管理執(zhí)行計(jì)劃,加快SQL到執(zhí)行計(jì)劃的轉(zhuǎn)換,并且提供一個(gè)方式可以從持久層讀取自定義的執(zhí)行計(jì)劃.它與緩存結(jié)果集不同,它更接近傳統(tǒng)分庫(kù)分表的緩存路由.Mycat2引入這個(gè)功能的初衷是Calcite在分析復(fù)雜SQL的耗時(shí)比較大.整體設(shè)計(jì)上參考了Oracle,Polardb-X的文檔.該功能在v1.18開始提供.
邏輯SQL,其中邏輯一詞對(duì)應(yīng)的邏輯庫(kù),邏輯表中的邏輯.它描述對(duì)邏輯庫(kù)邏輯表的操作.它在Mycat2里也叫DrdsSQL
,它包含了參數(shù)化SQL,參照綁定值,它們的類型,以及該SQL的字段別名.一個(gè)邏輯SQL的SQL模板具有多個(gè)基線,因?yàn)榧s束不同.
SQL運(yùn)行計(jì)劃基線,簡(jiǎn)稱基線.在Mycat2里,基線包含執(zhí)行計(jì)劃的'類型',邏輯SQL通過(guò)'約束'來(lái)校驗(yàn)該BASELINE
是否適用.一個(gè)基線可以對(duì)應(yīng)多個(gè)執(zhí)行計(jì)劃,它們的關(guān)系類似接口與實(shí)現(xiàn)的關(guān)系.而其中不同執(zhí)行計(jì)劃可能對(duì)不同的參數(shù)值具有不同的執(zhí)行代價(jià).它也可以通過(guò)FIX
命令強(qiáng)制綁定一個(gè)基線對(duì)應(yīng)一個(gè)執(zhí)行計(jì)劃,此時(shí)是一個(gè)基線對(duì)應(yīng)一個(gè)執(zhí)行計(jì)劃.基線在執(zhí)行計(jì)劃管理中最重要的作用是提供穩(wěn)定的執(zhí)行計(jì)劃,這是緩存執(zhí)行計(jì)劃后自然得到的效果.如果緩存有一定的失效,更新周期,則可以通過(guò)一定條件觸發(fā)更新,結(jié)合新的統(tǒng)計(jì)信息,可以發(fā)現(xiàn)更優(yōu)的執(zhí)行計(jì)劃.
一個(gè)執(zhí)行計(jì)劃對(duì)應(yīng)一種執(zhí)行器,它包含計(jì)算代價(jià)的函數(shù),也能生成執(zhí)行器實(shí)例,它還包含了生成它的'原因',比如通過(guò)hint
影響導(dǎo)致的生成了該計(jì)劃(baseline
中的SQL模板是不帶hint
的).
執(zhí)行器是執(zhí)行計(jì)劃生成的.它需要使用邏輯SQL的信息(參數(shù)值)才可以執(zhí)行并輸出結(jié)果集.同時(shí),它在執(zhí)行的時(shí)候可以統(tǒng)計(jì)執(zhí)行的運(yùn)行時(shí)信息,并反饋到統(tǒng)計(jì)器.因?yàn)閰?shù)化之后,selectItem
表達(dá)式中可能包含帶有?
的情況,所以字段名與參數(shù)值相關(guān).結(jié)果集輸出的字段信息需要使用邏輯SQL的別名信息.最后,它是與參數(shù)值類型強(qiáng)相關(guān)的,因?yàn)楸磉_(dá)式計(jì)算包含對(duì)應(yīng)的類型轉(zhuǎn)換.
其中挑選對(duì)于當(dāng)前參數(shù)值具有最小執(zhí)行成本的過(guò)程,實(shí)際上是SQL編譯優(yōu)化中代價(jià)分析過(guò)程的精簡(jiǎn)版,因?yàn)樗鼪](méi)有使用復(fù)雜的框架進(jìn)行分析也沒(méi)有生成執(zhí)行計(jì)劃的過(guò)程,而是僅僅對(duì)已有的執(zhí)行計(jì)劃代價(jià)計(jì)算函數(shù)賦值,計(jì)算得到代價(jià),從中選擇最小的來(lái)執(zhí)行.
對(duì)于有些SQL中帶有?參數(shù)的情況,它們的類型在Mycat2不能推導(dǎo).Mycat2對(duì)于這種情況的處理是延后編譯.在真正收到實(shí)際參數(shù)的時(shí)候,在把SQL模板與實(shí)際參數(shù)結(jié)合生成完整的,不帶有?的語(yǔ)法樹(AST
),然后再進(jìn)行參數(shù)化處理,生成新的SQL模板,參數(shù)和SQL模板約束.盡管MySQL在8.0.22
有了相關(guān)的類型推導(dǎo)規(guī)則,它可以進(jìn)一步,提早編譯即在預(yù)處理prepare
階段就編譯,但是Mycat2暫時(shí)不實(shí)現(xiàn)它.
他們存儲(chǔ)在原型庫(kù)(prototype
),在mycat庫(kù)下的spm
前綴的表里,一般是spm_baseline
和spm_plan
這兩個(gè)表.原型庫(kù)可能被多個(gè)Mycat2同時(shí)操作訪問(wèn),涉及修改數(shù)據(jù)的情況,mycat會(huì)開啟串行事務(wù)進(jìn)行操作.baseline
和plan
的id
也是全局唯一的,它們的生成方法與mycat的workid
相關(guān).
不是的,但是對(duì)于Mycat2來(lái)說(shuō),執(zhí)行計(jì)劃緩存是必須的.因?yàn)镸ycat2的執(zhí)行器使用了針對(duì)SQL模板生成對(duì)應(yīng)的關(guān)系表達(dá)式,進(jìn)一步編譯成執(zhí)行器.如果對(duì)于每一條SQL都進(jìn)行該過(guò)程,則性能不佳.如果僅僅進(jìn)行在Mycat2運(yùn)行的時(shí)候緩存生成的,而Mycat2關(guān)閉.重啟后,執(zhí)行計(jì)劃緩存就丟失了.用戶會(huì)在第一次使用SQL訪問(wèn)的時(shí)候,明顯感覺(jué)Mycat2很慢.對(duì)于這個(gè)情況,可以減少一些優(yōu)化過(guò)程,減少編譯時(shí)間,但是會(huì)導(dǎo)致生成的執(zhí)行計(jì)劃不佳的情況.所以Mycat2添加了對(duì)執(zhí)行計(jì)劃持久化的功能.
Mycat2暫時(shí)沒(méi)有實(shí)現(xiàn)自動(dòng)演化,所以獲得的執(zhí)行計(jì)劃都是Accept=true
的狀態(tài).在實(shí)現(xiàn)自動(dòng)演化的執(zhí)行計(jì)劃管理中,如果觸發(fā)了執(zhí)行計(jì)劃演化,可能會(huì)得到新的執(zhí)行計(jì)劃,它可能在一定條件下,理論上比現(xiàn)有的緩存的執(zhí)行計(jì)劃更具有性能優(yōu)勢(shì),但是此時(shí)該執(zhí)行計(jì)劃是沒(méi)有被驗(yàn)證的(Accept=false
),它還不能設(shè)置為Accept=true
.它需要經(jīng)過(guò)一定的驗(yàn)證過(guò)程(比如流量灰度),證明它比現(xiàn)有的執(zhí)行計(jì)劃確實(shí)在一定條件更加才會(huì)被設(shè)置為Accept=true
.
一方面,因?yàn)镸ycat2保留了中間件的文件文件配置(ZK也是如此),允許了用戶脫離Mycat2的感知直接操作配置文件,所以如果手動(dòng)更改了表的配置,導(dǎo)致持久化的執(zhí)行計(jì)劃與表的信息不一致,則會(huì)導(dǎo)致數(shù)據(jù)查詢錯(cuò)誤.對(duì)于這種情況,需要用戶自行刪除持久化的執(zhí)行計(jì)劃..因?yàn)樽詣?dòng)型hash的表允許使用SQL來(lái)修改表,所以它支持自動(dòng)刪除表相關(guān)的執(zhí)行計(jì)劃(一般的數(shù)據(jù)庫(kù)是在更改表配置后自動(dòng)刪除緩存的執(zhí)行計(jì)劃的).
另一方面,Mycat2不會(huì)自動(dòng)進(jìn)行把內(nèi)存中的執(zhí)行計(jì)劃持久化,這與其他數(shù)據(jù)庫(kù)不同,它們?cè)谟錾蠌?fù)雜SQL的時(shí)候會(huì)自動(dòng)持久化.而Mycat2提供了一個(gè)把內(nèi)存中所有基線及其執(zhí)行計(jì)劃持久化的命令.用于準(zhǔn)備上生產(chǎn)或者測(cè)試的時(shí)候保存執(zhí)行計(jì)劃.Mycat2在啟動(dòng)時(shí)候會(huì)自動(dòng)加載(load)原型庫(kù)中存在的執(zhí)行計(jì)劃.如果遇上不一致的問(wèn)題,把這涉及的表數(shù)據(jù)清空即可.
對(duì)于已經(jīng)緩存的執(zhí)行計(jì)劃,Mycat2就會(huì)使用已有的(一個(gè))執(zhí)行計(jì)劃,不會(huì)自動(dòng)演化新的執(zhí)行計(jì)劃(統(tǒng)計(jì)信息變化,表配置變化).
Mycat2提供了管理執(zhí)行計(jì)劃的命令,供用戶對(duì)執(zhí)行計(jì)劃進(jìn)行操作.值得注意的是,它是涉及兩個(gè)存儲(chǔ)空間的,Mycat2內(nèi)存緩存以及原型庫(kù)的持久化存儲(chǔ),對(duì)于其中一個(gè)Mycat2實(shí)例進(jìn)行的內(nèi)存操作不會(huì)發(fā)生在另一個(gè)Mycat2實(shí)例上.
首先,Mycat2在啟動(dòng)時(shí)候一定會(huì)加載原型庫(kù)的存在的執(zhí)行計(jì)劃.加載完畢才啟動(dòng)成功.然后,Mycat2就處于運(yùn)行狀態(tài).此后Mycat2將不會(huì)自動(dòng)對(duì)原型庫(kù)中的涉及持久化的執(zhí)行計(jì)劃的表進(jìn)行操作.而是僅僅在Mycat2內(nèi)存中的緩存進(jìn)行添加操作(如果遇上沒(méi)有執(zhí)行計(jì)劃的SQL).
如果用戶使用執(zhí)行計(jì)劃管理命令才會(huì)涉及到對(duì)原型庫(kù)的操作.
LIST 顯示所有執(zhí)行計(jì)劃,包含未持久化的,已經(jīng)持久化的執(zhí)行計(jì)劃. 但是不會(huì)把持久化的基線/執(zhí)行計(jì)劃替換內(nèi)存中的基線/執(zhí)行計(jì)劃
BASELINE LIST
LOAD 把持久化的基線及其執(zhí)行計(jì)劃加載(替換)到內(nèi)存
BASELINE LOAD xxxxx
BASELINE LOAD_ALL_BASELINES
該命令會(huì)在Mycat2啟動(dòng)時(shí)候自動(dòng)執(zhí)行
把持久化中的基線加載到內(nèi)存
BASELINE LOAD_ALL_BASELINES
BASELINE PERSIST_ALL_BASELINES
把內(nèi)存中的所有基線及其執(zhí)行計(jì)劃持久化
BASELINE PERSIST_ALL_BASELINES
LOAD_PLAN 把持久化的執(zhí)行計(jì)劃加載(替換)到內(nèi)存
BASELINE LOAD_PLAN xxxxx
PERSIST 把內(nèi)存中的基線持久化
BASELINE PERSIST xxxxx
PERSIST_PLAN 把內(nèi)存中的執(zhí)行計(jì)劃持久化
BASELINE PERSIST_PLAN xxxxx
CLEAR 清理內(nèi)存中的基線,但是不會(huì)清理持久化的基線
BASELINE CLEAR xxxxx
CLEAR_PLAN 清理內(nèi)存中的執(zhí)行計(jì)劃,但是不會(huì)清理持久化的執(zhí)行計(jì)劃
BASELINE CLEAR_PLAN xxxxx
DELETE 刪除持久化的基線,但是不會(huì)清理內(nèi)存中的執(zhí)行計(jì)劃
BASELINE DELETE xxxxx
DELETE_PLAN 刪除持久化的執(zhí)行計(jì)劃,但是不會(huì)清理內(nèi)存中的執(zhí)行計(jì)劃
BASELINE DELETE_PLAN xxxxx
根據(jù)SQL(帶hint)添加執(zhí)行計(jì)劃,而FIX命令則是把執(zhí)行計(jì)劃與基線綁定.該命令不會(huì)自動(dòng)持久化基線與執(zhí)行計(jì)劃.
在創(chuàng)建執(zhí)行計(jì)劃的時(shí)候遇上沒(méi)有基線的情況會(huì)自動(dòng)創(chuàng)建對(duì)應(yīng)的基線.如果add/fix命令生成的執(zhí)行計(jì)劃與現(xiàn)有緩存的執(zhí)行計(jì)劃相同則使用現(xiàn)有的執(zhí)行計(jì)劃.
BASELINE ADD /*+ mycat:xxx*/ select 1
BASELINE FIX /*+ mycat:xxx*/ select 1
取消基線與執(zhí)行計(jì)劃綁定
參數(shù)是基線id
BASELINE UNFIX xxxx
``
### 基于腳本的自動(dòng)演化
整體來(lái)說(shuō),手動(dòng)演化和淘汰可以結(jié)合用戶編寫的SQL腳本(根據(jù)一定的情況進(jìn)行觸發(fā))進(jìn)行調(diào)用實(shí)現(xiàn)自動(dòng)演化的效果.它與數(shù)據(jù)庫(kù)自動(dòng)演化的方式的區(qū)別是數(shù)據(jù)庫(kù)掌握更多的觸發(fā)條件,比如執(zhí)行計(jì)劃占用的內(nèi)存大小以及整個(gè)執(zhí)行計(jì)劃管理器的內(nèi)存空間大小.
### 序列化的執(zhí)行計(jì)劃
Mycat2的執(zhí)行計(jì)劃支持json格式序列化并反序列化成執(zhí)行計(jì)劃.所以可以修改保存在數(shù)據(jù)庫(kù)中執(zhí)行計(jì)劃來(lái)實(shí)現(xiàn)修改執(zhí)行器的行為.
更多建議: