簡介
MyCat 是目前最流行的基于 java 語言編寫的數(shù)據(jù)庫中間件,是一個實現(xiàn)了 MySQL 協(xié)議的服務(wù)器沃于,前端用戶可以把它看作是一個數(shù)據(jù)庫代理涩咖,用 MySQL 客戶端工具和命令行訪問海诲,而其后端可以用 MySQL 原生協(xié)議與多個 MySQL 服務(wù)器通信,也可以用 JDBC 協(xié)議與大多數(shù)主流數(shù)據(jù)庫服務(wù)器通信檩互,其核心功能是分庫分表特幔。配合數(shù)據(jù)庫的主從模式還可實現(xiàn)讀寫分離。官方學(xué)習(xí)地址
MyCat2 已經(jīng)可以使用闸昨,目前僅支持java8
垂直分庫 就是微服務(wù)的形式蚯斯,把一個服務(wù)單元拆到一個庫中
垂直分表 把一個大表,字段多的表饵较,拆分成多個小表拍嵌,或按照查詢邏輯拆分主要信息表或次要信息表
水平分庫 把數(shù)據(jù)庫集群,數(shù)據(jù)通過hash 或 取模 的方式散到你的數(shù)據(jù)庫節(jié)點中
水平分表 只針對大表進行切分循诉,按一定的邏輯進行切分
功能
1.讀寫分離
2.數(shù)據(jù)分片横辆,垂直拆分(分庫)、水平拆分(分表)茄猫、垂直+水平拆分(分庫分表)
3.多數(shù)據(jù)源整合
原理
MyCat 的原理種最重要的一個動詞時”攔截“狈蚤,它攔截了用戶發(fā)送過來的SQL語句,首先對SQL語句做了一些特定的分析:如分片分析划纽、路由分析脆侮、讀寫分離分析、緩存分析等阿浓,然后將此SQL發(fā)往后端的真實數(shù)據(jù)庫他嚷,并將返回的結(jié)果做適當(dāng)?shù)奶幚恚罱K再返回給用戶
這種方式把數(shù)據(jù)庫的分布式從代碼中解耦出來芭毙,程序員察覺不出來后臺使用MyCat還是MySQL
安裝及操作
github 地址,這里要找的是打包過的文件下載卸耘,不要下載源碼退敦,除非你要看源碼或改。
# 下載
wget http://dl.mycat.org.cn/1.6.7.6/20210730131311/Mycat-server-1.6.7.6-release-20210730131311-linux.tar.gz
#解壓
tar -xvf Mycat-server-1.6.7.6-release-20210730131311-linux.tar.gz
# 移動到指定文件夾
mv mycat/ /opt/software/
啟動
# 控制臺啟動蚣抗,確認(rèn)啟動過程不會出錯
bin/mycat console
# 后臺啟動
bin/mycat start
配置文件介紹:
1.schema.xml 定義邏輯庫侈百,表、分片節(jié)點等內(nèi)容
2.rule.xml 定義分片規(guī)則
3.server.xml 定義用戶以及系統(tǒng)相關(guān)變量翰铡,如端口等
MyCat連接MySQL換驅(qū)動成8.X
- 上傳 mysql-connector-java-8.0.19.jar 到 mycat 的 lib 目錄下
- 給文件賦權(quán) chmod 777 mysql-connector-java-8.0.19.jar
- 重新啟動钝域。mycat 會自動檢測驅(qū)動,所以不需要我們操心锭魔,如果遇到如下提示例证,則替換驅(qū)動成功
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
登錄MyCat
修改 server.xml
<!-- 連接mycat的用戶信息 -->
<user name="mycat" defaultAccount="true">
<!-- 密碼 -->
<property name="password">123456</property>
<!-- 邏輯庫名稱 -->
<property name="schemas">TESTDB</property>
<!-- 默認(rèn)邏輯庫 -->
<property name="defaultSchema">TESTDB</property>
<!--No MyCAT Database selected 錯誤前會嘗試使用該schema作為schema,不設(shè)置則為null,報錯 -->
<!-- 表級 DML 權(quán)限設(shè)置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
配置個基本的連接迷捧,以便登上去查庫中的表
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 在server.xml中被指定的邏輯庫 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
</schema>
<!-- 對應(yīng)schema中的 DataNode织咧,database 是真正物理上的數(shù)據(jù)庫 -->
<dataNode name="dn1" dataHost="host1" database="my_sql_test" />
<!-- name對應(yīng) dataNode 的 datatHost -->
<dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
</mycat:schema>
登錄
# 登錄運維管理窗口
mysql -u賬號 -p密碼 -P 9066 -h IP
# 查看數(shù)據(jù)庫
show database;
# 查看命令
show @@help
# 登錄數(shù)據(jù)窗口
mysql -u賬號 -p密碼 -P 8066 -h IP
# 如
mysql -umycat -p123456 -P8066 -h192.168.81.104 --default-auth=mysql_native_password
--default-auth=mysql_native_password 是因為 mycat 使用的是5.7以下的加密方式登錄
搭建讀寫分離
本文這里使用二進制復(fù)制胀葱,其余復(fù)制可以參考我以前所寫Mysql-8.0.2 主從復(fù)制(GTID)、Mysql-8.0.2 高可用MGR笙蒙,建議可以先使用二進制方式抵屿。
主機配置
修改 /etc/my.cnf
# 主服務(wù)器唯一ID
server-id=1
# 啟用二進制日志
log-bin=mysql-bin
# 設(shè)置不要復(fù)制的數(shù)據(jù)庫(可設(shè)置多個)
binlog-ignore-db=mysql
# binlog-ignore-db=information_schema
# 設(shè)置需要復(fù)制的數(shù)據(jù)庫,不配置代表所有,但切記配置不要復(fù)制的庫捅位,建議基礎(chǔ)庫都不排除掉
binlog-do-db=test1
# 設(shè)置binlog格式
binlog_format=STATEMENT
binlog 日志有三種格式
- STATEMENT 會把sql寫到日志中轧葛,并且?guī)в袝r間,所以從的時間和主的時間必須一致
- ROW 不記錄寫SQL艇搀,只記錄每行的改變
- MIXED 切換 STATEMENT 和 ROW
從機配置
server-id=2
# 啟用中繼日志
relay-log=mysql-relay
重啟主機朝群、從機服務(wù)
授權(quán)
主機
# 創(chuàng)建用戶
CREATE USER 賬號@'%' IDENTIFIED BY '密碼';
# 授權(quán)
GRANT REPLICATION SLAVE ON *.* TO 賬號@'%';
# 查看主機的狀態(tài)
mysql> show master status;
+---------------+-----------+--------------+------------------+-----------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+-----------+--------------+------------------+-----------------------------------------------+
| binlog.000021 | 372357328 | test1 | mysql | |
+---------------+-----------+--------------+------------------+-----------------------------------------------+
- File binlog 文件
- Position 接入位置,372357328 之前的數(shù)據(jù)不會復(fù)制
- Binlog_Do_DB 要復(fù)制的庫
- Binlog_Ignore_DB 不復(fù)制的庫
從機
建議做之前操作中符,可以把之前的主從配置信息干掉耐床,也就清潔一下夜涕。
# 停止復(fù)制
stop slave;
# 重置復(fù)制信息
reset master;
開始配置
# 配置復(fù)制信息
CHANGE MASTER TO
MASTER_HOST = '192.168.81.104',
MASTER_PORT = 3306,
MASTER_USER = 'repl',
MASTER_PASSWORD = 'Repl@123',
MASTER_LOF_FILE='binlog.000021',
MASTER_LOF_POS=372357328;
# 開始復(fù)制
start slave;
# 查看從機狀態(tài)
show slave status\G;
mycat配置
修改 schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 在server.xml中被指定的邏輯庫 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
</schema>
<!-- 對應(yīng)schema中的 DataNode,database 是真正物理上的數(shù)據(jù)庫 -->
<dataNode name="dn1" dataHost="host1" database="my_sql_test" />
<!-- name對應(yīng) dataNode 的 datatHost -->
<dataHost name="host1" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021">
<!-- 定義讀主機 -->
<readHost host="hostS1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021" ></readHost>
</writeHost>
</dataHost>
</mycat:schema>
dataNode 的 database 屬性,為MySQL 真實的數(shù)據(jù)庫名稱停撞。
dataHost 的 balance 屬性,通過此屬性配置讀寫分離的類型
- balance="0" 不開啟讀寫分離機制瘫拣,所有讀操作都發(fā)送到當(dāng)前可用的 writeHost
- balance="1" 全部的 readHost 與 stand by writeHost 參與 select 語句的負(fù)載均衡搏明,簡單來說,當(dāng)雙主雙從模式(M1->S1,M2->S2郭膛,并且M1與M2互為主備)晨抡,正常情況下,M2则剃,S1耘柱,S2都參與 select 語句的負(fù)載均衡。
- balance="2" 所有讀操作都隨機的在 writeHost棍现、readHost 上分發(fā)
- balance="3" 所有讀請求隨機的分發(fā)到 readHost 執(zhí)行调煎,writeHost 不負(fù)擔(dān)讀壓力
dataHost 的 writeType 屬性:
- writeType="0" 所有寫操作發(fā)送到配置的第一個 writeHost,第一個掛了切到還生存的第二個
- writeType="1" 所有操作都隨機的發(fā)送到配置的 writeHost己肮,1.5以后廢棄不推薦
writeType 重新啟動后以切換后的為準(zhǔn)士袄,切換記錄在配置文件中:dnindex.properties
dataHost 的 switchType 屬性:
- switchType="1" 默認(rèn)值,自動切換
- switchType="-1" 不自動切換
- switchType="2" 基于MySQL 主從同步的狀態(tài)決定是否切換
雙主雙從
主(M)1配置
在以上的主配置中添加
# 在作為從數(shù)據(jù)庫的時候谎僻,有寫入操作也要更新二進制日志文件
log-slave-updates
# 表示自增長字段每次遞增的量娄柳,指自己字段的起始值,其默認(rèn)值是1艘绍,取值范圍是1~65535(主要是區(qū)分M1赤拒,M2的自增)
auto-increment-increment=2
# 表示自增長字段從哪個數(shù)開始,指字段一次遞增多少鞍盗,他的取值范圍是1~65535
auto-increment-offset=1
主(M)2配置
和主1配置不同的是
server-id=3
# 表示自增長字段從哪個數(shù)開始需了,指字段一次遞增多少跳昼,他的取值范圍是1~65535
auto-increment-offset=2
兩臺主機都要創(chuàng)建一個復(fù)制用戶并授權(quán)。
從(S)1肋乍,2
延用之前的從配置鹅颊,但是注意 server-id 一定不一樣,配置完成后需要執(zhí)行以上從機步驟墓造。關(guān)系為 S1->(復(fù)制)M1,S2->M2堪伍。
主1主2相互復(fù)制
主1和主2相互執(zhí)行命令
CHANGE MASTER TO
MASTER_HOST = '192.168.81.xxx',
MASTER_PORT = 3306,
MASTER_USER = 'repl',
MASTER_PASSWORD = 'Repl@123',
MASTER_LOF_FILE='binlog.xxxx',
MASTER_LOF_POS=xxxxx;
# 啟動
start slave;
修改MyCat文件
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 在server.xml中被指定的邏輯庫 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
</schema>
<!-- 對應(yīng)schema中的 DataNode,database 是真正物理上的數(shù)據(jù)庫 -->
<dataNode name="dn1" dataHost="host1" database="my_sql_test" />
<!-- name對應(yīng) dataNode 的 datatHost -->
<dataHost name="host1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021">
<!-- 定義讀主1從1機 -->
<readHost host="hostS1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021" ></readHost>
</writeHost>
<!-- 定義寫主2機 -->
<writeHost host="hostM2" url="jdbc:mysql://192.168.81.106:3306" user="dev_fqr" password="Dev@fqr2021">
<!-- 定義讀主2從2機 -->
<readHost host="hostS2" url="jdbc:mysql://192.168.81.107:3306" user="dev_fqr" password="Dev@fqr2021" ></readHost>
</writeHost>
</dataHost>
</mycat:schema>
垂直切分(分庫)
垂直切分就是微服務(wù)的分庫操作觅闽,如 用戶表帝雇、積分表等在一個庫,被劃分為用戶服務(wù)蛉拙;訂單表尸闸、明細(xì)表、訂單快照表等在一個庫中孕锄,被劃分為訂單服務(wù)吮廉。
修改schema.xml配置
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 在server.xml中被指定的邏輯庫 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
<!-- 配置表,所有對 customer 表的操作都會走 dn2 -->
<table name="customer" dataNode="dn2"></table>
</schema>
<!-- 對應(yīng)schema中的 DataNode畸肆,database 是真正物理上的數(shù)據(jù)庫 -->
<dataNode name="dn1" dataHost="host1" database="orders" />
<dataNode name="dn2" dataHost="host2" database="users" />
<!-- name對應(yīng) dataNode 的 datatHost -->
<dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
</mycat:schema>
table 標(biāo)簽的 name 屬性宦芦,代表實際庫中的表,對所有mycat里customer的操作都會走指定的 dataNode.
水平拆分(分表)
修改schema.xml文件
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 在server.xml中被指定的邏輯庫 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
<!-- 配置表 -->
<table name="customer" dataNode="dn2"></table>
<!-- 配置表 -->
<table name="orders" dataNode="dn1,dn3" rule="mod_rule"></table>
</schema>
<!-- 對應(yīng)schema中的 DataNode轴脐,database 是真正物理上的數(shù)據(jù)庫 -->
<dataNode name="dn1" dataHost="host1" database="orders" />
<dataNode name="dn3" dataHost="host3" database="orders" />
<dataNode name="dn2" dataHost="host2" database="users" />
<!-- name對應(yīng) dataNode 的 datatHost -->
<dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
<dataHost name="host3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.106:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
</mycat:schema>
修改rule.xml文件
<!-- 分片規(guī)則 -->
<tableRule name="mod_rule">
<rule>
<!-- 指定分片的列 -->
<columns>customer_id</columns>
<!-- 指定分片算法调卑,為function的name -->
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<!-- 具體的分片算法指定類 -->
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- 數(shù)據(jù)節(jié)點,根據(jù)你要分片的數(shù)量(機器數(shù)量) -->
<property name="count">2</property>
</function>
tableRule 表規(guī)則大咱,其中name屬性就是 schema.xml table標(biāo)簽的 rule屬性
Mycat的分片 "join"
orders 訂單表已經(jīng)進行分表操作了恬涧,和它關(guān)聯(lián)的 orders_detail 訂單詳情表如何進行 join 查詢?其實我們也要對 orders_detail 進行分片操作徽级。
ER表
Mycat借鑒了NewSQL領(lǐng)域的新秀 Foundation DB 的設(shè)計思路气破,F(xiàn)oundation DB 創(chuàng)新性的提出了 Table Group 的概念,其將子表的存儲位置依賴于主表餐抢,并且物理上緊鄰存放,因此徹底解決了 JOIN 的效率和性能問題低匙,根據(jù)這一思路旷痕,提出了基于 E-R 關(guān)系的數(shù)據(jù)分片策略,子表的記錄與所關(guān)聯(lián)的父表記錄存放在同一個數(shù)據(jù)分片上顽冶。
修改 schema.xml 配置
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 在server.xml中被指定的邏輯庫 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
<!-- 配置表 -->
<table name="customer" dataNode="dn2"></table>
<!-- 配置表 -->
<table name="orders" dataNode="dn1,dn3" rule="mod_rule">
<!-- join表的name欺抗,該表的(primaryKey)主鍵,該表的(joinKey)外鍵强重,對應(yīng)主表的(parentKey)ID -->
<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>
</schema>
<!-- 對應(yīng)schema中的 DataNode绞呈,database 是真正物理上的數(shù)據(jù)庫 -->
<dataNode name="dn1" dataHost="host1" database="orders" />
<dataNode name="dn3" dataHost="host3" database="orders" />
<dataNode name="dn2" dataHost="host2" database="users" />
<!-- name對應(yīng) dataNode 的 datatHost -->
<dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
<dataHost name="host3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.106:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
</mycat:schema>
childTable 為要join的子表
全局表
在分片的情況下贸人,當(dāng)業(yè)務(wù)表因為規(guī)模而進行分片以后,業(yè)務(wù)表與這些附屬的字典表之間的關(guān)聯(lián)佃声,就成了比較棘手的問題艺智,考慮到字典表具有以下幾個特性:
- 變動部頻繁
- 數(shù)據(jù)量總體變化不大
- 數(shù)據(jù)規(guī)模不大,很少有超過數(shù)十萬條記錄
鑒于此圾亏,Mycat定義了一種特殊的表十拣,稱之為"全局表",全局表具有以下特性:
- 全局表插入志鹃、更新操作會實時在所有節(jié)點上執(zhí)行夭问,保持各個分片的數(shù)據(jù)一致性
- 全局表的查詢操作,只從一個節(jié)點獲取
- 全局表可以跟任何一個表進行JOIN操作
將字典或者符合字典特性的一些表定義為全局表曹铃,則從另外一個方面缰趋,很好的解決了數(shù)據(jù)JOIN的難題。通過全局表+基于E-R關(guān)系的分片策略陕见,MyCat 可以滿足 80%以上的企業(yè)應(yīng)用開發(fā)秘血。
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 在server.xml中被指定的邏輯庫 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
<!-- 配置表 -->
<table name="customer" dataNode="dn2"></table>
<!-- 配置表 -->
<table name="orders" dataNode="dn1,dn3" rule="mod_rule">
<!-- join表的name,該表的(primaryKey)主鍵淳玩,該表的(joinKey)外鍵直撤,對應(yīng)主表的(parentKey)ID -->
<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>
<!-- 全局表,name="表名" dataNode="指定節(jié)點" type="全局global" -->
<table name="dict_order_type" dataNode="dn1,dn2,dn3" type="global"></table>
</schema>
<!-- 對應(yīng)schema中的 DataNode,database 是真正物理上的數(shù)據(jù)庫 -->
<dataNode name="dn1" dataHost="host1" database="orders" />
<dataNode name="dn3" dataHost="host3" database="orders" />
<dataNode name="dn2" dataHost="host2" database="users" />
<!-- name對應(yīng) dataNode 的 datatHost -->
<dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.104:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
<dataHost name="host3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.106:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<!-- 心跳檢測 -->
<heartbeat>select user()</heartbeat>
<!-- 定義寫主1機 -->
<writeHost host="hostM1" url="jdbc:mysql://192.168.81.105:3306" user="dev_fqr" password="Dev@fqr2021"></writeHost>
</dataHost>
</mycat:schema>
常用分片規(guī)則
- 取模蜕着,此規(guī)則為對分片字段求模運算谋竖。也是水平分表最常用規(guī)則
- 分片枚舉,通過在配置文件中配置可能的枚舉ID承匣,自己配置分片蓖乘,本規(guī)則適用于特定的場景,比如有些有任務(wù)需要按照省份或區(qū)縣來做報錯韧骗,而全國省份區(qū)縣固定的嘉抒,這類業(yè)務(wù)使用本條規(guī)則。
# 修改 schema.xml
<table name="orders" dataNode="dn1,dn3" rule="sharding_by_intfile"></table>
# 修改 rule.xml
<!-- 分片規(guī)則 -->
<tableRule name="mod_rule">
<rule>
<!-- 指定分片的列 -->
<columns>areacode</columns>
<!-- 指定分片算法袍暴,為function的name -->
<algorithm>hash-int</algorithm>
</rule>
</tableRule>
<!-- 具體的分片算法指定類 -->
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
<!-- 標(biāo)識配置文件 -->
<property name="mapFile">partition-hash-int.txt</property>
<!-- type:0為int類型些侍,非0為String類型 -->
<property name="type">1</property>
<!-- 默認(rèn)節(jié)點:小于 0 表示不設(shè)置默認(rèn)節(jié)點,大于等于0表示設(shè)置默認(rèn)節(jié)點政模,
配置文件找不到枚舉值岗宣,就讓他路由到默認(rèn)節(jié)點找,沒有設(shè)置默認(rèn)節(jié)點淋样,在文件找不到耗式,就報錯 -->
<property name="defaultNode">0</property>
</function>
# 修改partition-hash-int.txt文件,添加如下
# 找到區(qū)域編號110的,認(rèn)為是第1個數(shù)據(jù)節(jié)點(dn1)
110=0
# 找到區(qū)域編號120的刊咳,認(rèn)為是第2個數(shù)據(jù)節(jié)點(dn3)
120=1
- 范圍約定彪见,此分片適用于,提前規(guī)劃好分片字段某個范圍屬于哪個分片娱挨。
# 修改 schema.xml
<table name="payment_info" dataNode="dn1,dn2" rule="auto_sharding_long"></table>
# 修改 rule.xml
<!-- 分片規(guī)則 -->
<tableRule name="auto_sharding_long">
<rule>
<!-- 指定分片的列 -->
<columns>order_id</columns>
<!-- 指定分片算法余指,為function的name -->
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
<!-- 具體的分片算法指定類 -->
<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
<!-- 定義范圍 -->
<property name="mapFile">autopartition-long.txt</property>
<!-- 不在范圍找默認(rèn)節(jié)點 -->
<property name="defaultNode">0</property>
</function>
# 修改autopartition-long.txt文件,不在配置文件默認(rèn)找第0個節(jié)點(dn1)
# 0-102 到第1個節(jié)點(dn1)
0-102=0
# 103-200到第2個節(jié)點(dn2)
103-200=1
- 按時間分片
# 修改 schema.xml
<table name="login_info" dataNode="dn1,dn2" rule="sharding_by_date"></table>
# 修改 rule.xml
<!-- 分片規(guī)則 -->
<tableRule name="sharding_by_date">
<rule>
<!-- 指定分片的列 -->
<columns>create_time</columns>
<!-- 指定分片算法让蕾,為function的name -->
<algorithm>partbyday</algorithm>
</rule>
</tableRule>
<!-- 具體的分片算法指定類 -->
<function name="partbyday" class="io.mycat.route.function.PartitionByDate">
<!-- 格式化時間 -->
<property name="dateFormat">yyyy-MM-dd</property>
<!-- 開始日期 -->
<property name="sBeginDate">2021-01-01</property>
<!-- 結(jié)束日期浪规,結(jié)束日期一定是 節(jié)點*分區(qū)天數(shù),
也就是我2個節(jié)點探孝,分區(qū)天數(shù)3笋婿,1-3號到dn1,3-6號到dn2顿颅,6-9號的話又分到dn1缸濒;
如果不設(shè)置結(jié)束日期,則6-9號要分到dn3粱腻,沒有dn3則報錯
-->
<property name="sEndDate">2021-01-31</property>
<!-- 分區(qū)天數(shù) -->
<property name="sPartionDay">2</property>
</function>
全局序列
在實現(xiàn)分庫分表的情況下庇配,數(shù)據(jù)庫自增主鍵已無法保證自增主鍵的全局唯一,為此绍些,Mycat 提供了全局的 sequence 捞慌,并且提供了包含本地配置和數(shù)據(jù)庫配置等多種實現(xiàn)方式
本地文件
此方式 Mycat 將 sequence 配置到文件中,當(dāng)使用到 sequence 中的配置后柬批,Mycat 會更下 classpath 中的sequence_conf.properties 文件中 sequence 當(dāng)前的值
優(yōu)點:本地加載啸澡,讀取速度快
缺點:抗風(fēng)險能力差,Mycat所在主機宕機后氮帐,無法讀取本地文件嗅虏。
時間戳方式
全局序列 ID=64位二進制(42(毫秒)+5(機器ID)+5(業(yè)務(wù)編碼)+12(重復(fù)累加))換算成十進制為18位的long類型,每秒可以并發(fā)12位二進制的累加上沐。
優(yōu)點:配置簡單
缺點:18位ID過長
數(shù)據(jù)庫方式
利用數(shù)據(jù)庫一個表來進行技術(shù)累加皮服。但是并不是每次生成序列都讀寫數(shù)據(jù)庫,這樣效率太低参咙。Mycat 會預(yù)加載一部分號碼段到 Mycat 的內(nèi)存中龄广,這樣大部分讀寫序列都是在內(nèi)存中完成的。如果內(nèi)存中的號碼段用完了 Mycat 會再向數(shù)據(jù)庫要一次蕴侧。
注意:
MYCAT_SEQUENCE 表和以下的 3 個function蜀细,需要放在同一個節(jié)點上。function請直接在具體節(jié)點的數(shù)據(jù)庫上執(zhí)行戈盈,如果執(zhí)行的時候報:
you might want to use the less safe log_bin_trust_function_creators variable
需要對數(shù)據(jù)庫做如下設(shè)置:
windows下my.ini[mysqld]加上log_bin_trust_function_creators=1
linux下/etc/my.cnf下my.ini[mysqld]加上log_bin_trust_function_creators=1
修改完后,即可在mysql數(shù)據(jù)庫中執(zhí)行下面的函數(shù)。
1. 創(chuàng)建全局序列表
CREATE TABLE MYCAT_SEQUENCE(
- sequence名稱塘娶,最好為表名
name varchar(50) NOT NULL,
- 當(dāng)前value归斤,起始值
current_value INT NOT NULL,
- 增長步長! 可理解為mycat在數(shù)據(jù)庫中一次讀取多少個 sequence. 當(dāng)這些用完后, 下次再從數(shù)據(jù)庫中讀取。
increment INT NOT NULL DEFAULT 100,
PRIMARY KEY(name)
)ENGINE=INNODB;
# 往表中插入數(shù)據(jù)
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('orders',1, 1000);
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('users',1, 1000);
2. 創(chuàng)建全局序列所需函數(shù)
- 獲取當(dāng)前sequence的值 (返回當(dāng)前值,增量)
DROP FUNCTION IF EXISTS mycat_seq_currval;
DELIMITER
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf-8
DETERMINISTIC
BEGIN
DECLARE retval VARCHAR(64);
SET retval=“-999999999,null”;
SELECT concat(CAST(current_value AS CHAR),“,”,CAST(increment AS CHAR)) INTO retval FROM MYCAT_SEQUENCE WHERE name = seq_name;
RETURN retval;
END
DELIMITER;
- 設(shè)置sequence值
DROP FUNCTION IF EXISTS mycat_seq_setval;
DELIMITER
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),value INTEGER) RETURNS varchar(64) CHARSET utf-8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = value
WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END
DELIMITER;
– 獲取下一個sequence值
DROP FUNCTION IF EXISTS mycat_seq_nextval;
DELIMITER
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf-8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END
DELIMITER;
3. sequence_db_conf.properties相關(guān)配置,指定sequence相關(guān)配置在哪個節(jié)點上
orders=dn1
users=dn1
4. server.xml配置:
<system>
<!-- 0本地文件 1數(shù)據(jù)庫方式 2時間戳方式 -->
<property name="sequnceHandlerType">1</property>
</system>
5.插入數(shù)據(jù)
insert into orders(id,title,price) values(next value for MYCATSEQ_ORDERS,'手機','2000.00')刁岸;
insert into (id,name) values(next value for MYCATSEQ_USERS,'張三')脏里;