30-29 之資料庫層擴展中間件 - MyCAT 的淺淺談
it 鐵人賽 2019 mysql
Lastmod: 2019-12-16

正文開始

前面幾篇文章中,咱們提到了如何擴展資料庫層級服務,讓它可以接更多的客,但是這些擴展方法中,都有提到一個『 中間件 』來使用,接下後本篇文章中,咱們將介紹其中一種比較常見的中間件 :

MyCAT

本篇文章分為以下幾個章節 :

  • MyCAT 基本概念
  • MyCAT 的各種架構實現配置
  • 使用 Docker 來實現 MyCAT 讀寫分離

MyCAT 基本概念

在資料庫中間件中,事實上分為兩種類型 :

  • proxy
  • smart-client

它們兩個的基本差別如下圖 1 所示,proxy 是一個外部的服務,所有的應用都會透過這個 proxy 服務來操作資料庫。

而 smart-client 概念就是包在應用層中,當成一個 sdk 概念的程式碼。

圖 1 : proxy vs smart-client

而其中 mycat 就是屬於 proxy 的其中一種應用。

~ 小知識 ~

現在幾個比較可以說的出名字的中間件有 :

proxy : cobar、mycat、mysql-router、atlas、vitess smart-client : 大部份語言有實現簡單版的,而如果是支援比較多功能的有 sharding-jdbc、tddl。

有興趣的友人可以自已查查來比較看看。

這裡問一下,那一種比較好呢 ?

首先咱們先說說 smart-client 的優點 :

  • 實現簡單 : 大部份的語言都有相對應簡單的實現套件,例如大部份 orm 套都只要設定好 master 與 slave 然後開啟讀寫分離,就會自動的處理 ( ex. django orm、laravel eloquent model )。
  • 沒有服務壞掉風險 : proxy 是一個服務,所以你需要考慮到它是否有高可用,而 smart-client 則否,它只是個 sdk。

Smart-client 缺點 :

  • 比較深入的功能需自已實現 : 例如分散式事務、負載平衡這些,大部份的套件都沒有,真想要只能自已在這些套件上,在封裝一個層級來自已實現。
  • 版本問題 : 有可能不同服務上,有不同的版本,這也代表如果一個版本有 bug,那就所有的應用都要升級,當服務多時,你要如何確保所有服務都有升級呢 ?
  • 語言問題 : 只有與中間件 sdk 同語言的才能使用。

總結來說,如果你的資料庫層級只到了『 讀寫分離 』的情境,那建議是只要使用 smart-client 模式,而是是直接使用各語言有提供的套件就好,而如果到了『 分庫分表 』而且公司內有很多人會使用,那就真的要考慮 proxy 了。

MyCat 特性

mycat 是一個由阿里開發的 cobar 演變來的資料庫的中間件服務,它是屬於『 proxy 類型 』,它基本上有以下的特點 :

  • 支持讀寫分離、分庫分表。
  • NIO 的實現。
  • 支持分散式事務 ( XA 協議的協調者 )
  • 支持跨庫 join,但是限兩表

首先第一點 mycat 支持讀寫分離、分庫分表,寫讓咱們應用端一樣照常的寫一樣 sql,然後內部會自動幫處理這些事情。

第二點 mycat 他本身是 nio 也就所謂的 non-blocking i/o 機制,就是咱們下面這篇文章中所提到的 reactor 模式,這也代表太可以處理大量的請求。

30-07 之應用層的 I/O 優化 - 非阻塞 I/O 模型 Reactor

第三點支持分散式事務,也就是咱們應用端一樣像往常一樣使用事務,而 mycat 就會自動幫咱們變成分散式事務。

30-28 之資料庫層擴展難題 - MySQL 分散式事務處理

第四點跟庫 join,這可以讓咱們應用端寫 join 和平常一樣,但是注意,它只限兩表。

MyCAT 的各種架構實現配置

基本配置

  1. 這個部份定義了 mycat 的邏輯資料庫,像咱們下 sql 時有時後需要補一個 use xxxdb 的這種語句時,以下範例來看,咱們下的就是 use masterdb 這樣。
  2. 定義 mycat 的邏輯資料節點,它會使用連到對應對 dataHost 與 database。
  3. 定義 mycat 的邏輯 host,並且也定義好這個 host 的模式,例如是不是負載均衡等。
  4. 定義實際使用的資料庫位置。

其中 dataHost 有些參數注意一下。

  • maxCon : 代表最大連線量,某些方面這個可以說是一個連線池。
  • balance : 負載均衡的類型
  • 0 : 不開啟讀寫分離,就都寫到 writeHost。
  • 1 : 雙主重模式用,這個有點複雜。
  • 2 : 所有讀操作,都隨機分配在 writeHost 與 readHost。
  • 3 : 所有讀操作,都送到 read,writeHost 只處理寫操作。
  • writeType : 寫的負載均衡類型
  • 0 : 所有寫操作都送到第一台 writeHost,當它掛了,會自動切換為第二台。
  • 1 : 所有寫操作隨機送到某台 writeHost。
  • 2 : 不執行寫的操作。
  • switchType : 主從切換模式,但是有點看不太懂它的類型
  • slaveThreshold : 主從延遲 hreshold,也就是說主從的延遲超過這個時間,這此從庫不會參於這次分配。
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

	<!-- 1. 定義 mycat 邏輯資料庫 -->
	<schema name="masterdb" checkSQLschema="false" sqlMaxLimit="100" dataNode="db1" />

	<!-- 2. 定義 mycat 邏輯資料操作節點 -->
    <!-- 不過比較特別的是 database 它是指實際要連到的預設資料庫 -->
	<dataNode name="db1" dataHost="xxx-db" database="test" />

	<!-- 3. 定義 mycat 邏輯 host   -->
	<dataHost name="xxx-db" maxCon="1000" minCon="10" balance="1"
			  writeType="0" dbType="mysql" dbDriver="native" switchType="-1" slaveThreshold="100">
		<heartbeat>select user()</heartbeat>
		<!-- 4. 定義實際使用資料庫位置 -->
		<writeHost host="m1" url="192.168.0.10:3306" user="root">
		</writeHost>
	</dataHost>

</mycat:schema>

這裡簡單畫一下配置的關係圖,如下圖 2 所示。

圖 2 : mycat 配置圖關係

MyCAT 讀寫分離實現

下面為讀寫分離的配置,差別在於 dataHost 的的設定。

  • balance 改為 3 : 代表所有讀的操作,會送到 wrtie 裡面的 read 機讀取。
  • 增加 readHost 配置。

當要使用時,你可以下以下的 sql 來取得資料。

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

	<schema name="db" checkSQLschema="false" sqlMaxLimit="100" dataNode="db-node" />

	<dataNode name="db-node" dataHost="db-host" database="test" />

    <!-- 以下有變  -->
	<dataHost name="db-host" maxCon="1000" minCon="10" balance="3"
			  writeType="0" dbType="mysql" dbDriver="native" switchType="-1" slaveThreshold="100">
		<heartbeat>select user()</heartbeat>
		<!-- !. 定義實際使用資料庫位置 (寫)  -->
		<writeHost host="writer" url="192.168.0.10:3306" user="root" password="pass">
			<!-- !. 定義實際使用資料庫位置 (讀)  -->
			<readHost host="reader" url="192.168.0.20:3306" user="root" password="pass" />
		</writeHost>
	</dataHost>

</mycat:schema>

下圖 3 為 mycat 讀寫分離的配置概念圖。

圖 3 : mycat 讀寫分離的配置概念圖

~ 注意事項 ~ 有個東西要注意一下,mysql 的資料同步是要自已實現,mycat 不能會幫你處理喔。

MyCAT 分庫實現

分庫的配置就是什麼都建立兩組,然後用戶端要打時,自已決定去打那一個位置。

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
        
        <schema name="Order" checkSQLschema="false" sqlMaxLimit="100" dataNode="order-node"/>
        <schema name="User" checkSQLschema="false" sqlMaxLimit="100" dataNode="user-node"/>
        
        <dataNode name="order-node" dataHost="order-host" database="db1" />
        <dataNode name="user-node" dataHost="user-host" database="db2" />
        
        <dataHost name="order-host" maxCon="1000" minCon="10" balance="2"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="host1" url="192.168.1.199:3306" user="root" password="123456"/>
        </dataHost>
        <dataHost name="user-host" maxCon="1000" minCon="10" balance="2"
                          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                <writeHost host="host2" url="192.168.1.166:3306" user="root" password="123456"/>
        </dataHost>
</mycat:schema>

圖 4 : 分庫分表 mycat 配置概念圖

使用 Docker 來實現 MyCAT 讀寫分離

請至筆者的 github 抓取這個專案,不過先說一下,這個 docker-compose 是抓了其它 mycat 的配置來修修改改,改改修修,雖然可以動,不過裡面還很亂,閒時會慢慢的用乾淨點。

馬克的 github-docker-tools

其中需要注意看的檔案如下 :

  • docker-compose : 所有服務的配置。
  • config/hosts : 將服務的 host 進行指定。
  • config/mycat/server.xml : mycat 實際服務的權限配置。
  • config/mycat/schema.xml : mycat 資料庫配置

基本使用

移至 mycat-master-slave 這個資料夾裡面,然後在執行以下指令。

docker-compose up

等待一段時間以後,你可以使用以下的指令進行連線,正常應該是可以成功的連進去,其中這個位置是連到 mycat 喔。

mysql -h127.0.0.1 -P8066 -uroot -ppass

host: 127.0.0.1:8066
username: root
password: pass

然後你就可以在裡面下指令了,一開始應該是空的,那你可以使用以下指令建立一下測試資訊。

use db;
create table user(
   id INT NOT NULL AUTO_INCREMENT,
   name VARCHAR(20) NOT NULL,
   age INT NOT NULL,
   PRIMARY KEY ( id )
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

INSERT INTO user (name, age) VALUES ('A-Mark', 18);
INSERT INTO user (name, age) VALUES ('B-Jack', 10);
INSERT INTO user (name, age) VALUES ('C-Ian', 36);
INSERT INTO user (name, age) VALUES ('E-Jiro', 30);
INSERT INTO user (name, age) VALUES ('D-Fucc', 27);
INSERT INTO user (name, age) VALUES ('C-Mark', 46);

然後你就可以正常的執行 sql 操作囉。

SELECT * FROM user;

Docker-Compose 檔說明

這裡使用 docker-compose 來配置一個模擬的 mycat 讀寫分離情境,其中有三個服務 :

  • mysql_write : 用來專門寫入的 mysql
  • mysql_read : 用來專門讀取的 mysql
  • mycat : 就是資料庫中件間服務

有幾個重點說一下。

首先讀與寫的 mysql 服務都是使用 bitnami/mysql:5.7 ,是因為它可以很簡單的讓我可以使用兩台資料庫資料同步的功能,不然用原生的很配置要寫很常。

然後這裡還有設置 db 的 docker network,不知道為什麼嘗試的時後直接使用 link 會失敗,詳細原因就待查,反正這只是淺淺的範例。

version: '3'

services:
  mysql_write:
    image: 'bitnami/mysql:5.7' 
    ports:
      - 33061:3306
    networks:
      db:
        ipv4_address: 192.168.0.10
    environment:
      - MYSQL_REPLICATION_MODE=master
      - MYSQL_REPLICATION_USER=repl_user
      - MYSQL_USER=mark
      - MYSQL_DATABASE=test
      - ALLOW_EMPTY_PASSWORD=yes
      - MYSQL_ROOT_PASSWORD=pass
    volumes:
      - ./mysql/write/:/etc/mysql/conf.d
      - ./config/hosts:/etc/hosts:ro
  mysql_read:
    image: 'bitnami/mysql:5.7' 
    ports:
      - 33062:3306
    networks:
      db:
        ipv4_address: 192.168.0.20
    environment:
      - MYSQL_REPLICATION_MODE=slave
      - MYSQL_REPLICATION_USER=repl_user
      - MYSQL_USER=mark
      - MYSQL_DATABASE=test
      - MYSQL_MASTER_HOST=192.168.0.10
      - MYSQL_MASTER_PORT_NUMBER=3306
      - MYSQL_MASTER_ROOT_PASSWORD=pass
      - ALLOW_EMPTY_PASSWORD=yes
    volumes:
      - ./mysql/read/:/etc/mysql/conf.d
      - ./config/hosts:/etc/hosts:ro
  mycat:
    image: 'qiyue/mycat:1.6.5'
    volumes:
      - ./config/mycat/:/usr/local/mycat/conf/
      - ./log/mycat-logs/:/mycat/logs/:rw
    ports:
      - "8066:8066"
      - "9066:9066"
    networks:
      db:
        ipv4_address: 192.168.0.5
    ulimits:
      nproc: 65535
    hostname: mycat
    restart: always
networks:
    db:
      driver: bridge
      ipam:
        driver: default
        config:
          - subnet: 192.168.0.0/24

然後還有這三個服務都配置在 db 這個 network 內網中,然後這裡比較特別的是,會將這三台服務配置 config/hosts 也就是讓人在內網中打 writer 就會對應到 192.168.0.10 這個位置,概念就像是 localhost 指向。

原本想嘗試在 mycat 中直接配置 ip,但嘗試了一下不太行,原因還是待查,難怪看起他人的配置都是要配置 config/hosts。

127.0.0.1	localhost
192.168.0.10	writer
192.168.0.20	reader
192.168.0.5	mycat

這裡就淺淡到這囉。詳細的內容自已去 github 看 code 吧,因為嚴格來說算最後的一天,我疲勞了。

結論與心得

本篇文章咱們介紹了其中一種資料庫中件間『 MyCAT 』。然後咱們非常淺的理解一下它的使用範例,與使用 docker 來實際上將它建立出來。

那為啥會選他來介紹,只因為它資料比較多…… 到了今天已經沒有多餘的體力可以更深入的調查與研究了,未來如果有機會在開篇來真的詳細談談這東西。

雖然才 29 天,但基本上就和最後一天沒差多少了,雖然參加過不少次,但是真的最後寫到很疲勞,終於要結束了……

參考資料

comments powered by Disqus