Mycat 是一個開源的數據庫中間件,能夠解決分佈式數據庫環境下的大多數問題,如讀寫分離、分庫分表等,除此以外,它還具有如下特性:php
在引入 Mycat 後,全部的客戶端請求須要通過中間件進行轉發上,此時客戶端直接面向的是 Mycat 上的邏輯庫或邏輯表:java
在 Mycat 的配置文件中進行定義,它對應一個或者多個實際的數據庫或數據庫集羣。node
能夠對應一張實際的表,也能夠表示爲多個分片表的集合。按其特性能夠分爲如下四類:mysql
將表按照分片鍵進行分片後,一個表中的全部數據就會被分發到不一樣的數據庫上,這些數據庫節點就稱爲分片節點。git
在 Mycat 的安裝目錄的 conf
目錄下,有如下三個核心配置文件:github
一個 server.xml 的配置示例以下,主要包含如下標籤:算法
<system>
<!-- 0爲須要密碼登錄、1爲不須要密碼登錄 ,默認爲0,設置爲1則須要指定默認帳戶-->
<property name="nonePasswordLogin">0</property>
<property name="charset">utf8</property>
</system>
<firewall>
<whitehost>
<host host="1*7.0.0.*" user="root"/>
</whitehost>
<blacklist check="false">
</blacklist>
</firewall>
<!-- 定義默認帳戶-->
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<!-- 權限設置 -->
<privileges check="true">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
</user>
<!-- 定義默認帳戶-->
<user name="user">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>
複製代碼
在上面的示例中,用戶配置的是明文密碼,這樣會存在安全隱患,所以 Mycat 也支持對密碼進行加密,示例以下:sql
# 該jar包在Mycat安裝目錄的lib目錄下
shell > java -cp Mycat-server-1.6.7.1-release.jar io.mycat.util.DecryptUtil 0:root:123456
GO0bnFVWrAuFgr1JMuMZkvfDNyTpoiGU7n/Wlsa151CirHQnANVk3NzE3FErx8v6pAcO0ctX3xFecmSr+976QA==
複製代碼
能夠將明文密碼替換爲加密密碼,但此時還需在對應用戶的 user 標籤下增長以下配置,表明啓用加密功能:shell
<property name="usingDecrypt">1</property>
複製代碼
下面是一個示例的 schema.xml 配置文件,主要包含如下標籤:數據庫
select * from db.table1
等查詢時,會自動修改成 select * from table1
。sqlMaxLimit 用於限制返回數據的行數。<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- mod-long 分片規則在下文的 rule.xml中定義 -->
<table name="order" dataNode="dn1,dn2,dn3" rule="mod-long" />
</schema>
<dataNode name="dn1" dataHost="cluster01" database="order_db" />
<dataNode name="dn2" dataHost="cluster02" database="order_db" />
<dataNode name="dn3" dataHost="cluster03" database="order_db" />
<dataHost name="cluster01" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<!-- 心跳檢查 -->
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.226:3306" user="root" password="123456">
<readHost host="salve" url="192.168.0.227:3306" user="root" password="123456" />
</writeHost>
</dataHost>
...... 省略 cluster02,cluster03
複製代碼
這裏解釋一下 dataHost 標籤的相關屬性:maxCon 表示最大鏈接數,minCon 表示最小鏈接數,dbType 表示數據庫類型 ( 如 mysql、oracle 等),其餘屬性都有多個可選值,具體以下:
dbDriver:數據庫類型,可選的值有 native 和 JDBC,若是是 mysql,maridb,postgresql 等數據庫,直接可使用 native 便可。其餘數據庫則須要將對應的驅動包拷貝到 Mycat 安裝目錄的 lib 目錄下,並寫上完整的驅動類的類名。
switchType:
balance:
balance="0"
:不開啓讀寫分離機制,全部讀請求都發送到當前可用的 writeHost 上。balance="1"
:所有的 readHost 與 stand by writeHost 參與讀操做。stand by writeHost 一般指的是雙主複製中處於 stand 狀態的主節點,即假設集羣複製架構爲 Master1 -> Slave1,Master2 -> Slave2,而且 M1 與 M2 互爲主備 ),此時 Master2 ,Slave1,Slave2 都會參與讀的負載。balance="2"
:全部讀請求隨機在 writeHost、 readhost 上進行分發。balance="3"
:全部讀請求隨機分發到 writeHost 對應的 readhost 執行,writerHost 不負擔讀壓力。rule.xml 文件中定義的是分片規則,主要包含如下標籤:
<tableRule name="mod-long">
<rule>
<columns>order_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- nodes 節點的數量-->
<property name="count">3</property>
</function>
複製代碼
Mycat 內置支持十幾種分片算法,如 取模分片,枚舉分片,範圍分片,字符串 hash 分片,一致性 hash 分片,日期分片等。關於這些分片算法的詳細說明能夠參考官方文檔:Mycat 官方指南
Mycat 讀寫分離的配置很是簡單,只須要經過配置 balance,writeHost 和 readHost 就能夠實現,示例以下:
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<!-- 能夠配置多個writeHost -->
<writeHost host="Master" url="hostname1:3306" user="root" password="123456">
<!-- 能夠配置多個readHost-->
<readHost host="Slave" url="hostname2:3306" user="root" password="123456" />
</writeHost>
</dataHost>
複製代碼
可是須要注意的是如上的配置仍是會存在單點問題,由於只有一個 writeHost ,Mycat 支持配置多個 writeHost,示例以下:
<writeHost host="Master" url="hostname1:3306" user="root" password="123456">
<!-- 能夠配置多個readHost-->
<readHost host="Slave1" url="hostname2:3306" user="root" password="123456" />
<readHost host="Slave2" url="hostname3:3306" user="root" password="123456" />
</writeHost>
<writeHost host="Slave3" url="hostname4:3306" user="root" password="123456" />
複製代碼
以上是 Mycat 官方指南中給出的配置,即在一主三從的複製架構下,能夠選擇其中一個 Slave 爲備用的寫入節點,此時當 Master 節點宕機後,會繼續在該備用節點執行寫入操做。這個配置和架構存在如下兩個問題:
基於以上兩個緣由,若是想要實現高可用,並不建議配置多個 writeHost ,而是配置一個 writeHost ,但其指向的是虛擬的讀 IP 地址,此時複製架構由 MMM 或者 MHA 架構來實現,並由它們來提供虛擬機的讀 IP。
綜合以上所有內容,這裏給出一個分庫分表的示例,其架構以下:
如上圖所示,這裏模擬的是一個電商數據庫,並對其執行分庫分表操做:
爲節省篇幅,以上全部測試表和測試庫的創建語句單獨整理至:ec_shop.sql 。分庫分表的具體操做以下:
這裏新增一個 Mycat 用戶,並定義其管理的邏輯數據庫爲 ec_shop,另外使用 fakeMySQLVersion 來定義你所須要模擬的 MySQL 數據庫的版本。若是沒有特殊需求, Mycat 自帶的 server.xml 中的其餘配置可不作更改:
<system>
<property name="fakeMySQLVersion">5.7.20</property>
</system>
<user name="heibaiying">
<property name="password">
B+BlA/U17pjyzHslglpDgYUxpgqK8qC62IRt/S74RBW6R7dZFJAXVb5tJDgmhzM4fj14MMhLnNmvKko6D73+iA==
</property>
<property name="schemas">ec_shop</property>
<property name="usingDecrypt">1</property>
</user>
複製代碼
這裏使用 childTable 來將訂單表和訂單明細表定義爲 ER 表,避免跨分片查詢。並將地址表 area_info 使用 type="global"
聲明爲全局,一樣也是爲了不跨分片查詢:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="ec_shop" checkSQLschema="false" sqlMaxLimit="100">
<table name="customers" primaryKey="customer_id" dataNode="dn01"/>
<table name="products" primaryKey="product_id" dataNode="dn02"/>
<table name="orders" primaryKey="order_id" dataNode="dn03,dn04" rule="mod-long">
<childTable name="order_detail" primaryKey="order_detail_id" joinKey="order_id" parentKey="order_id"/>
</table>
<table name="area_info" primaryKey="area_id" type="global" dataNode="dn01,dn02,dn03,dn04"/>
</schema>
<dataNode name="dn01" dataHost="host01" database="ec_shop_customer"/>
<dataNode name="dn02" dataHost="host02" database="ec_shop_product"/>
<dataNode name="dn03" dataHost="host03" database="ec_shop_order"/>
<dataNode name="dn04" dataHost="host04" database="ec_shop_order"/>
<dataHost name="host01" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.226:3306" user="root" password="123456"/>
</dataHost>
<dataHost name="host02" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.227:3306" user="root" password="123456"/>
</dataHost>
<dataHost name="host03" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.228:3306" user="root" password="123456"/>
</dataHost>
<dataHost name="host04" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.229:3306" user="root" password="123456"/>
</dataHost>
</mycat:schema>
複製代碼
定義訂單表所使用的分片規則,這裏使用取模算法做爲示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
<tableRule name="mod-long">
<rule>
<columns>order_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- nodes 節點的數量-->
<property name="count">2</property>
</function>
</mycat:rule>
複製代碼
這裏我後端使用的數據庫是 MySQL 8.0.17 ,相比於使用 MySQL 5.6 或 5.7 來整合 Mycat ,多了一些注意事項,主要以下:
即使你在 server.xml 中正確的配置了用戶名和密碼,但在使用 mysql shell 鏈接 Mycat 時,仍是會拋出密碼錯誤的異常:
Access denied for user 'xxx', because password is error
複製代碼
這是因爲從 MySQL 8.0.4 開始使用 caching_sha2_password
做爲認證的插件,而以前版本的插件爲 mysql_native_password
,我在測試中使用的 Mycat 版本爲 1.6.7,它並不支持 caching_sha2_password
。所以在登陸時候須要經過 --default_auth
來指定使用原有的認證插件:
# 1.6.7 版本 Mycat 默認的鏈接端口號爲 8066
mysql -uheibaiying -p -h127.0.0.1 -P8066 --default_auth=mysql_native_password
複製代碼
Mycat 和 MySQL 都正常啓動,可是在 Mycat 上執行 SQL 語句失敗,提示無效的數據庫。此時能夠查看 Mycat logs 目錄下的 mycat.log 文件,一般會出現下面所示的異常:
(io.mycat.backend.mysql.nio.MySQLConnectionAuthenticator.handle(MySQLConnectionAuthenticator.java:91)
- can't connect to mysql server ,errmsg:Client does not support authentication protocol requested by
server; consider upgrading MySQL client MySQLConnection
複製代碼
這和上面是一樣的緣由,都是由於認證插件而致使的問題。此時須要修改帳戶所使用的認證插件:
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'xxxx';
FLUSH PRIVILEGES;
複製代碼
修改後可使用以下命令進行查看:
mysql> SELECT Host,User,plugin FROM mysql.user;
+---------------+------------------+-----------------------+
| Host | User | plugin |
+---------------+------------------+-----------------------+
| % | root | mysql_native_password |
| 192.168.200.% | repl | mysql_native_password |
| localhost | mysql.infoschema | caching_sha2_password |
| localhost | mysql.session | caching_sha2_password |
| localhost | mysql.sys | caching_sha2_password |
| localhost | root | mysql_native_password |
+---------------+------------------+-----------------------+
複製代碼
以後再重啓 Mycat 服務就能夠正常鏈接。
Mycat 官方指南:www.mycat.io/document/my…
更多文章,歡迎訪問 [全棧工程師手冊] ,GitHub 地址:github.com/heibaiying/…