天天看點

Mycat進階

作者:原克技術

一、Mycat配置詳解

1、Mycat支援的兩種配置方式

Mycat從1.5版本開始支援兩種配置方式:ZooKeeper及本地XML方式。Mycat預設以本地加載XML的方式啟動,如果需要配置成以ZooKeeper的方式啟動,則應把conf目錄下zk.conf檔案中的 loadfromzk 參數設定成 true。

2、server.xml配置檔案

server.xml配置檔案包含了Mycat的系統配置資訊

2.1、user标簽

<user name="user">
    <property name="password">user</property>
    <property name="schemas">TESTDB</property>
    <property name="readOnly">true</property>
    <property name="defaultSchema">TESTDB</property>
</user>           

user标簽主要用于定義登入Mycat的使用者和權限。在如上配置中定義了使用者名和密碼都為test的使用者,該使用者可以通路的schema隻有TESTDB。

若要在schema.xml中定義TESTDB,則TESTDB必須先在server.xml中定義,否則該使用者将無法通路該TESTDB。如果使用了use指令,則Mycat會有如下錯誤提示:

Error 1044 (HY000):Access denied for user 'test' to database 'xxx'           

可以修改user标簽的name屬性來指定使用者名,修改password的值來修改密碼,修改readOnly的值為true或false來限制使用者的讀寫權限。如果需要同時通路多個schema,則多個schema之間使用英文逗号隔開,例如:

<property name="schemas">TESTDB,db1,db2</property>           

benchmark屬性

通過設定benchmark屬性的值來限制前端的整體連接配接數量,如果其值為0或不對其進行設定,則表示不限制連接配接數量。例如:

<property name="benchmark">1000</property>           

2.2、system标簽

2.2.1、charset屬性

<system>
  <property name="charset">utf8</property>
</system>           

2.2.2、processors屬性

processors屬性指定系統可用的線程數量,預設值為機器CPU核心×每個核心運作線程的數量,processors值會影響processorBufferPool、processorBufferLocalPercent、processorExecutor屬性。NIOProcessor的個數也由processors屬性決定,是以調優時可以适當地修改processors值。

2.2.3、processorBufferChunk屬性

processorBufferChunk屬性指定每次配置設定Socket Direct Buffer的預設值為4096個位元組,也會影響BufferPool的長度,如果一次性擷取的位元組過多而導緻Buffer不夠用,則會經常出現警告,可以适當調大processorBufferChunk值。

2.2.4、sequnceHandlerType屬性

sequnceHandlerType屬性指定Mycat全局序列的類型:0為本地檔案方式;1為資料庫方式;2為時間戳序列方式。預設使用本地檔案方式,檔案方式主要用于測試。

2.2.5、MySQL連接配接的相關屬性

初始化MySQL前後端連接配接所涉及的一些屬性如下。

· packetHeaderSize:指定MySQL協定中的封包頭長度,預設值為4個位元組。

· maxPacketSize:指定MySQL協定可以攜帶的資料的最大大小,預設值為16MB。

· idleTimeout:指定連接配接的空閑時間的逾時長度。如果某個連接配接的空閑時間超過idleTimeout的值,則該連接配接将關閉資源并回收,機關為毫秒,預設為30分鐘。

· charset:初始化連接配接字元集,預設為utf8。

· txIsolation:初始化前端連接配接事務的隔離級别,後續的txIsolation值為用戶端的配置值。預設值為REPEATED_READ,對應的數字為3。

· sqlExecuteTimeout:執行SQL語句的逾時時間,若SQL語句的執行時間超過這個值,則會直接關閉連接配接,機關為秒,預設值為300秒。

2.2.6、心跳屬性

· processorCheckPeriod:清理NIOProcessor前後端空閑、逾時、關閉連接配接的時間間隔,機關為毫秒,預設為1秒。

· dataNodeIdleCheckPeriod:對後端連接配接進行空閑、逾時檢查的時間間隔,機關為毫秒,預設為300秒。

· dataNodeHeartbeatPeriod:對後端的所有讀、寫庫發起心跳的間隔時間,機關為毫秒,預設為10秒。

2.2.7、分布式事務開關屬性

handleDistributedTransactions是分布式事務開關:0為不過濾分布式事務;1為過濾分布式事務(如果分布式事務内隻涉及全局表,則不過濾);2為不過濾分布式事務,但是記錄分布式事務日志。主要用于控制是否允許跨庫事務,配置如下:

<property name="handleDistributedTransactions">0</property>           

2.2.8、useOffHeapForMerge屬性

該屬性用于配置是否啟用非堆記憶體處理跨分片結果集,1為開啟,0為關閉,Mycat從1.6版本開始支援此屬性。配置如下:

<property name="useOffHeapForMerge">0</property>           

2.2.9全局表一緻性檢測

其原理是通過在全局表中增加_MYCAT_OP_TIME字段來進行一緻性檢測,為BIGINT類型。create語句通過Mycat執行時會自動加上這個字段,其他情況下需要手工添加。1為開啟、0為關閉,Mycat從1.6版本開始支援此屬性。配置如下:

<property name="useGlobleTableCheck">0</property>  <!-- 1為開啟全加班一緻性檢測、0為關閉 -->           

全局表一緻性檢測功能的使用說明及步驟如下。

(1)在所有全局表中增加一個BIGINT類型的内部列,列名為_mycat_op_time(alter table t add column_mycat_op_time bigint [not null default 0]),同時建議在該列建立索引(alter table t add index_op_idx(_mycat_op_time))。

(2)在對全局表進行crud時,可以将内部列當作不存在,建議不要對内部列進行update、insert等操作,否則會在Log日志中出現警告語句“不用操作内部列”。

(3)因為全局表中多了一個内部列,是以在對全局表進行 insert 時必須帶有列名,意味着SQL插入的語句必須是insert into t(id,name)values(xx,xx),而不能使用insert into t values(xx,xx),否則會報列數不對的異常。這樣的操作可能會給開發工程師帶來不便,将來會改善這個問題。

2.2.10、useSqlStat屬性

開啟SQL實時統計,1為開啟、0為關閉。配置如下:

<property name="useSqlStat">0</property>  <!-- 1為開啟實時統計、0為關閉 -->           

3、 schema.xml配置檔案

schema.xml作為Mycat中重要的配置檔案之一,涵蓋了Mycat的邏輯庫、表、分片規則、分片節點及資料源。

3.1、schema标簽

<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">


  </schema>           

schema标簽用于定義Mycat執行個體中的邏輯庫。Mycat可以有多個邏輯庫,每個邏輯庫都有自己的相關配置。可以使用schema标簽來劃分不同的邏輯庫,如果不配置schema标簽,則所有的表配置都會屬于同一個預設的邏輯庫。

<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
    <!-- auto sharding by id (long) -->
    <!--splitTableNames 啟用<table name 屬性使用逗号分割配置多個表,即多個表使用這個配置-->
<!--fetchStoreNodeByJdbc 啟用ER表使用JDBC方式擷取DataNode-->
    <table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
      <childTable name="customer_addr" primaryKey="id" joinKey="customer_id" parentKey="id"> </childTable>
    </table>
    <!-- <table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate"
      /> -->
  </schema>           

如上所示配置了兩個不同的邏輯庫,邏輯庫的概念等同于MySQL資料庫中的Database概念,我們在查詢邏輯庫中的表時,需要切換到該邏輯庫下才可以查詢其中的表。

3.1.1、dataNode屬性

該屬性用于綁定邏輯庫到具體的Database上,

<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
    <!-- auto sharding by id (long) -->
    <!--splitTableNames 啟用<table name 屬性使用逗号分割配置多個表,即多個表使用這個配置-->
<!--fetchStoreNodeByJdbc 啟用ER表使用JDBC方式擷取DataNode-->
    <table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
      <childTable name="customer_addr" primaryKey="id" joinKey="customer_id" parentKey="id"> </childTable>
    </table>
    <!-- <table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate"
      /> -->
  </schema>
  <!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"
    /> -->
  <dataNode name="dn1" dataHost="localhost1" database="db1" />
  <dataNode name="dn2" dataHost="localhost1" database="db2" />
  <dataNode name="dn3" dataHost="localhost1" database="db3" />           

3.1.2、checkSQLschema屬性

當該值設定為true時,如果我們執行語句 ** select*from TESTDB.travelrecord; ** ,則Mycat會把schema字元去掉,把SQL語句修改為**select*from travelrecord;** 可避免發送到後端資料庫執行時報“**(ERROR 1146(42S02):Table'testdb.travelrecord'doesn't exist)”錯誤。

不過,即使設定該值為true,如果語句所帶的schema名字不是schema指定的名字,例如** select*from db1.travelrecord;** ,那麼Mycat并不會删除db1這個字元串。如果沒有定義該庫,則會報錯,在SQL語句中最好不帶這個字段。

3.1.3、sqlMaxLimit屬性

當該屬性設定為某個數值時,每次執行的SQL語句如果沒有加上limit語句,MyCat也會自動在 limit 語句後面加上對應的數值。例如設定值為 100,則執行** select*from TESTDB.travelrecord;** 的效果和執行** select*from TESTDB.travelrecord limit 100;** 的效果一樣。

如果不設定該值,則Mycat預設會把查詢到的資訊全部傳回,是以在正常使用的過程中還是建議設定該值,避免過多的資料傳回。

當然,如果在SQL語句中也顯式地指定了limit的大小則不受該屬性的限制。需要注意的是,如果運作SQL語句的schema為非拆分庫的,那麼該屬性不會生效,需要手動在SQL語句後面添加limit。

3.2、table标簽

table标簽定義了Mycat中的邏輯表,所有需要拆分的表都需要在table标簽中定義。




<table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
  <childTable name="customer_addr" primaryKey="id" joinKey="customer_id" parentKey="id"> </childTable>
</table>           
Mycat進階

3.2.1、name屬性

定義邏輯表的名稱,如同我們在資料庫中執行create table語句的表名一樣,同一個schema标簽中定義的table的名字必須唯一。

3.2.2、dataNode屬性

定義邏輯表所屬的dataNode,該屬性的值需要與dataNode标簽中name屬性的值互相對應。如果需要定義過多的dn,則可以使用如下方法減少配置

<table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate"> </table>           

3.2.3、rule屬性

該屬性用于指定邏輯表要使用的規則的名字,規則的名字在 rule.xml 中定義,必須與tableRule标簽中name屬性的值一一對應。

3.2.4、ruleRequired屬性

該屬性用于指定表是否綁定分片規則,如果配置為true,但沒有配置具體的rule,則程式會報錯。

3.2.5、primaryKey屬性

邏輯表對應真實表的主鍵,例如:分片的規則是使用非主鍵進行分片,那麼在使用主鍵查詢時,就會發送查詢語句到所有配置的dn上;如果使用該屬性配置真實表的主鍵,那麼Mycat會緩存主鍵與具體dn的資訊,再次使用主鍵進行查詢時就不會進行廣播式的查詢了,而是直接把 SQL 語句發送到具體的 dn。但是盡管配置了該屬性,如果緩存并沒有命中,則還是會把該SQL語句發送給所有的dn執行來獲得資料。

3.2.6、type屬性

該屬性定義了邏輯表的類型,目前邏輯表隻有“全局表”和“普通表”兩種類型。

· 全局表:type的值是global,代表全局表。

· 普通表:不指定該值為global的所有表。

3.2.7、autoIncrement屬性

MySQL對于非自增長主鍵使用last_insert_id()是不會傳回結果的,隻會傳回0。是以,隻有對定義了自增長主鍵的表使用last_insert_id()才可以傳回主鍵的值。Mycat目前提供了自增長主鍵功能,但是如果對應的 MySQL 節點上的表沒有定義 auto_increment,那麼在 Mycat 層調用last_insert_id()也是不會傳回結果的。

由于insert操作時沒有帶入分片鍵,是以Mycat會先取下這個表對應的全局序列,然後指派給分片鍵。

如果要使用這個功能,則最好配合資料庫模式的全局序列。使用 autoIncrement="true"指定這個表使用自增長主鍵,這樣Mycat才不會抛出“分片鍵找不到”的異常。使用autoIncrement="false"來禁用這個功能,autoIncrement的值預設為false。

3.2.8、needAddLimit屬性

指定表是否需要自動在每個語句的後面加上limit限制。由于使用了分庫分表,是以資料量有時會特别大。如果恰巧忘記加上數量限制,那麼查詢所有的資料需要一定的時間。

是以,添加該屬性後Mycat将會自動為我們在查詢語句後面加上LIMIT 100。如果語句中有limit限制,則不會重複添加。該屬性預設為true,你也可以把該值設定為false來禁用預設的行為。

3.3、childTable标簽

childTable标簽用于定義E-R分片的子表,通過标簽上的屬性與父表進行關聯。

Mycat進階
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
  <!-- auto sharding by id (long) -->
  <!--splitTableNames 啟用<table name 屬性使用逗号分割配置多個表,即多個表使用這個配置-->
<!--fetchStoreNodeByJdbc 啟用ER表使用JDBC方式擷取DataNode-->
  <table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
    <childTable name="customer_addr" primaryKey="id" joinKey="customer_id" parentKey="id"> </childTable>
  </table>
  <!-- <table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate"
      /> -->
</schema>           

3.3.1、name屬性

定義子表的名稱。

3.3.2、joinKey屬性

插入子表時會使用這個值查找父表存儲的資料節點。

3.3.3、parentKey屬性

parentKey為與父表建立關聯關系的列名。程式首先擷取 joinKey的值,再通過parentKey屬性指定的列名産生查詢語句,通過執行該語句得知父表存儲在哪個分片上,進而确定子表存儲的位置。

3.3.4、primaryKey屬性

同table标簽所描述的。

3.3.5、needAddLimit屬性

3.4、dataNode标簽

dataNode标簽定義了Mycat中的資料節點,也就是我們通常所說的資料分片。一個dataNode标簽就是一個獨立的資料分片。

如下所示為使用名為localhost1的資料庫執行個體上的db1實體資料庫組成一個資料分片,我們通過名字dn1辨別這個分片。

<dataNode name="dn1" dataHost="localhost1" database="db1" />           

dataNode标簽的相關屬性如表所示:

Mycat進階

3.4.1、name屬性

定義資料節點的唯一名字,我們需要在table标簽上應用這個名字,來建立表與分片的對應關系。

3.4.2、dataHost屬性

該屬性用于定義該分片所屬的資料庫執行個體,屬性值引用自 dataHost 标簽上定義的 name屬性。

3.4.3、database屬性

該屬性用于定義該分片所屬資料庫執行個體上的具體的庫,這裡使用兩個次元來定義分片:執行個體+具體的庫。因為每個庫上的表結構是一樣的,是以這樣就可以輕松地對表進行水準拆分。

3.5、dataHost标簽

作為schema.xml中的最後一個标簽,該标簽在Mycat邏輯庫中作為底層标簽存在,直接定義了具體的資料庫執行個體、讀寫分離和心跳語句。

<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
      writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
  <heartbeat>select user()</heartbeat>
  <!-- can have multi write hosts -->
  <writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"
         password="root">
  </writeHost>
  <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>           
Mycat進階

3.5.1、name屬性

唯一辨別dataHost标簽,供上層标簽使用。

3.5.2、maxCon屬性

指定每個讀寫執行個體連接配接池的最大連接配接數。内嵌标簽writeHost、readHost都會使用這個屬性的值來執行個體化連接配接池的最大連接配接數。

3.5.3、minCon屬性

指定每個讀寫執行個體連接配接池的最小連接配接數,初始化連接配接池的大小。

3.5.4、balance屬性

負載均衡類型,目前的取值有如下4種。

· balance="0":不開啟讀寫分離機制,所有讀操作都發送到目前可用的writeHost上。

· balance="1":全部的readHost與stand by writeHost都參與select語句的負載均衡,簡而言之,當為雙主雙從模式(M1→S1,M2→S2,并且 M1 與 M2 互為主備)時,在正常情況下,M2、S1和S2都參與select語句的負載均衡。

· balance="2":所有的讀操作都随機地在writeHost、readHost上分發。

· balance="3":所有的讀請求都随機分發到writeHost對應的readHost上執行,writeHost不負擔讀壓力,注意balance=3隻在Mycat 1.4及之後的版本中有,在Mycat 1.3中沒有。

3.5.5、writeType屬性

負載均衡類型目前的取值有兩種。

· writeType="0":所有的寫操作都發送到配置的第1個writeHost上,writeHost1挂了則切到 writeHost2 上,重新恢複 writeHost1 節點後,不會再切回來,還是以 writeHost2為準,切換記錄在配置檔案dnindex.properties中。

· writeType="1":所有的寫操作都随機地發送到配置的writeHost上,Mycat 1.5版本以後不再推薦使用該值。

3.5.6、switchType屬性

·-1表示不自動切換。

· 1為預設值,表示自動切換。

· 2表示基于MySQL主從同步的狀态決定是否切換,心跳語句如下:

· 3表示基于MySQL Galary Cluster的切換機制(适合叢集,Mycat 1.4.1及以上版本支援),心跳語句如下:

3.5.7、tempReadHostAvailable屬性

如果配置了writeHost屬性,下面的readHost依舊可用,則預設值為0。

3.6、heartbeat标簽

這個标簽内指明了用于後端資料庫進行心跳檢查的語句。

3.7、writeHost标簽、readHost标簽

這兩個标簽都指定Mycat後端資料庫的相關配置,用于執行個體化後端連接配接池。唯一的不同是,writeHost指定寫執行個體,readHost指定讀執行個體,組成這些讀寫執行個體來滿足系統的要求。

在一個dataHost内可以定義多個writeHost和readHost。但是,如果writeHost指定的後端資料庫當機,那麼這個writeHost綁定的所有readHost也将不可用;另一方面,Mycat會自動檢測到writeHost當機,并切換到備用的writeHost上。

Mycat進階

3.7.1、host屬性

用于辨別不同的執行個體,對于writeHost,我們一般使用*M1;對于readHost,我們一般使用*S1。

3.7.2、url屬性

後端執行個體的連接配接位址,如果使用native的dbDriver,則一般為address:port形式;如果使用JDBC 或其他 dbDriver,則需要特殊指定。在使用 JDBC 時,則可以寫為 jdbc:MySQL://localhost:3306/。

3.7.3、user屬性

後端存儲執行個體的使用者名。

3.7.4、password屬性

後端存儲執行個體的密碼。

3.7.5、weight屬性

在readHost中作為讀節點的權重(Mycat在1.4版本以後才有)。

3.7.6、usingDecrypt屬性

同server.xml中usingDecrypt的配置。

4、sequence配置檔案

在實作分庫分表的情況下,資料庫自增主鍵已經無法保證在叢集中是全局唯一的主鍵,是以,Mycat提供了全局sequence,并且提供了本地配置、資料庫配置等多種實作方式。

4.1、本地檔案方式

采用該方式,Mycat将sequence配置到classpath目錄的sequence_conf.properties檔案中。

在sequence_conf.properties檔案中做如下配置:

HOTNEWS.HISIDS=
HOTNEWS.MINID=1001
HOTNEWS.MAXID=2000
HOTNEWS.CURID=1000           

其中HISIDS表示使用過的曆史分段(一般無特殊需要則可不配置),MINID表示最小的ID值,MAXID表示最大的ID值,CURID表示目前的ID值。

要啟用這種方式,則首先需要在server.xml中配置如下參數:

<property name="sequnceHandlerType">0</property>           

注意:sequnceHandlerType配置為0,表示使用本地檔案方式。

insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,'test');           

采用這種方式的缺點是Mycat重新釋出後,配置檔案中的sequence會恢複到初始值;優點是本地加載且讀取速度較快。

4.2、資料庫方式

在資料庫中建立一張名為 sequence 的表,有 sequence 的目前值(current_value)、步長(increment int類型,指每次讀取多少個sequence,假設為K)等資訊。

sequence的擷取步驟如下。

(1)初次使用sequence時,根據傳入的sequence名稱,從資料庫表中讀取current_value、increment到Mycat中,并将資料庫中的current_value修改為current_value+increment的值。

(2)Mycat将讀取到的current_value+increment作為本次使用的sequence值,在下次使用時,sequence自動加1,當使用increment次後,執行與步驟1相同的操作。

(3)Mycat負責維護這張表,用到那些sequence時,隻需要在這張表中插入一條記錄即可。若某次讀取的sequence沒有用完系統就當機了,則本次已經讀取sequence且未使用的值将會被丢棄。

要啟用這種方式,則需要在server.xml中配置如下參數:

<property name="sequnceHandlerType">1</property>           

注意:sequnceHandlerType需要配置為1,表示使用資料庫方式生成sequence。

資料庫配置如下:

(1)建立存放MYCAT_SEQUENCE的表:

create table MYCAT_SEQUENCE (name varchar(50) not null, current_value int not null , increment int not  null default 100,primary key(name) )engine = InnoDB;           

name、current_value和increment分别是sequence的名稱、目前value和增長步長。increment可了解為Mycat從資料庫中批量讀取100個(預設值)sequence來使用,用完這些值後,再從資料庫中讀取。

插入一條sequence語句:

insert into MYCAT_SEQUENCE (name ,current_value ,increment ) values ('GLOBAL',100000,100)           

(2)建立相關的function

Mycat進階
Mycat進階

4.3、本地時間戳方式

4.4、其他方式

4.4.1、使用catlet注解方式

4.4.2、也可以使用ZooKeeper方式實作

4.5、自增長主鍵

Mycat自增長主鍵和傳回生成主鍵ID的實作如下:

(1)MySQL本身對非自增長主鍵使用last_insert_id()隻會傳回0。

(2)MySQL對定義自增長的主鍵才可以用last_insert_id()傳回主鍵的值。

Mycat目前提供了自增長主鍵功能,但是如果對應的 MySQL 節點上的資料表沒有定義auto_increment,那麼在Mycat層調用last_insert_id()也是不會傳回結果的。

正确的配置方式如下:

(1)MySQL定義自增長主鍵。

Mycat進階

(2)Mycat定義主鍵自增。

Mycat進階

在table标簽中增加autoIncrement="true":

(3)Mycat對應sequence_db_conf.properties增加相應的設定。

(4)在資料庫的mycat_sequence表中增加TABLE1表的sequence記錄。

5、zk-create.yaml配置檔案

在介紹配置之前,先介紹幾個概念。Mycat Zone指的是分布于不同地域(Zone)的Mycat Cluster,Zone的命名建議用地理位置來辨別,比如北京聯通機房1。Cluster是Mycat叢集,一個Cluster包含一個或多個Mycat Server。一般來講,一個Zone都有一組主備Mycat負載均衡器LB,LB與同一中心内的Mycat Cluster組成一對多關系,即一個LB可以服務一個中心内的所有Cluster的負載均衡請求,也可以是多個LB,每個負擔不同的Mycat Cluster的流量。此外,建議每個LB都有一個Backup,Backup平時并不連接配接Mycat Cluster,但監測到LB Master下線以後,就立即開始連接配接Mycat Cluster并開始工作。它們的關系大概可以用一組箭頭來表示:Zone→Mycat Cluster→Mycat Server→MySQL,如圖所示:

Mycat進階