下載Mycat
https://github.com/MyCATApache/Mycat-Server/releases
Mycat基礎分庫
基礎配置
- 修改server.xml
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<property name="defaultSchema">TESTDB</property>
<!--No MyCAT Database selected 錯誤前會嘗試使用該schema作爲schema,不設置則爲null,報錯 -->
<!-- 表級 DML 權限設置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
可以修改用戶名和密碼以及邏輯庫名。另外將全局主鍵改爲文件的方式,將下面的1改爲0.
<property name="sequenceHandlerType">1</property>
- 修改schema.xml
修改了數據庫名,數據庫用戶密碼
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
<table name="position" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" >
</table>
</schema>
<dataNode name="dn1" dataHost="localhost1" database="ds_0" />
<dataNode name="dn2" dataHost="localhost1" database="ds_1" />
<dataHost name="localhost1" 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://localhost:3306" user="root"
password="0490218292">
</writeHost>
</dataHost>
</mycat:schema>
- 配置rule.xml
rule.xml裏定義了很多分片規則,我們選擇最簡單的取模分片mod-long,只需要將這個名稱配置到schema.xml裏就行了。另外因爲我們只有2個節點,所以取餘的數量需要將3改爲2。
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">3</property>
</function>
將mod-long配置到rule裏。
<table name="position" primaryKey="id" dataNode="dn1,dn2" rule="mod-long" autoIncrement="true" >
</table>
啓動
在mycat/bin目錄下,用管理員身份啓動cmd,執行如下命令:
mycat install
mycat start
啓動完成後,就可以進行訪問了。
命令行方式:
mysql -uroot -p123456 -h127.0.0.1 -P8066
測試
插入數據測試:
INSERT INTO POSITION (id,NAME,salary,city) VALUES(1,"lagou3","1222","sichuan");
INSERT INTO POSITION (id,NAME,salary,city) VALUES(2,"lagou3","1222","sichuan");
INSERT INTO POSITION (id,NAME,salary,city) VALUES(3,"lagou3","1222","sichuan");
INSERT INTO POSITION (id,NAME,salary,city) VALUES(4,"lagou3","1222","sichuan");
數據是否分佈到了ds_0、ds_1兩個庫中。
查詢:
select * from POSITION;
看能否查出兩個庫中的所有數據。
碰到的問題
問題1:因爲我是mysql8,所以在mycat/lib中用mysql-connector-java-8.0.11.jar替換了原有的.
問題2:The server time zone value '???ú±ê×??±??' is unrecognized or represents more than one time zone
<writeHost host="hostM1" url="jdbc:mysql://localhost:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="0490218292">
</writeHost>
url配置里加上了時區。
此外mycat還有如下命令:
啓動命令:./mycat start
停止命令:./mycat stop
重啓命令:./mycat restart
查看狀態:./mycat status
全局序列ID
全局序列id有5種方式可以選擇。
在server.xml裏有如下配置:
<property name="sequenceHandlerType">0</property>
- 0 代表本地文件方式
- 1 代表數據庫方式生成
- 2 代表使用本地時間戳方式
- 3 表示基於ZK與本地配置的分佈式ID生成器
- 4 使用zookeeper遞增方式生成
1. 本地文件方式
本地主鍵的配置文件是conf/sequence_conf.properties。
#default global sequence
GLOBAL.HISIDS=
GLOBAL.MINID=10001
GLOBAL.MAXID=20000
GLOBAL.CURID=10000
# self define sequence
COMPANY.HISIDS=
COMPANY.MINID=1001
COMPANY.MAXID=2000
COMPANY.CURID=1000
CUSTOMER.HISIDS=
CUSTOMER.MINID=1001
CUSTOMER.MAXID=2000
CUSTOMER.CURID=1000
ORDER.HISIDS=
ORDER.MINID=1001
ORDER.MAXID=2000
ORDER.CURID=1000
HOTNEWS.HISIDS=
HOTNEWS.MINID=1001
HOTNEWS.MAXID=2000
HOTNEWS.CURID=1000
我們默認在進行插入的時候就會使用mycat的全局主鍵生成,那麼上面有那麼多的配置項,具體取哪一個配置項呢?
這個配置的前綴是跟表名對應的。舉例說明:
<table name="position" primaryKey="id" dataNode="dn1,dn2" rule="mod-long" autoIncrement="true" >
</table>
比如這個表,就需要配置如下:
POSITION.HISIDS=
POSITION.MINID=1
POSITION.MAXID=20000
POSITION.CURID=0
這樣,在我們不指定id進行插入時就可以取我們配置的序列號了。
INSERT INTO POSITION (NAME,salary,city) VALUES("lagou3","1222","sichuan");
當然也可以顯示指定取哪一個序列號配置:
INSERT INTO POSITION (id,NAME,salary,city) VALUES('next value for MYCATSEQ_GLOBAL',"lagou3","1222","sichuan");
MYCATSEQ_ 後面連接的就是配置前綴名稱。
2. 數據庫方式
配置文件是conf/sequence_db_conf.properties,裏面需要配置db的id相關的配置信息存在哪個庫中。
#sequence stored in datanode
GLOBAL=dn1
COMPANY=dn1
CUSTOMER=dn1
ORDERS=dn1
此處的dn1是邏輯庫名。
我們需要在dn1對應的數據庫中新建表,並且插入數據。Mycat給我們提供了建表等語句(conf/dbseq.sql)。我們執行即可。
increment字段表示一次從數據庫取幾個值。比如設置爲10的話,就是一次取10個id供應用端使用。
3. 時間戳方式
配置文件是conf/sequence_time_conf.properties。
#sequence depend on TIME
WORKID=01
DATAACENTERID=01
全局表配置
只需要在schema.xml的配置中加上type=global就行了
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
<table name="city" primaryKey="id" dataNode="dn1,dn2" autoIncrement="true" type="global" >
</table>
</schema>
讀寫分離
通過writeHost 和readHost 實現
<dataHost name="localhost2" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="jdbc:mysql://localhost:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="0490218292">
<readHost host="hostS1" url="jdbc:mysql://192.168.56.115:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="123456">
</readHost>
</writeHost>
</dataHost>
或者
<dataHost name="localhost2" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="jdbc:mysql://localhost:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="0490218292">
</writeHost>
<writeHost host="hostS1" url="jdbc:mysql://192.168.56.115:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="123456">
</writeHost>
</dataHost>
兩種方式對比:
- 第一種配置方式,如果寫節點掛了,讀節點不可用。
- 第二種配置方式(查詢的時候取第二個writeHost),如果其中一個寫節點掛了,另一個不受影響。
如果設置全庫的讀寫分離,schema.xml配置如下:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn3">
</schema>
<dataNode name="dn3" dataHost="localhost2" database="crm" />
<dataHost name="localhost2" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="jdbc:mysql://localhost:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="0490218292">
<readHost host="hostS1" url="jdbc:mysql://192.168.56.115:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="123456">
</readHost>
</writeHost>
</dataHost>
</mycat:schema>
此時
如果設置某些表(示例中是dept)讀寫分離,配置如下:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
<table name="position" primaryKey="id" dataNode="dn1,dn2" rule="mod-long" autoIncrement="true" >
</table>
<table name="city" primaryKey="id" dataNode="dn1,dn2" autoIncrement="true" type="global" >
</table>
<table name="dept" primaryKey="id" dataNode="dn3" autoIncrement="true" ruleRequired="false">
</table>
</schema>
<dataNode name="dn1" dataHost="localhost1" database="ds_0" />
<dataNode name="dn2" dataHost="localhost1" database="ds_1" />
<dataNode name="dn3" dataHost="localhost2" database="crm" />
<dataHost name="localhost1" 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://localhost:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="0490218292">
</writeHost>
</dataHost>
<dataHost name="localhost2" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="jdbc:mysql://localhost:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="0490218292">
<readHost host="hostS1" url="jdbc:mysql://192.168.56.115:3306?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8" user="root"
password="123456">
</readHost>
</writeHost>
</dataHost>
</mycat:schema>
強制路由
通過mycat的註釋實現:
/*#mycat:db_type=slave*/ SELECT * FROM dept;
/*#mycat:db_type=master*/ SELECT * FROM dept;
1.6版本後,還有如下注釋:
/*!mycat:sql=sql */ 指定真正執行的SQL
/*!mycat:schema=schema1 */ 指定走那個schema
/*!mycat:datanode=dn1 */ 指定sql要運行的節點
/*!mycat:catlet=io.mycat.catlets.ShareJoin */ 通過catlet支持跨分片複雜SQL實現以及存
儲過程支持等
主從延時切換
switchType參數:
- -1:表示不自動切換
- 1:表示自動切換
- 2:表示基於Mysql主從同步狀態決定是否切換
- 3:基於Mysql cluster集羣切換機制
1.4版本後開始支持MySQL主從複製狀態綁定的讀寫分離機制,讓讀操作更加安全可靠。需要配置內容如下:
MyCAT心跳檢查語句配置爲 show slave status,dataHost上定義兩個新屬性:switchType="2"與slaveThreshold="100"
此時意味着開啓 MySQL 主從複製狀態綁定的讀寫分離與切換機制,Mycat心
跳機制通過檢測 show slave status 中的 "Seconds_Behind_Master", "Slave_IO_Running",
"Slave_SQL_Running" 三個字段來確定當前主從同步的狀態以及 Seconds_Behind_Master 主從複製時延, 當Seconds_Behind_Master > slaveThreshold時,讀寫分離篩選器會過濾掉此Slave機器,防止讀到很久之前的舊數據,而當主節點宕機後,切換邏輯會檢查Slave上的Seconds_Behind_Master是否爲0,爲0時則表示主從同步,可以安全切換,否則不會切換
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0"
dbType="mysql" dbDriver="native" switchType="2" slaveThreshold="100">
<heartbeat>show slave status </heartbeat>
<!-- can have multi write hosts -->
<writeHost host="M1" url="localhost:3306" user="root" password="123456">
</writeHost>
<writeHost host="S1" url="localhost:3316" user="root"
</dataHost>
1.4.1開始支持MySQL集羣模式:
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0"
dbType="mysql" dbDriver="native" switchType="3" >
<heartbeat> show status like ‘wsrep%’</heartbeat>
<writeHost host="M1" url="localhost:3306" user="root"password="123456">
</writeHost>
<writeHost host="S1"url="localhost:3316"user="root"password="123456" >
</writeHost>
</dataHost>
XA事務使用
Mycat 從 1.6.5 版本開始支持標準XA分佈式事務,考慮到MySQL5.7之前版本XA有bug,所以推薦最佳搭配XA功能使用 MySQL 5.7 版本。
Mycat實現XA標準分佈式事務,Mycat作爲XA事務協調者角色,即使事務過程中Mycat宕機掛掉,由於Mycat會記錄事務日誌,所以Mycat恢復後會進行事務的恢復善後處理工作。考慮到分佈式事務的性能開銷比較大,所以只推薦在全局表的事務以及其他一些對一致性要求比較高的場景。
使用示例:
- XA事務需要設置手動提交
set autocommit=0;
- 開啓XA事務
set xa=on;
- 執行相應的SQL語句部分
insert into city(id,name,province) values(200,'chengdu','sichuan');
update position set salary='300000' where id<5;
- 提交或回滾事務
commit;
rollback;
如果其中一個sql語句失敗,整體都會回滾。
保證Repeatable Read
mycat 有一個特性,就是開事務之後,如果不運行 update/delete/select for update 等更新類語句SQL 的話,不會將當前連接與當前 session 綁定。如下圖所示:
這樣做的好處是可以保證連接可以最大限度的複用,提升性能。但是回導致兩次查詢的結果不一致。違背了MySQL可重複讀的特性。Mycat給我們提供了一個配置來解決這個問題。
server.xml 的 system 配置了 strictTxIsolation=true)(默認是false) 的時候,就會嚴格遵守隔離級別。配置後的連接如下圖所示: