Mycat使用
主從複製
複製的基本原理
slave 會從master 讀取 binlog 來進行數據同步
- master將改變記錄到二進制日誌(binary log)。這些記錄過程叫做二進制日誌事件,binary log events
- slave 將 master 的 binary log events 拷貝到它的中繼日誌(relay log)
- slave 重做中繼日誌中的事件,將改變應用到自己的數據庫中。 MySQL 複製是異步的且串行化的
複製的基本原則
- 每個 master 可以有多個 salve
- 每個 slave 只有一個 master
- 每個 slave 只能有一個唯一的服務器 ID
配置思路
- 配置主機,win的 my.ini,注意是:
[mysqld]
下
[mysqld]
# The TCP/IP Port the MySQL Server will listen on
port=3306
server-id=1
log-bin=自己本地的路徑/data/mysqlbin #自定義的路徑
binlog-ignore-db=mysql
binlog-do-db=需要複製的主數據庫名字
binlog_format=STATEMENT
binlog_format 有三種:STATEMENT(默認,記錄寫操作sql,函數調用會出錯)、ROW(記錄每行變化,效率問題)、MIXED(有函數切換到R,無函數切換到S,有系統變量會出錯)
- 從機,修改 Linux 中的 my.cnf(/etc 目錄)
[mysqld]
#加入這些
server-id = 2
relay-log=mysql-relay
- 重啓,模擬時關閉防火牆
- 在 Windows 主機上建立帳戶並授權 slave,查看
執行完此步驟後不要再操作主服務器MYSQL,防止主服務器狀態值變化
GRANT REPLICATION SLAVE ON *.* TO 'zhangsan'@'從機器數據庫IP' IDENTIFIED BY '123456';
show master status;
- 在Linux 從機上配置需要複製的主機
CHANGE MASTER TO MASTER_HOST='主機IP',
MASTER_USER='zhangsan',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='File名字',
MASTER_LOG_POS=Position數字;
start slave;
show slave status\G;# 查看是否配置成功
主機新建庫、新建表、insert記錄,從機複製
進入正題
概述
是什麼?
Mycat 是數據庫中間件,前身是阿里的 cobar,屬於 proxy 層方案。
拓展:sharding-jdbc:噹噹開源的,屬於 client 層方案。
能夠幹什麼?
- 讀寫分離
- 數據分片
- 垂直拆分
- 水平拆分
- 垂直+水平拆分
- 多數據源整合
原理
Mycat 的原理中最重要的一個動詞是“攔截”,它攔截了用戶發送過來的 SQL 語句,
- 首先對 SQL 語句做了一些特定的分析:如分片分析、路由分析、讀寫分離分析、緩存分析等,
- 然後將此 SQL 發往後端的真實數據庫,並將返回的結果做適當的處理,
- 最終再返回給用戶
所以,我們只要配置 mycat,它會攔截,自動發給對應服務器處理
安裝使用
下載地址:http://www.mycat.org.cn/
修改配置文件
- schema.xml:定義邏輯庫,表、分片節點等內容
- rule.xml:定義分片規則
- server.xml:定義用戶以及系統相關變量,如端口等.
啓動前先修改 schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!--邏輯庫 name名稱, checkSQLschema sqlMaxLimit 末尾是否要加 limit xxx-->
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> </schema>
<!--邏輯庫 name名稱, dataHost 引用的哪個dataHost database:對應mysql的database-->
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataHost name="localhost1" 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">
</writeHost>
</dataHost>
</mycat:schema>
再修改 server.xml
<user name="root">
<property name="password">654321</property>
<property name="schemas">TESTDB</property>
</user>
驗證數據庫訪問情況:
mysql -uroot -p123123 -h 192.168.154.1 -P 3306
mysql -uroot -p123123 -h 192.168.154.154 -P 3306
# 如本機遠程訪問報錯,請建對應用戶
grant all privileges on *.* to root@'缺少的host' identified by '123123';
啓動程序:
- 控制檯啓動 :去mycat/bin 目錄下 mycat console
- 後臺啓動 :去mycat/bin 目錄下 mycat start
啓動時可能出現報錯:域名解析失敗
- 用 vim 修改 /etc/hosts 文件 (在 127.0.0.1 後面增加你的機器名)
- 修改後重新啓動網絡服務 service network restart
登錄:
- 後臺管理窗口
- mysql -uroot -p654321 -P9066 -h192.168.67.131
- show database
- show @@help
- 數據窗口
- mysql -uroot -p654321 -P8066 -h192.168.67.131
讀寫分離
注意:必須完成主從複製的搭建,才能實現讀寫分離
配置 schema.xml,在前面的基礎上加了讀庫配置
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
</schema>
<dataNode name="dn1" dataHost="host1" database="atguigu_mc" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="3"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostm1" url="192.168.67.1:3306" user="root"
password="123123">
<!--讀庫(從庫)的配置 -->
<readHost host="hosts1" url="192.168.67.131:3306" user="root"
password="123123">
</readHost>
</writeHost>
</dataHost>
</mycat:schema>
負載均衡類型,目前的取值有4 種:
balance="0"
, 不開啓讀寫分離機制,所有讀操作都發送到當前可用的 writeHost 上。balance="1"
,全部的 readHost 與 stand by writeHost 參與 select 語句的負載均衡,簡單的說,當雙主雙從模式(M1->S1,M2->S2,並且 M1 與 M2 互爲主備),正常情況下,M2,S1,S2 都參與 select 語句的負載均衡。balance="2"
,所有讀操作都隨機的在 writeHost、readhost 上分發。balance="3"
,所有讀請求隨機的分發到 readhost 執行,writerHost 不負擔讀壓力
讀寫分離:
- 創建表 t_replica
- 分別在兩個庫下插入:
insert into t_replica(name) values (@@hostname)
- 然後再 mycat下執行 select * from t_replica 查詢實驗
垂直分庫
一個庫的瓶頸約爲 5kw,一個表的瓶頸約爲 500w
水平與垂直拆分
- 水平拆分的意思,就是把一個表的數據給弄到多個庫的多個表裏去,但是每個庫的表結構都一樣,只不過每個庫表放的數據是不同的,所有庫表的數據加起來就是全部數據。用多個庫來抗更高的併發,還有就是用多個庫的存儲容量來進行擴容。
- 垂直拆分的意思,就是把一個有很多字段的表給拆分成多個表,或者是多個庫上去。一般來說,會將較少的訪問頻率很高的字段放到一個表裏去,然後將較多的訪問頻率很低的字段放到另外一個表裏去。
場景
有一個庫,
- #客戶表 rows:20萬
- #訂單表 rows:600萬
- #訂單詳細表 rows:600萬
- #訂單狀態字典表 rows:20萬
垂直分庫操作
- 搭建兩個乾淨的庫,通過 mycat 做分庫操作
- 拆分時需注意 join 關聯情況要避免
配置 schema.xml
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<table name="customer" dataNode="dn2" ></table>
</schema>
<dataNode name="dn1" dataHost="host1" database="atguigu_mc" />
<!--配置 dn2-->
<dataNode name="dn2" dataHost="host2" database="atguigu_sm" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="3"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostm1" url="192.168.67.1:3306" user="root"
password="123123">
<readHost host="hosts1" url="192.168.67.131:3306" user="root"
password="123123">
</readHost>
</writeHost>
</dataHost>
<!--配置 host2,balance改爲0,不做讀寫分離 -->
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostm2" url="192.168.67.1:3306" user="root"
password="123123">
</writeHost>
</dataHost>
</mycat:schema>
若未建立新分庫,則會報錯哦
水平分表
水平分表
- 我們拆分 order 表
修改 schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<table name="customer" dataNode="dn2" ></table>
<!--配置要分的 order 表,指定rule 算法-->
<table name="orders" dataNode="dn1,dn2" rule="mod_rule" ></table>
</schema>
<dataNode name="dn1" dataHost="host1" database="atguigu_mc" />
<dataNode name="dn2" dataHost="host2" database="atguigu_sm" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="2"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostm1" url="192.168.67.1:3306" user="root"
password="123123">
<!-- <readHost host="hosts1" url="192.168.67.131:3306" user="root"
password="123123">
</readHost>-->
</writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostm2" url="192.168.67.1:3306" user="root"
password="123123">
</writeHost>
</dataHost>
修改 rule.xml
<tableRule name="mod_rule">
<!--配置分片規則,注意字段名啊,要和表中一致 -->
<rule>
<columns>customer_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- 節點數記得改啊 -->
<property name="count">2</property>
</function>
關聯表
跨庫無法 join 查詢,那關聯表(訂單詳情表)怎麼拆呢?
配置 ER 表:爲了相關聯的表的行儘量分在一個庫下,這裏就是訂單詳情表的配置
配置 schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<table name="customer" dataNode="dn2" ></table>
<table name="orders" dataNode="dn1,dn2" rule="mod_rule" >
<!--在這裏指定關聯表 -->
<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>
<!--在這裏指定全局表 -->
<table name="dict_status" dataNode="dn1,dn2" type="global" ></table>
</schema>
<dataNode name="dn1" dataHost="host1" database="atguigu_mc" />
<dataNode name="dn2" dataHost="host2" database="atguigu_sm" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="2"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostm1" url="192.168.67.1:3306" user="root"
password="123123">
<!-- <readHost host="hosts1" url="192.168.67.131:3306" user="root"
password="123123">
</readHost>-->
</writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostm2" url="192.168.67.1:3306" user="root"
password="123123">
</writeHost>
</dataHost>
</mycat:schema>
全局表:每個庫都會使用的表,一般數據量不會過大,這裏就是訂單狀態字典表的配置
- 設定爲全局的表,會直接複製給每個數據庫一份,所有寫操作也會同步給多個庫。
- 所以全局表一般不能是大數據表或者更新頻繁的表
- 一般是字典表或者系統表爲宜。
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<table name="customer" dataNode="dn2" ></table>
<table name="orders" dataNode="dn1,dn2" rule="mod_rule" ></table>
<table name="dict_order_type" dataNode="dn1,dn2" type="global" ></table>
</schema>
<dataNode name="dn1" dataHost="host1" database="atguigu_mc" />
<dataNode name="dn2" dataHost="host2" database="atguigu_sm" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="2"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostm1" url="192.168.67.1:3306" user="root"
password="123123">
<!-- <readHost host="hosts1" url="192.168.67.131:3306" user="root"
password="123123">
</readHost>-->
</writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostm2" url="192.168.67.1:3306" user="root"
password="123123">
</writeHost>
</dataHost>
分表小結
- 直接根據 rule 算法分表:如訂單表
- ER 表(childTable):如訂單詳情表 隨着訂單表
連帶
分 - 全局表:每個庫都會複製的表,如字典表、系統表
全局序列
用於:保證配置多個節點時,如訂單 id 不會重複
幾種方式:
- 本地文件:不推薦,抗風險能力太差
- 時間戳方式:不推薦,太長了
- 數據庫方式
數據庫方式
原理
- 利用數據庫一個表 來進行計數累加。
- 但是並不是每次生成序列都讀寫數據庫,這樣效率太低
- mycat 會預加載一部分號段到 mycat 的內存中,這樣大部分讀寫序列都是在內存中完成的。
- 如果內存中的號段用完了 mycat會再向數據庫要一次。
那如果mycat崩潰了 ,那內存中的序列豈不是都沒了?
是的。如果是這樣,那麼 mycat 啓動後會向數據庫申請新的號段,原有號段會棄用。
也就是說如果mycat重啓,那麼損失是當前的號段沒用完的號碼,但是不會因此出現主鍵重複。
建庫序列腳本
腳本:
win10
DELIMITER $$
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)
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;
DELIMITER $$
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),VALUE INTEGER) RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = VALUE
WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;
DELIMITER $$
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER;
測試:
CREATE TABLE MYCAT_SEQUENCE (NAME VARCHAR(50) NOT NULL,current_value INT NOT
NULL,increment INT NOT NULL DEFAULT 100, PRIMARY KEY(NAME)) ENGINE=INNODB;
SELECT * FROM MYCAT_SEQUENCE
TRUNCATE TABLE MYCAT_SEQUENCE
##增加要用的序列
INSERT INTO MYCAT_SEQUENCE(NAME,current_value,increment) VALUES ('ORDERS', 400000,
100);
修改 mycat 配置
- vim sequence_db_conf.properties ,修改序列對應的節點,參考schema.xml
- vim server.xml,改成1,使用數據庫方式
<property name="sequnceHandlerType">1<property>
自主生成
- 根據業務邏輯組合
- 可以利用 redis 的單線程原子性 incr來生成序列