MyCat
1 MyCat背景
Mycat的前身是阿里巴巴大名鼎鼎的Cobar,Cobar在開源了一段時間後,就沒有再維護了,阿里巴巴放棄了該項目,再加上Cobar在使用過程中也發現存在一些問題;2013年國內一批開源軟件愛好者對Cobar這個項目進行了改進,並命名爲Mycat,這就是MyCat的誕生。
MyCat是完全免費開源的,不屬於任何商業公司。Mycat於2014年首次在上海的《中華架構師》大會上對外宣講,隨後越來越多的項目採用了Mycat;截至2015年11月,超過300個項目採用Mycat,涵蓋銀行、電信、電子商務、物流、移動應用、O2O的衆多領域和公司;
Mycat官網:http://www.mycat.io/
2 MyCat是什麼
Mycat是一個開源數據庫中間件;
什麼是中間件?
比如你帶了一些家鄉的特產打算送給一個朋友,那麼怎麼送給你的朋友?
第一方式不用中間件:
你親自坐車到你朋友家,把特產送給你的朋友,或者是你朋友來你家取這些特產;
第二種方式使用中間件:
你叫一個快遞,把這些特產同城郵寄給你的朋友,那麼這個快遞員就充當中間件的角色。
Mycat是一個實現了MySQL協議的的數據庫中間件服務器,我們可以把它看作是一個數據庫代理,用MySQL客戶端工具和命令行訪問Mycat,而Mycat再使用MySQL原生(Native)協議與多個MySQL服務器通信;
Mycat也可以使用JDBC協議與大多數主流數據庫服務器通信,包括SQL Server、Oracle、DB2、PostgreSQL 等主流數據庫,也支持MongoDB這種新型NoSQL方式的存儲,未來還會支持更多類型的存儲;
一般地,Mycat主要用於代理MySQL數據庫,雖然它也支持去訪問其他類型的數據庫;Mycat的默認端口是8066,可以使用常見的對象映射框架比如MyBatis操作Mycat;
3 Mycat主要能做什麼
1、數據庫的讀寫分離
通過Mycat可以自動實現讀寫分離,寫數據時操作主數據庫,讀數據時操作從數據庫,這樣能有效地分攤數據庫壓力;當主數據庫出現故障後,Mycat能自動切換到另一個主數據庫上,進而提供高可用的數據庫服務,當然這需要部署多主多從的模式;
2、數據庫分庫分表
分庫分表指的是對數據庫數據的拆分;
分庫分表分爲兩種:水平拆分和垂直拆分;
一種是根據表中數據的邏輯關係,將同一個表中的數據按照某種條件拆分到多臺數據庫服務器上面,這種切分稱之爲數據的水平切分,也可以稱爲橫向切分;
一種是按照不同的表來切分到不同的數據庫服務器之上,這種切可以稱之爲數據的垂直切分,也可以稱爲縱向切分;
性能有瓶頸了,可以讀寫分離;
數據庫容量有瓶頸了,可以分庫分表
3 Mycat環境搭建
1、下載:
http://dl.mycat.io/1.6-RELEASE/
wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
2、下載後解壓即可:
tar –zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz –C /usr/local
3、解壓後即安裝完成;
Java語言開發的,直接解壓即可使用:
tomcat
maven
zookeeper
activemq
mycat
4 Mycat日常管理
4.1 Mycat啓動和關閉
Mycat啓動
切換到mycat的bin路徑下
./mycat start
mycat安裝後需要配置,然後才能啓動
Mycat關閉
切換到mycat的bin路徑下
./mycat stop
4.2 Mycat命令行
登錄mycat,可以使用mysql的命令行工具來操作:
./mysql -umycat -p -P8066 -h127.0.0.1
mycat默認數據訪問端口是8066
4.3 MyCat配置文件
1、server.xml
主要用於配置mycat的服務器信息
常用配置:
1、配置序列生成方式(數據庫表的主鍵生成方式)
2、配置mycat邏輯數據庫
3、配置mycat的訪問賬戶和密碼
2、schema.xml
用於配置邏輯數據庫的映射、表、分片規則、數據結點及真實的數據庫信息;
常用配置:
1、配置邏輯庫映射
2、配置水平切分的表
3、配置垂直切分的表
4、配置真實的數據庫
5、配置讀寫結點
5 Mycat讀寫分離
1、配置server.xml文件
設置連接mycat時的用戶名和密碼, 邏輯庫:
<user name="mycat">
<property name="password">123456</property>
<property name="schemas">mycatdb</property>
</user>
參考配置
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="useSqlStat">0</property> <!-- 1爲開啓實時統計、0爲關閉 -->
<property name="useGlobleTableCheck">0</property> <!-- 1爲開啓全加班一致性檢測、0爲關閉 -->
<property name="sequnceHandlerType">2</property>
<!-- <property name="useCompression">1</property>--> <!--1爲開啓mysql壓縮協議-->
<!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--設置模擬的MySQL版本號-->
<!-- <property name="processorBufferChunk">40960</property> -->
<!--
<property name="processors">1</property>
<property name="processorExecutor">32</property>
-->
<!--默認爲type 0: DirectByteBufferPool | type 1 ByteBufferArena-->
<property name="processorBufferPoolType">0</property>
<!--默認是65535 64K 用於sql解析時最大文本長度 -->
<!--<property name="maxStringLiteralLength">65535</property>-->
<!--<property name="sequnceHandlerType">0</property>-->
<!--<property name="backSocketNoDelay">1</property>-->
<!--<property name="frontSocketNoDelay">1</property>-->
<!--<property name="processorExecutor">16</property>-->
<!--
<property name="serverPort">8066</property> <property name="managerPort">9066</property>
<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
<!--分佈式事務開關,0爲不過濾分佈式事務,1爲過濾分佈式事務(如果分佈式事務內只涉及全局表,則不過濾),2爲不過濾分佈式事務,但是記錄分佈式事務日誌-->
<property name="handleDistributedTransactions">0</property>
<!--
off heap for merge/order/group/limit 1開啓 0關閉
-->
<property name="useOffHeapForMerge">1</property>
<!--
單位爲m
-->
<property name="memoryPageSize">1m</property>
<!--
單位爲k
-->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!--
單位爲m
-->
<property name="systemReserveMemorySize">384m</property>
<!--是否採用zookeeper協調切換 -->
<property name="useZKSwitch">true</property>
</system>
<!-- 全局SQL防火牆設置 -->
<!--
<firewall>
<whitehost>
<host host="127.0.0.1" user="mycat"/>
<host host="127.0.0.2" user="mycat"/>
</whitehost>
<blacklist check="false">
</blacklist>
</firewall>
-->
<!-- 配置mycat的訪問賬號 -->
<user name="mycat">
<property name="password">123456</property>
<!-- mycat的邏輯數據庫 -->
<property name="schemas">mycatdb</property>
<!-- 表級 DML 權限設置 -->
<!--
<privileges check="false">
<schema name="mycatdb" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
<!-- 配置mycat的訪問賬號 -->
<user name="user">
<property name="password">123456</property>
<property name="schemas">mycatdb</property>
<property name="readOnly">true</property>
</user>
</mycat:server>
2、配置schema.xml文件
2.1、配置schema
作用:schema用於配置邏輯庫
只做讀寫分離,不做分庫分表,則schema標籤裏面不用配置table;
給schema標籤加上屬性dataNode,配置dataNode的名字(name);
最終配置如下:
<!--schema配置邏輯數據庫與真實數據庫的映射-->
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"/>
2.2、配置dataNode
作用:dataNode定義了MyCat中的數據節點,也就是我們通常說所的數據分片,
一個dataNode標籤就是一個獨立的數據分片,通俗理解,一個分片就是一個物理數據庫;
配置說明:
name 定義數據節點的名字,這個名字需要是唯一的,這個名字在schema裏面會使用到;
dataHost用於定義該分片屬於哪個數據庫實例的,屬性值是引用dataHost標籤上定義的name屬性;
database用於對應真實的數據庫名,必須是真實存在的;
最終配置如下:
<dataNode name="dn1" dataHost="localhost1" database="workdb" />
2.3、配置dataHost
作用:定義具體的數據庫實例、讀寫分離配置和心跳語句;
balance屬性:
負載均衡類型,目前的取值有4種:
1. balance="0", 不開啓讀寫分離機制,所有讀操作都發送到當前可用的writeHost上;
2. balance="1",全部的readHost與stand by writeHost參與select語句的負載均衡,
簡單的說,當雙主雙從模式(M1->S1,M2->S2,並且M1與 M2互爲主備),正常情況下,
M2,S1,S2都參與select語句的負載均衡。
3.balance="2",所有讀操作都隨機的在writeHost、readhost上分發;
4.balance="3",所有讀請求隨機的分發到wiriterHost對應的readhost執行,writerHost
不負擔讀壓力;
推薦balance設置爲1;
switchType屬性:
1.用於指定主服務器發生故障後的切換類型:
2.-1 表示不自動切換 1 默認值,自動切換 2 基於MySQL主從同步的狀態決定是否切換
3 基於MySQL galary cluster的切換機制(適合集羣)(1.4.1)
通常情況下,我們MySQL採用雙主雙從的模式下,switchType爲1即可。
因爲雙主從模式下,主從同步關係很複雜,不能根據MySQL的狀態來切換。
只需要在一個主出問題後,切換到另外的主。
heartbeat標籤
1.用於和後端數據庫進行心跳檢查的語句
2.當switchType爲1時,mysql心跳檢查語句是select user()
3.switchType爲2時,心跳檢查語句是show slave status
writeHost與readHost標籤:
1. 這兩個標籤都指定後端數據庫的相關配置給mycat,用於實例化後端連接池。唯一不同的是,
writeHost指定寫實例、readHost指定讀實例,組合這些讀寫實例來滿足系統的要求。
2.在一個dataHost內可以定義多個writeHost和readHost。但是,如果writeHost指定的後端
數據庫宕機,那麼這個writeHost綁定的所有readHost都將不可用。另一方面,由於這個
writeHost宕機系統會自動的檢測到,並切換到備用的writeHost上去。
一主三從配置參考:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<!-- 只實現讀寫分離,沒有涉及到分庫分表,那麼<schema>標籤下不需要配置任何表 -->
</schema>
<!--配置真實的數據庫名稱 test01 -->
<dataNode name="dn1" dataHost="localhost1" database="test01" />
<!--配置具體的數據庫連接信息、讀寫分離、心跳語句 -->
<dataHost name="localhost1"
maxCon="1000"
minCon="10"
balance="1"
writeType="0"
dbType="mysql"
dbDriver="native"
switchType="1"
slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!--配置寫數據庫(主庫) 一主三從的讀寫分離配置 -->
<writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
<!--配置寫數據庫下的讀數據庫(從庫)-->
<readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
<readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
<readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
雙主雙從配置參考
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!--schema配置邏輯數據庫與真實數據庫的映射-->
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="1000" dataNode="dn1">
<!---3307主、3308主、3309從、3310從-->
<!---讀寫分離,在schema標籤下不需要配置table,當分庫分表的時候才需要配置table-->
</schema>
<!---3307主、3308主、3309從、3310從-->
<dataNode name="dn1" dataHost="localhost1" database="test01" />
<!--
1、balance="1" 開啓讀寫分離,如果是0表示沒有開啓讀寫分離
2、switchType="1" 故障切換類型,1代表自動切換
-->
<dataHost name="localhost1"
maxCon="1000"
minCon="10"
balance="1"
writeType="0"
dbType="mysql"
dbDriver="native"
switchType="1"
slaveThreshold="100">
<!--心跳語句 檢查服務器有沒有故障-->
<heartbeat>select user()</heartbeat>
<!---主庫3307-->
<writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
<!---從庫3309-->
<readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
<!---從庫3308-->
<readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
</writeHost>
<!---主庫3308-->
<writeHost host="hostM3308" url="localhost:3308" user="root" password="123456">
<!---從庫3310-->
<readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
<!---從庫3307-->
<readHost host="hostS3307" url="localhost:3307" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
3 測試讀寫分離
1、配置好MySQL主從複製並啓動主從MySQL;
2、啓動Mycat:/usr/local/mycat/bin/mycat start
3、登錄Mycat:mysql -uroot -p -P8066 -h 192.168.230.129
4、mycat默認數據訪問端口是8066
5、use mycatdb;
6、創建表,插入數據,觀察MySQL數據的情況;
insert into orders (id, money) values (next value for MYCATSEQ_GLOBAL, 105);
7、在從庫中創建一個表tables,從庫數據不會同步到主庫;
8、然後通過mycat往tables中插入數據,觀察是否能插入成功,若不能成功,表示已經實現了讀寫分離;
9、然後,關閉主MySQL3308的服務;
10、在這裏再進行數據插入,觀察其它MySQL實例數據同步情況。
11、最後,把3308啓動,再觀察數據能不能同步到3308和它的從節點;
12、主節點的自動切換,會記錄在文件dnindex.properties在停掉3306主之前,cat一下該文件的內容;停掉之後,再cat一下;
6 MyCat分庫分表(水平)
1、配置server.xml
指定主鍵生成策略
<property name="sequnceHandlerType">1</property>
指定使用Mycat全局序列的類型:
0爲本地文件方式,1爲數據庫方式,2爲時間戳序列方式
2、配置schema.xml
指定邏輯庫,分片結點,結點主機等
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!--schema配置邏輯數據庫與真實數據庫的映射-->
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="1000">
<!---3307主、3308主、3309從、3310從-->
<!---水平切分,需要在schema標籤下需要配置需要對哪個table進行水平切分, orders表有1000萬數據需要水平切分-->
<!--rule="mod-long"指定切分的規則,取模的規則 表的id % 4 = 餘數-->
<table name="orders" primaryKey="id" dataNode="dn1,dn2,dn3,dn4" rule="mod-long" />
</schema>
<!---3307主、3308主、3309從、3310從-->
<dataNode name="dn1" dataHost="localhost1" database="test01" />
<dataNode name="dn2" dataHost="localhost1" database="test02" />
<dataNode name="dn3" dataHost="localhost1" database="test03" />
<dataNode name="dn4" dataHost="localhost1" database="test04" />
<!--
1、balance="1" 開啓讀寫分離,如果是0表示沒有開啓讀寫分離
2、switchType="1" 故障切換類型,1代表自動切換
-->
<dataHost name="localhost1"
maxCon="1000"
minCon="10"
balance="1"
writeType="0"
dbType="mysql"
dbDriver="native"
switchType="1"
slaveThreshold="100">
<!--心跳語句 檢查服務器有沒有故障-->
<heartbeat>select user()</heartbeat>
<!---主庫3307-->
<writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
<!---從庫3309-->
<readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
<!---從庫3308-->
<readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
</writeHost>
<!---主庫3308-->
<writeHost host="hostM3308" url="localhost:3308" user="root" password="123456">
<!---從庫3310-->
<readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
<!---從庫3307-->
<readHost host="hostS3307" url="localhost:3307" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
3、配置rule.xml
指定分片結點數
改爲4
4、插入數據驗證
insert into tb11 (id, name) values (next value for MYCATSEQ_GLOBAL,'zhangsan');
不管是何種方式的切分,主鍵生成必須交給MyCat實現;
7 Mycat分庫分表(垂直)
1、修改server.xml
<property name="sequnceHandlerType">0</property>
2、修改schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!--schema配置邏輯數據庫與真實數據庫的映射-->
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="1000">
<!---3307主、3308主、3309從、3310從-->
<!---垂直切分,需要在schema標籤下需要配置需要對哪個table進行垂直切分-->
<!--需求:原來是一個數據庫共120張表,現在進行垂直切分,切分成3個數據庫,分別是:
web(前臺數據庫30張表)、admin(後臺數據庫45張表)、red(紅包數據庫45張表)
-->
<!--web(前臺數據庫30張表)-->
<table name="users" primaryKey="id" dataNode="dn1" />
<table name="auth" primaryKey="id" dataNode="dn1" />
<!--...省略了下面的28張表...-->
<!--admin(後臺數據庫45張表)-->
<table name="products" primaryKey="id" dataNode="dn2" />
<table name="creditors" primaryKey="id" dataNode="dn2" />
<!--...省略了下面的43張表...-->
<!--red(紅包數據庫45張表)-->
<table name="redpackage" primaryKey="id" dataNode="dn3" />
<table name="redpackageType" primaryKey="id" dataNode="dn3" />
<!--...省略了下面的43張表...-->
</schema>
<!---3307主、3308主、3309從、3310從-->
<dataNode name="dn1" dataHost="localhost1" database="web" />
<dataNode name="dn2" dataHost="localhost1" database="admin" />
<dataNode name="dn3" dataHost="localhost1" database="red" />
<!--
1、balance="1" 開啓讀寫分離,如果是0表示沒有開啓讀寫分離
2、switchType="1" 故障切換類型,1代表自動切換
-->
<dataHost name="localhost1"
maxCon="1000"
minCon="10"
balance="1"
writeType="0"
dbType="mysql"
dbDriver="native"
switchType="1"
slaveThreshold="100">
<!--心跳語句 檢查服務器有沒有故障-->
<heartbeat>select user()</heartbeat>
<!---主庫3307-->
<writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
<!---從庫3309-->
<readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
<!---從庫3308-->
<readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
</writeHost>
<!---主庫3308-->
<writeHost host="hostM3308" url="localhost:3308" user="root" password="123456">
<!---從庫3310-->
<readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
<!---從庫3307-->
<readHost host="hostS3307" url="localhost:3307" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
3、插入數據驗證
插入數據驗證
insert into tb11 (id, name) values (next value for MYCATSEQ_GLOBAL,'zhangsan');
4、垂直切分價值:
垂直切分帶來的價值:可以屏蔽掉多數據源的問題,只需要一個統一入口mycat就可以操作下面的多個數據庫;不管是何種方式的切分,主鍵生成必須交給MyCat實現;
8 Mycat全局序列號
8.1 本地文件方式
server.xml配置文件:sequnceHandlerType=0
conf/sequence_conf.properties
8.2 時間戳方式
server.xml配置文件:sequnceHandlerType=2
使用一個時間戳作爲主鍵
8.3 數據庫方式
1、在數據庫中執行腳本
DROP TABLE IF EXISTS MYCAT_SEQUENCE;
CREATE TABLE MYCAT_SEQUENCE (name VARCHAR(50) NOT NULL,current_value INT NOT NULL,increment INT NOT NULL DEFAULT 1,
PRIMARY KEY(name)) ENGINE=InnoDB default charset=utf8;
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ("GLOBAL", 0, 100);
DROP FUNCTION IF EXISTS mycat_seq_currval;
DELIMITER //
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf8
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 ;
DROP FUNCTION IF EXISTS mycat_seq_setval;
DELIMITER //
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),value INTEGER) RETURNS varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = value
WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END //
DELIMITER ;
DROP FUNCTION IF EXISTS mycat_seq_nextval;
DELIMITER //
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END //
DELIMITER ;
2、server.xml配置:
<property name="sequnceHandlerType">1</property>
注:sequnceHandlerType 需要配置爲1,表示使用數據庫方式生成sequence.
3、指定sequence在哪個結點
sequence_db_conf.properties相關配置,指定sequence相關配置在哪個節點上
GLOBAL=dn1,dn2,dn3,dn4
4、插入數據
插入時怎麼用
insert into tb1(id,name) values(next value for MYCATSEQ_GLOBAL,"test");