1. 程式人生 > 實用技巧 >Mycat 概念與配置

Mycat 概念與配置

1.1 Mycat 介紹與核心概念
  • 1.1.1 基本介紹
歷史:從阿里 cobar 升級而來,由開源組織維護,2.0 正在開發中。 定位:執行在應用和資料庫之間,可以當做一個 MySQL 伺服器使用,實現對 MySQL資料庫的分庫分表,也可以通過 JDBC 支援其他的資料庫。 Mycat 的關鍵特性(官網首頁)
  • 1、可以當做一個 MySQL 資料庫來使用;
  • 2、支援 MySQL 之外的資料庫,通過 JDBC 實現;
  • 3、解決了我們提到的所有問題,多表 join、分散式事務、全域性序列號、翻頁排序;
  • 4、支援 ZK 配置,帶監控 mycat-web;
  • 5、2.0 正在開發中;
  • 1.1.2 核心概念

概念 含義
主機 物理主機,一臺伺服器,一個數據庫服務,一個 3306 埠
物理資料庫 真實的資料庫,例如 146、150、151 的 gpcat 資料庫
物理表 真實的表,例如 146、150、151 的 gpcat 資料庫的 order_info 表
分片 將原來單個數據庫的資料切分後分散儲存在不同的資料庫節點
分片節點 分片以後資料儲存的節點
分片鍵 分片依據的欄位,例如 order_info 表以 id 為依據分片,id 就是分片鍵,通常是主鍵
分片演算法 分片的規則,例如隨機、取模、範圍、雜湊、列舉以及各種組合演算法
邏輯表 相對於物理表,是分片表聚合後的結果,對於客戶端來說跟真實的表沒有區別
邏輯資料庫 相對於物理資料庫,是資料節點聚合後的結果,例如 catmall
下載、解壓 Mycat(有 Windows 版本,可以在本地資料庫測試): http://dl.mycat.io/
wget http://dl.mycat.io/1.6.7.3/20190927161129/Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz tar -xzvf Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz
Mycat 解壓以後有 5 個目錄:
目錄 作用
bin 啟動目錄
catlet 空目錄
conf 配置目錄
lib jar 包依賴
logs 日誌目錄
1.2 Mycat 配置詳解 主要的配置檔案 server.xml、schema.xml、rule.xml 和具體的分片配置檔案。 坑非常多,配置錯誤會導致無法啟動,這個時候要看日誌! 注意備份,不知道什麼時候就跑不起來了……
  • 1.2.1 server.xml
包含系統配置資訊。 system 標籤:例如字符集、執行緒數、心跳、分散式事務開關等等。 user 標籤:配置登入使用者和許可權。
<user name="root" defaultAccount="true"> 
  <property name="password">123456</property> 
  <property name="schemas">catmall</property>
</user>
mycat 對密碼加密:
java -cp Mycat-server-1.6.7.3-release.jar io.mycat.util.DecryptUtil 0:root:123456
  • 1.2.2 schema.xml
https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_schema schema 在 MySQL 裡面跟資料庫是等價的。 schema.xml 包括邏輯庫、表、分片規則、分片節點和資料來源,可以定義多個 schema。 這裡面有三個主要的標籤(table、dataNode、dataHost): <table/> 表名和庫名最好都用小寫 定義了邏輯表,以及邏輯表分佈的節點和分片規則:
<schema name="catmall" checkSQLschema="false" sqlMaxLimit="100">
  <!-- 範圍分片 -->
  <table name="customer" primaryKey="id" dataNode="dn1,dn2,dn3" rule="rang-long-cust" />
  <!-- 取模分片 --> 
  <table name="order_info" dataNode="dn1,dn2,dn3" rule="mod-long-order" >
	 <!-- ER 表 -->
	 <childTable name="order_detail" primaryKey="id" joinKey="order_id" parentKey="order_id"/> 
  </table> 
  <!-- 全域性表 -->
  <table name="student" primaryKey="sid" type="global" dataNode="dn1,dn2,dn3" />
 </schema>

配置 作用
primaryKey 指定該邏輯表對應真實表的主鍵。MyCat 會快取主鍵(通過 primaryKey 屬性配置)與 具體 dataNode 的資訊。 當分片規則(rule)使用非主鍵進行分片時,那麼在使用主鍵進行查詢時,MyCat 就 會通過快取先確定記錄在哪個 dataNode 上,然後再在該 dataNode 上執行查詢。 如果沒有快取/快取並沒有命中的話,還是會發送語句給所有的 dataNode
dataNode 資料分片的節點
autoIncrement 自增長(全域性序列),true 代表主鍵使用自增長策略
type 全域性表:global。其他:不配置
<dataNode/>
<dataNode name="dn1" dataHost="host1" database="gpcat" />
資料節點與物理資料庫的對應關係。 配置物理主機的資訊,readhost 是從屬於 writehost 的。
<dataHost name="host1" maxCon="1000" minCon="10" balance="0"
	writeType="0" dbType="mysql" dbDriver="native" switchType="1"
	slaveThreshold="100">
	<heartbeat>select user()</heartbeat> <!-- can have multi write hosts -->
	<writeHost host="hostM1" url="localhost:3306" user="root"
		password="123456"> <!-- can have multi read hosts -->
		<readHost host="hostS2" url="192.168.8.146:3306" user="root"
			password="xxx" />
	</writeHost>
	<writeHost host="hostS1" url="localhost:3316" user="root"
		password="123456" /> <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
balance:負載的配置,決定 select 語句的負載
作用
0 不開啟讀寫分離機制,所有讀操作都發送到當前可用的 writeHost 上。
1 所有讀操作都隨機發送到當前的 writeHost 對應的 readHost 和備用的 writeHost
2 所有的讀操作都隨機發送到所有的 writeHost,readHost 上
3 所有的讀操作都只發送到 writeHost 的 readHost 上
writeType:讀寫分離的配置,決定 update、delete、insert 語句的負載
作用
0 所有寫操作都發送到可用的 writeHost 上(預設第一個,第一個掛了以後發到第二個)
1 所有寫操作都隨機的傳送到 writeHost
switchType:主從切換配置
作用
-1 表示不自動切換
1 預設值,表示自動切換
2 基於 MySQL 主從同步的狀態決定是否切換,心跳語句為 show slave status
3 基於 MySQL galary cluster 的切換機制(適合叢集)(1.4.1),心跳語句為 show status like 'wsrep%'。
  • 1.2.3 rule.xml
定義了分片規則和演算法 分片規則:
<tableRule name="rang-long-cust">
	<rule>
		<columns>id</columns>
		<algorithm>func-rang-long-cust</algorithm>
	</rule>
</tableRule>
分片演算法:
<function name="func-rang-long-cust" class="io.mycat.route.function.AutoPartitionByLong">
	<property name="mapFile">rang-long-cust.txt</property>
</function
分片配置:rang-long-cust.txt
10001-20000=1 
0-10000=0 
20001-100000=2
3.2.4 ZK 配置 https://www.cnblogs.com/47Gamer/p/13656317.html Mycat 也支援 ZK 配置(用於管理配置和生成全域性 ID),執行 bin 目錄下init_zk_data.sh,會自動將 zkconf 下的所有配置檔案上傳到 ZK(先拷貝到這個目錄)。
cd /usr/local/soft/mycat/conf 
cp *.txt *.xml *.properties zkconf/ 

cd /usr/local/soft/mycat/bin 
./init_zk_data.sh
啟用 ZK 配置: mycat/conf/myid.properties
loadZk=true 
zkURL=127.0.0.1:2181 
clusterId=010 myid=01001 
clusterSize=1 
clusterNodes=mycat_gp_01 
#server booster ; booster install on db same server,will reset all minCon to 2 
type=server 
boosterDataHosts=dataHost1
注意如果執行 init_zk_data.sh 指令碼報錯的話,代表未寫入成功,此時不要啟用 ZK配置並重啟,否則本地檔案會被覆蓋。 啟動時如果 loadzk=true 啟動時,會自動從 zk 下載配置檔案覆蓋本地配置。 在這種情況下如果修改配置,需要先修改 conf 目錄的配置,copy 到 zkconf,再行上傳。
  • 1.2.5 啟動停止
進入 mycat/bin 目錄(注意要先啟動物理資料庫):
操作 命令
啟動 ./mycat start
停止 ./mycat stop
重啟 ./mycat restart
檢視狀態 ./mycat status
前臺執行 ./mycat console
連線:
mysql -uroot -p123456 -h 192.168.8.151 -P8066 catmall
1.3 Mycat 分片驗證 explain 可以用來看路由結果 在三個資料庫中建表
CREATE TABLE `customer` (
	`id` INT (11) DEFAULT NULL,
	`name` VARCHAR (255) DEFAULT NULL
) ENGINE = INNODB DEFAULT CHARSET = utf8;

CREATE TABLE `order_info` (
	`order_id` INT (11) NOT NULL COMMENT '訂單 ID',
	`uid` INT (11) DEFAULT NULL COMMENT '使用者 ID',
	`nums` INT (11) DEFAULT NULL COMMENT '商品數量',
	`state` INT (2) DEFAULT NULL COMMENT '訂單狀態',
	`create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '建立時間',
	`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
	PRIMARY KEY (`order_id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;

CREATE TABLE `order_detail` (
	`order_id` INT (11) NOT NULL COMMENT '訂單號',
	`id` INT (11) NOT NULL COMMENT '訂單詳情',
	`goods_id` INT (11) DEFAULT NULL COMMENT '貨品 ID',
	`price` DECIMAL (10, 2) DEFAULT NULL COMMENT '價格',
	`is_pay` INT (2) DEFAULT NULL COMMENT '支付狀態',
	`is_ship` INT (2) DEFAULT NULL COMMENT '是否發貨',
	`status` INT (2) DEFAULT NULL COMMENT '訂單詳情狀態',
	PRIMARY KEY (`order_id`, `id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;

CREATE TABLE `student` (
	`sid` INT (8) NOT NULL AUTO_INCREMENT,
	`name` VARCHAR (255) DEFAULT NULL,
	`qq` VARCHAR (255) DEFAULT NULL,
	PRIMARY KEY (`sid`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;
schema.xml
<table name="customer" dataNode="dn1,dn2,dn3" rule="rang-long-cust"
	primaryKey="id" />
<table name="order_info" dataNode="dn1,dn2,dn3" rule="mod-long-order">
	<childTable name="order_detail" joinKey="order_id"
		parentKey="order_id" primaryKey="id" />
</table>
<table name="student" dataNode="dn1,dn2,dn3" primaryKey="sid"
	type="global" />
資料節點配置
<dataNode name="dn1" dataHost="host1" database="gpcat" />
<dataNode name="dn2" dataHost="host2" database="gpcat" />
<dataNode name="dn3" dataHost="host3" database="gpcat" />
<dataHost balance="0" maxCon="1000" minCon="10" name="host1"
	writeType="0" switchType="1" slaveThreshold="100" dbType="mysql"
	dbDriver="native">
	<heartbeat>select user()</heartbeat>
	<writeHost host="hostM1" url="192.168.8.146:3306" password="123456"
		user="root" />
</dataHost>
<dataHost balance="0" maxCon="1000" minCon="10" name="host2"
	writeType="0" switchType="1" slaveThreshold="100" dbType="mysql"
	dbDriver="native">
	<heartbeat>select user()</heartbeat>
	<writeHost host="hostM1" url="192.168.8.150:3306" password="123456"
		user="root" />
</dataHost>
<dataHost balance="0" maxCon="1000" minCon="10" name="host3"
	writeType="0" switchType="1" slaveThreshold="100" dbType="mysql"
	dbDriver="native">
	<heartbeat>select user()</heartbeat>
	<writeHost host="hostM1" url="192.168.8.151:3306" password="123456"
		user="root" />
</dataHost>
schema——rule.xml——分片配置。
  • 1.3.1 範圍分片
<tableRule name="rang-long-cust">
	<rule>
		<columns>id</columns>
		<algorithm>rang-long-cust</algorithm>
	</rule>
</tableRule>
<function name="rang-long-cust" class="io.mycat.route.function.AutoPartitionByLong">
	<property name="mapFile">rang-long-cust.txt</property>
</function>
customer:
INSERT INTO `customer` (`id`, `name`) VALUES (6666, '趙先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (7777, '錢先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (16666, '孫先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (17777, '李先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (26666, '周先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (27777, '吳先生');
  • 1.3.2 取模分片(ER 表)
order_info:
<tableRule name="mod-long-order">
	<rule>
		<columns>order_id</columns>
		<algorithm>mod-long</algorithm>
	</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
	<property name="count">3</property>
</function>
INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`,`create_time`, `update_time`) 
VALUES (1, 1000001, 1, 2, '2019-9-23 14:35:37', '2019-9-23 14:35:37'); 
INSERT INTO `order_info` (`order_id`,`uid`, `nums`, `state`, `create_time`, `update_time`)
VALUES (2,1000002, 1, 2, '2019-9-24 14:35:37', '2019-9-24 14:35:37'); 
INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`,`update_time`) 
VALUES (3, 1000003, 3, 1, '2019-9-25 11:35:49','2019-9-25 11:35:49');
order_detail:
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`,`is_pay`, `is_ship`, `status`)
VALUES (3, 20180001, 85114752, 19.99, 1,1, 1); 
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`,`price`, `is_pay`, `is_ship`, `status`)
VALUES (1, 20180002, 25411251,1280.00, 1, 1, 0); 
INSERT INTO `order_detail` (`order_id`, `id`,`goods_id`, `price`, `is_pay`, `is_ship`, `status`) 
VALUES (1, 20180003,62145412, 288.00, 1, 1, 2); 
INSERT INTO `order_detail` (`order_id`,`id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) 
VALUES (2,20180004, 21456985, 399.00, 1, 1, 2); 
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`)
VALUES (2, 20180005, 21457452, 1680.00, 1, 1, 2); 
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`,`is_ship`, `status`) 
VALUES (2, 20180006, 65214789, 9999.00, 1, 1, 3);
  • 1.3.3 全域性表
student:
<table name="student" dataNode="dn1,dn2,dn3" primaryKey="sid" type="global"/>
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (1, '黑白','166669999'); 
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (2, 'AV哥', '466669999'); 
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (3, '最強菜鳥', '368828888'); 
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (4, '載入中', '655556666'); 
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (5, '貓老公', '265286999'); 
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (6, '一個人的精彩', '516895555');
1.4 Mycat 全域性 ID Mycat 全域性序列實現方式主要有 4 種:本地檔案方式、資料庫方式、本地時間戳演算法、ZK。也可以自定義業務序列。 注意獲取全域性 ID 的字首都是:MYCATSEQ_
  • 1.4.1 檔案方式
配置檔案 server.xml sequnceHandlerType 值: 0 檔案 1 資料庫 2 本地時間戳 3 ZK
<property name="sequnceHandlerType">0</property>
檔案方式,配置 conf/sequence_conf.properties:
CUSTOMER.HISIDS= 
CUSTOMER.MINID=10000001 
CUSTOMER.MAXID=20000000 
CUSTOMER.CURID=10000001
語法:select next value for MYCATSEQ_CUSTOMER
INSERT INTO `customer` (`id`, `name`) VALUES (next value for MYCATSEQ_CUSTOMER, 'qingshan');
優點:本地載入,讀取速度較快。 缺點:當 Mycat 重新發布後,配置檔案中的 sequence 需要替換。Mycat 不能做叢集部署。
  • 1.4.2 資料庫方式
<property name="sequnceHandlerType">1</property>
配置: sequence_db_conf.properties 把這張表建立在 146 上,所以是 dn1
#sequence stored in datanode 
GLOBAL=dn1 
CUSTOMER=dn1
在第一個資料庫節點上建立 MYCAT_SEQUENCE 表:
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,
	remark VARCHAR (100),
	PRIMARY KEY (NAME)
) ENGINE = INNODB;
注:可以在 schema.xml 配置檔案中配置這張表,供外部訪問。
<table name="mycat_sequence" dataNode="dn1" autoIncrement="true" primaryKey="id"></table>
建立儲存過程——獲取當前 sequence 的值
DROP FUNCTION
IF EXISTS `mycat_seq_currval`;

DELIMITER;

;

CREATE DEFINER = `root`@`%` FUNCTION `mycat_seq_currval` (seq_name VARCHAR(50)) RETURNS VARCHAR (64) CHARSET latin1 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;
建立儲存過程,獲取下一個 sequence:
DROP FUNCTION
IF EXISTS `mycat_seq_nextval`;

DELIMITER;

;

CREATE DEFINER = `root`@`%` FUNCTION `mycat_seq_nextval` (seq_name VARCHAR(50)) RETURNS VARCHAR (64) CHARSET latin1 DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment
WHERE
	NAME = seq_name;

RETURN mycat_seq_currval (seq_name);


END;

;

DELIMITER;
建立儲存過程,設定 sequence:
DROP FUNCTION
IF EXISTS `mycat_seq_setval`;

DELIMITER;

;

CREATE DEFINER = `root`@`%` FUNCTION `mycat_seq_setval` (
	seq_name VARCHAR (50),

VALUE
	INTEGER
) RETURNS VARCHAR (64) CHARSET latin1 DETERMINISTIC
BEGIN
	UPDATE MYCAT_SEQUENCE
SET current_value =
VALUE

WHERE
	NAME = seq_name;

RETURN mycat_seq_currval (seq_name);


END;

;

DELIMITER;
插入記錄:
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ('GLOBAL', 1, 100,''); 
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ('ORDERS', 1, 100,'訂單表使用');
測試:
select next value for MYCATSEQ_ORDERS
  • 1.4.3 本地時間戳方式
ID= 64 位二進位制 (42(毫秒)+5(機器 ID)+5(業務編碼)+12(重複累加) ,長度為18 位
<property name="sequnceHandlerType">2</property>
配置檔案 sequence_time_conf.properties:
#sequence depend on TIME 
WORKID=01
DATAACENTERID=01
驗證:select next value for MYCATSEQ_GLOBAL
  • 1.4.4 ZK 方式
修改 conf/myid.properties 設定 loadZk=true(啟動時會從 ZK 載入配置,一定要注意備份配置檔案,並且先用 bin/init_zk_data.sh,把配置檔案寫入到 ZK)
<property name="sequnceHandlerType">3</property>
配置檔案:sequence_distributed_conf.properties:
# 代表使用 zk 
INSTANCEID=ZK 
# 與 myid.properties 中的 CLUSTERID 設定的值相同 
CLUSTERID=010
複製配置檔案
cd /usr/local/soft/mycat/conf 
cp *.txt *.xml *.properties zkconf/ 
chown -R zkconf/  
cd /usr/local/soft/mycat/bin 
./init_zk_data.sh
驗證:select next value for MYCATSEQ_GLOBAL
  • 1.4.5 使用
在 schema.xml 的 table 標籤上配置 autoIncrement="true",不需要獲取和指定序列的情況下,就可以使用全域性 ID 了。