[04][03][02] Mycat 進(jìn)階

MySQL 主從復(fù)制

主從復(fù)制的含義

在 MySQL 多服務(wù)器的架構(gòu)中,至少要有一個主節(jié)點(master),跟主節(jié)點相對的, 我們把它叫做從節(jié)點(slave)。主從復(fù)制,就是把主節(jié)點的數(shù)據(jù)復(fù)制到一個或者多個從 節(jié)點。主服務(wù)器和從服務(wù)器可以在不同的 IP 上,通過遠(yuǎn)程連接來同步數(shù)據(jù),這個是異步 的過程

主從復(fù)制的形式

一主一從/一主多從


多主一從


雙主復(fù)制


級聯(lián)復(fù)制


主從復(fù)制的用途

數(shù)據(jù)備份:把數(shù)據(jù)復(fù)制到不同的機器上,以免單臺服務(wù)器發(fā)生故障時數(shù)據(jù)丟失
讀寫分離:讓主庫負(fù)責(zé)寫,從庫負(fù)責(zé)讀,從而提高讀寫的并發(fā)度
高可用 HA:當(dāng)節(jié)點故障時,自動轉(zhuǎn)移到其他節(jié)點,提高可用性
擴展:結(jié)合負(fù)載的機制,均攤所有的應(yīng)用訪問請求,降低單機 IO

主從復(fù)制是怎么實現(xiàn)的呢? 回顧:Redis 主從復(fù)制怎么實現(xiàn)的?

binlog

客戶端對 MySQL 數(shù)據(jù)庫進(jìn)行操作的時候,包括 DDL 和 DML 語句,服務(wù)端會在日 志文件中用事件的形式記錄所有的操作記錄,這個文件就是 binlog 文件(屬于邏輯日志, 跟 Redis 的 AOF 文件類似)
基于 binlog,我們可以實現(xiàn)主從復(fù)制和數(shù)據(jù)恢復(fù)
Binlog 默認(rèn)是不開啟的,需要在服務(wù)端手動配置。注意有一定的性能損耗

binlog 配置

編輯 /etc/my.cnf

server-id=1

重啟 MySQL 服務(wù)

service mysqld stop 
service mysqld start

## 如果出錯查看日志
vi /var/log/mysqld.log
cd /var/lib/mysql

是否開啟 binlog

show variables like 'log_bin%';

binlog 格式

STATEMENT:記錄每一條修改數(shù)據(jù)的 SQL 語句(減少日志量,節(jié)約 IO)
ROW:記錄哪條數(shù)據(jù)被修改了,修改成什么樣子了(5.7 以后默認(rèn))
MIXED:結(jié)合兩種方式,一般的語句用 STATEMENT,函數(shù)之類的用 ROW

查看 binlog 格式:

show global variables like '%binlog_format%';

查看 binlog 列表

show binary logs

查看 binlog 內(nèi)容

show binlog events in 'mysql-bin.000001';

用 mysqlbinlog 工具,基于時間查看 binlog (注意這個是 Linux 命令, 不是 SQL)

/usr/bin/mysqlbinlog --start-datetime='2019-08-22 13:30:00' --stop-datetime='2019-08-22 14:01:01' -d gupao /var/lib/mysql/mysql-bin.000001

主從復(fù)制原理

主從復(fù)制配置

  • 主庫開啟 binlog,設(shè)置 server-id
  • 在主庫創(chuàng)建具有復(fù)制權(quán)限的用戶,允許從庫連接
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'192.168.8.147' IDENTIFIED BY '123456';
FLUSH PRIVILEGES;
  • 從庫/etc/my.cnf 配置,重啟數(shù)據(jù)庫
server-id=2
log-bin=mysql-bin
relay-log=mysql-relay-bin
read-only=1
log-slave-updates=1

log-slave-updates 決定了在從 binlog 讀取數(shù)據(jù)時,是否記錄 binlog,實現(xiàn)雙主和 級聯(lián)的關(guān)鍵

  • 在從庫執(zhí)行
stop slave;
change master to master_host='192.168.8.146',master_user='repl',master_password='123456',master_log_file='mysql-bin.000001', master_log_pos=4;
start slave
  • 查看同步狀態(tài)
SHOW SLAVE STATUS \G

以下為正常:


主從復(fù)制原理

這里面涉及到幾個線程:


  • slave 服務(wù)器執(zhí)行 start slave,開啟主從復(fù)制開關(guān), slave 服務(wù)器的 IO 線程請 求從 master 服務(wù)器讀取 binlog(如果該線程追趕上了主庫,會進(jìn)入睡眠狀態(tài))
  • master 服務(wù)器創(chuàng)建 Log Dump 線程,把 binlog 發(fā)送給 slave 服務(wù)器。slave 服 務(wù)器把讀取到的 binlog 日志內(nèi)容寫入中繼日志 relay log(會記錄位置信息,以便下次繼 續(xù)讀?。?/li>
  • slave 服務(wù)器的 SQL 線程會實時檢測 relay log 中新增的日志內(nèi)容,把 relay log 解析成 SQL 語句,并執(zhí)行

Mycat 高可用

目前 Mycat 沒有實現(xiàn)對多 Mycat 集群的支持,可以暫時使用 HAProxy 來做負(fù)載 思路:HAProxy 對 Mycat 進(jìn)行負(fù)載。Keepalived 實現(xiàn) VIP


Mycat 注解

注解的作用

當(dāng)關(guān)聯(lián)的數(shù)據(jù)不在同一個節(jié)點的時候,Mycat 是無法實現(xiàn)跨庫 join 的
舉例:
如果直接在 150 插入主表數(shù)據(jù),151 插入明細(xì)表數(shù)據(jù),此時關(guān)聯(lián)查詢無法查詢出來

-- 150 節(jié)點插入
INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`, `update_time`) VALUES (9, 1000003, 2673, 1, '2019-9-25 11:35:49', '2019-9-25 11:35:49');

-- 151 節(jié)點插入
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (9, 20180001, 2673, 19.99, 1, 1, 1);

在 mycat 數(shù)據(jù)庫查詢,直接查詢沒有結(jié)果

select a.order_id,b.price from order_info a, order_detail b where a.nums = b.goods_id;

Mycat 作為一個中間件,有很多自身不支持的 SQL 語句,比如存儲過程,但是這些 語句在實際的數(shù)據(jù)庫節(jié)點上是可以執(zhí)行的。有沒有辦法讓 Mycat 做一層透明的代理轉(zhuǎn)發(fā), 直接找到目標(biāo)數(shù)據(jù)節(jié)點去執(zhí)行這些 SQL 語句呢?
那我們必須要有一種方式告訴 Mycat 應(yīng)該在哪個節(jié)點上執(zhí)行。這個就是 Mycat 的注 解。我們在需要執(zhí)行的 SQL 語句前面加上一段代碼,幫助 Mycat 找到我們的目標(biāo)節(jié)點

注解的用法

注解的形式是 :
/*!mycat: sql=注解 SQL 語句/
注解的使用方式是 :
/*!mycat: sql=注解 SQL 語句
/ 真正執(zhí)行的 SQL

使用時將 = 號后的 "注解 SQL 語句" 替換為需要的 SQL 語句即可
使用注解有一些限制,或者注意的地方:

原始 SQL 注解 SQL
select 如果需要確定分片,則使用能確定分片的注解,比如/!mycat: sql=select * from users where user_id=1/ 如果要在所有分片上執(zhí)行則可以不加能確定分片的條件
insert 使用 insert 的表作為注解 SQL,必須能確定到某個分片 原始 SQL 插入的字段必須包括分片字段 非分片表(只在某個節(jié)點上):必須能確定到某個分片
delete 使用 delete 的表作為注解 SQL
update 使用 update 的表作為注解 SQL

使用注解并不額外增加 MyCat 的執(zhí)行時間;從解析復(fù)雜度以及性能考慮,注解 SQL 應(yīng)盡量簡單,因為它只是用來做路由的
注解可以幫我們解決什么問題呢?

注解使用示例

創(chuàng)建表或存儲過程

customer.id=1 全部路由到 146

-- 存儲過程
/*!mycat: sql=select * from customer where id =1 */ CREATE PROCEDURE test_proc() BEGIN END;

-- 表
/*!mycat: sql=select * from customer where id =1 */ CREATE TABLE test2(id INT); 

特殊語句自定義分片

Mycat 本身不支持 insert select,通過注解支持

/*!mycat: sql=select * from customer where id =1 */ INSERT INTO test2(id) SELECT id FROM order_detail;

多表 ShareJoin

/*!mycat:catlet=io.mycat.catlets.ShareJoin */
select a.order_id,b.price from order_info a, order_detail b where a.nums = b.goods_id;

如果你在錄播中看到翻車了,可以去看這篇文章: https://gper.club/articles/7e7e7f7ff7g59gc1g68

讀寫分離

讀寫分離: 配置 Mycat 讀寫分離后,默認(rèn)查詢都會從讀節(jié)點獲取數(shù)據(jù),但是有些場 景需要獲取實時數(shù)據(jù),如果從讀節(jié)點獲取數(shù)據(jù)可能因延時而無法實現(xiàn)實時,Mycat 支持 通過注解 /*balance*/ 來強制從寫節(jié)點(write host)查詢數(shù)據(jù)

/*balance*/ select a.* from customer a where a.id=6666;

讀寫分離數(shù)據(jù)庫選擇(1.6 版本之后)

/*!mycat: db_type=master */ select * from customer;
/*!mycat: db_type=slave */ select * from customer;
/*#mycat: db_type=master */ select * from customer;
/*#mycat: db_type=slave */ select * from customer;

注解支持的'! '不被 mysql 單庫兼容
注解支持的'#'不被 MyBatis 兼容
隨著 Mycat 的開發(fā),更多的新功能正在加入

注解原理

Mycat 在執(zhí)行 SQL 之前會先解析 SQL 語句,在獲得分片信息后再到對應(yīng)的物理節(jié) 點上執(zhí)行。如果 SQL 語句無法解析,則不能被執(zhí)行。如果語句中有注解,則會先解析注 解的內(nèi)容獲得分片信息,再把真正需要執(zhí)行的 SQL 語句發(fā)送對對應(yīng)的物理節(jié)點上
所以我們在使用主機的時候,應(yīng)該清楚地知道目標(biāo) SQL 應(yīng)該在哪個節(jié)點上執(zhí)行,注 解的 SQL 也指向這個分片,這樣才能使用。如果注解沒有使用正確的條件,會導(dǎo)致原始 SQL 被發(fā)送到所有的節(jié)點上執(zhí)行,造成數(shù)據(jù)錯誤

分片策略詳解

Mycat 權(quán)威指南.pdf Page 116 分片的目標(biāo)是將大量數(shù)據(jù)和訪問請求均勻分布在多個節(jié)點上,通過這種方式提升數(shù) 據(jù)服務(wù)的存儲和負(fù)載能力

Mycat 分片策略詳解

總體上分為連續(xù)分片和離散分片,還有一種是連續(xù)分片和離散分片的結(jié)合,例如先 范圍后取模


比如范圍分片(id 或者時間)就是典型的連續(xù)分片,單個分區(qū)的數(shù)量和邊界是確定 的。離散分片的分區(qū)總數(shù)量和邊界是確定的,例如對 key 進(jìn)行哈希運算,或者再取模
關(guān)鍵詞:范圍查詢、熱點數(shù)據(jù)、擴容

連續(xù)分片優(yōu)點:

  • 范圍條件查詢消耗資源少(不需要匯總數(shù)據(jù))
  • 擴容無需遷移數(shù)據(jù)(分片固定)

連續(xù)分片缺點:

  • 存在數(shù)據(jù)熱點的可能性
  • 并發(fā)訪問能力受限于單一或少量 DataNode(訪問集中)

離散分片優(yōu)點:

  • 并發(fā)訪問能力增強(負(fù)載到不同的節(jié)點)
  • 范圍條件查詢性能提升(并行計算)

離散分片缺點:

  • 數(shù)據(jù)擴容比較困難,涉及到數(shù)據(jù)遷移問題
  • 數(shù)據(jù)庫連接消耗比較多

連續(xù)分片

范圍分片(已演示)

<tableRule name="auto-sharding-long">
    <rule>
        <columns>id</columns>
        <algorithm>rang-long</algorithm>
    </rule>
</tableRule>

<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
    <property name="mapFile">autopartition-long.txt</property>
</function>

# range start-end ,data node index
# K=1000,M=10000
0-500M=0
500M-1000M=1
1000M-1500M=2

特點:容易出現(xiàn)冷熱數(shù)據(jù)

按自然月分片

建表語句

CREATE TABLE `sharding_by_month` (
    `create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

邏輯表

<schema name="catmall" checkSQLschema="false" sqlMaxLimit="100">
    <table name="sharding_by_month" dataNode="dn1,dn2,dn3" rule="qs-sharding-by-month" />
</schema>

分片規(guī)則

<tableRule name="sharding-by-month">
    <rule>
        <columns>create_time</columns>
        <algorithm>qs-partbymonth</algorithm>
    </rule>
</tableRule>

分片算法

<function name="qs-partbymonth" class="io.mycat.route.function.PartitionByMonth">
    <property name="dateFormat">yyyy-MM-dd</property>
    <property name="sBeginDate">2019-10-01</property>
    <property name="sEndDate">2019-12-31</property>
</function>

columns 標(biāo)識將要分片的表字段,字符串類型,與 dateFormat 格式一致
algorithm 為分片函數(shù)
dateFormat 為日期字符串格式
sBeginDate 為開始日期
sEndDate 為結(jié)束日期

注意:節(jié)點個數(shù)要大于月份的個數(shù)

測試語句

INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-10-16', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-10-27', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-11-04', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-11-11', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-12-25', database());
INSERT INTO sharding_by_month (create_time,db_nm) VALUES ('2019-12-31', database());

另外還有按天分片(可以指定多少天一個分片)、按小時分片

離散分片

枚舉分片

將所有可能出現(xiàn)的值列舉出來,指定分片。例如:全國 34 個省,要將不同的省的數(shù) 據(jù)存放在不同的節(jié)點,可用枚舉的方式
建表語句:

CREATE TABLE `sharding_by_intfile` (
    `age` int(11) NOT NULL,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

邏輯表:

<table name="sharding_by_intfile" dataNode="dn$1-3" rule="qs-sharding-by-intfile" />

分片規(guī)則:

<tableRule name="sharding-by-intfile">
    <rule>
        <columns>sharding_id</columns>
        <algorithm>hash-int</algorithm>
    </rule>
</tableRule>

分片算法:

<function name="hash-int" class="org.opencloudb.route.function.PartitionByFileMap">
    <property name="mapFile">partition-hash-int.txt</property>
    <property name="type">0</property>
    <property name="defaultNode">0</property>
</function>

type:默認(rèn)值為 0,0 表示 Integer,非零表示 String
PartitionByFileMap.java,通過 map 來實現(xiàn)
策略文件:partition-hash-int.txt

16=0
17=1
18=2

插入數(shù)據(jù)測試:

INSERT INTO `sharding_by_intfile` (age,db_nm) VALUES (16, database());
INSERT INTO `sharding_by_intfile` (age,db_nm) VALUES (17, database());
INSERT INTO `sharding_by_intfile` (age,db_nm) VALUES (18, database());

特點:適用于枚舉值固定的場景

一致性哈希

一致性 hash 有效解決了分布式數(shù)據(jù)的擴容問題
建表語句:

CREATE TABLE `sharding_by_murmur` (
    `id` int(10) DEFAULT NULL,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

邏輯表

<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
    <table name="sharding_by_murmurhash" primaryKey="id" dataNode="dn$1-3" rule="sharding-by-murmur" />
/schema>

分片規(guī)則

<tableRule name="sharding-by-murmur">
    <rule>
        <columns>id</columns>
        <algorithm>qs-murmur</algorithm>
    </rule>
</tableRule>

分片算法

<function name="qs-murmur" class="io.mycat.route.function.PartitionByMurmurHash">
    <property name="seed">0</property>
    <property name="count">3</property>
    <property name="virtualBucketTimes">160</property>
</function>

測試語句

INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (1, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (2, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (3, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (4, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (5, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (6, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (7, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (8, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (9, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (10, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (11, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (12, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (13, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (14, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (15, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (16, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (17, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (18, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (19, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (20, database());

特點:可以一定程度減少數(shù)據(jù)的遷移

十進(jìn)制取模分片(已演示)

根據(jù)分片鍵進(jìn)行十進(jìn)制求模運算

<tableRule name="mod-long">
    <rule>
        <columns>sid</columns>
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <!-- how many data nodes -->
    <property name="count">3</property>
</function>

特點:分布均勻,但是遷移工作量比較大

固定分片哈希

這是先求模得到邏輯分片號,再根據(jù)邏輯分片號直接映射到物理分片的一種散列算 法
建表語句:

CREATE TABLE `sharding_by_long` (
    `id` int(10) DEFAULT NULL,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

邏輯表

<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
    <table name="sharding_by_long" dataNode="dn$1-3" rule="qs-sharding-by-long" />
</schema>

分片規(guī)則

<tableRule name="qs-sharding-by-long">
    <rule>
        <columns>id</columns>
        <algorithm>qs-sharding-by-long</algorithm>
    </rule>
</tableRule>

平均分成 8 片(%1024 的余數(shù),1024=128*8):

<function name="qs-sharding-by-long" class="io.mycat.route.function.PartitionByLong">
    <property name="partitionCount">8</property>
    <property name="partitionLength">128</property>
</function>

partitionCount 為指定分片個數(shù)列表
partitionLength 為分片范圍列表


第二個例子:
兩個數(shù)組,分成不均勻的 3 個節(jié)點(%1024 的余數(shù),1024=2256+1512):

<function name="qs-sharding-by-long" class="io.mycat.route.function.PartitionByLong">
    <property name="partitionCount">2,1</property>
    <property name="partitionLength">256,512</property>
</function>

3 個節(jié)點,對 1024 取模余數(shù)的分布


測試語句

INSERT INTO `sharding_by_long` (id,db_nm) VALUES (222, database());
INSERT INTO `sharding_by_long` (id,db_nm) VALUES (333, database());
INSERT INTO `sharding_by_long` (id,db_nm) VALUES (666, database());

特點:在一定范圍內(nèi) id 是連續(xù)分布的。

取模范圍分片

邏輯表

<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
    <table name="sharding_by_pattern" primaryKey="id" dataNode="dn$0-10" rule="qs-sharding-by-pattern"/>
</schema>

建表語句

CREATE TABLE `sharding_by_pattern` (
    `id` varchar(20) DEFAULT NULL,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

分片規(guī)則

<tableRule name="sharding-by-pattern">
    <rule>
        <columns>user_id</columns>
        <algorithm>sharding-by-pattern</algorithm>
    </rule>
</tableRule>

分片算法

<function name="sharding-by-pattern" class=" io.mycat.route.function.PartitionByPattern">
    <property name="patternValue">100</property>
    <property name="defaultNode">0</property>
    <property name="mapFile">partition-pattern.txt</property>
</function>

patternValue 取?;鶖?shù),這里設(shè)置成 100
partition-pattern.txt,一共 3 個節(jié)點
id=19%100=19,在 dn1;
id=222%100=22,dn2;
id=371%100=71,dn3

# id partition range start-end ,data node index
###### first host configuration
1-20=0
21-70=1
71-100=2
0-0=0

測試語句

INSERT INTO `sharding_by_pattern` (id,db_nm) VALUES (19, database());
INSERT INTO `sharding_by_pattern` (id,db_nm) VALUES (222, database());
INSERT INTO `sharding_by_pattern` (id,db_nm) VALUES (371, database());

特點:可以調(diào)整節(jié)點的數(shù)據(jù)分布

范圍取模分片

建表語句

CREATE TABLE `sharding_by_rang_mod` (
    `id` bigint(20) DEFAULT NULL,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

邏輯表

<schema name="test" checkSQLschema="false" sqlMaxLimit="100">
    <table name="sharding_by_rang_mod" dataNode="dn$1-3" rule="qs-sharding-by-rang-mod" />
</schema>

分片規(guī)則

<tableRule name="qs-sharding-by-rang-mod">
    <rule>
        <columns>id</columns>
        <algorithm>qs-rang-mod</algorithm>
    </rule>
</tableRule>

分片算法

<function name="qs-rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
    <property name="mapFile">partition-range-mod.txt</property>
</function>

partition-range-mod.txt

# range start-end ,data node group size
0-20000=1
20001-40000=2

解讀:先范圍后取模。Id 在 20000 以內(nèi)的,全部分布到 dn1。Id 在 20001-40000 的,%2 分布到 dn2,dn3

插入數(shù)據(jù):

INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (666, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (6667, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (16666, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (21111, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (22222, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (23333, database());
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (24444, database());

特點:擴容的時候舊數(shù)據(jù)無需遷移

其他分片規(guī)則

應(yīng)用指定分片 PartitionDirectBySubString
日期范圍哈希 PartitionByRangeDateHash
冷熱數(shù)據(jù)分片 PartitionByHotDate
也 可 以 自 定 義 分 片 規(guī) 則 : extends AbstractPartitionAlgorithm implements RuleAlgorithm

切分規(guī)則的選擇

步驟:

  • 找到需要切分的大表,和關(guān)聯(lián)的表
  • 確定分片字段(盡量使用主鍵),一般用最頻繁使用的查詢條件
  • 考慮單個分片的存儲容量和請求、數(shù)據(jù)增長(業(yè)務(wù)特性)、擴容和數(shù)據(jù)遷移 問題

例如:按照什么遞增?序號還是日期?主鍵是否有業(yè)務(wù)意義?
一般來說,分片數(shù)要比當(dāng)前規(guī)劃的節(jié)點數(shù)要大

總結(jié):根據(jù)業(yè)務(wù)場景,合理地選擇分片規(guī)則

舉例:
老師:3.7 億的數(shù)據(jù)怎么分表?我是不是分成 3 臺服務(wù)器?

  • 一年內(nèi)到達(dá)多少?兩年內(nèi)到達(dá)多少?(數(shù)據(jù)的增長速度)?
    答:一臺設(shè)備每秒鐘往 3 張表各寫入一條數(shù)據(jù),一共 4 臺設(shè)備。每張表一天 86400*4=345600 條。每張表一個月 10368000 條
    分析:增長速度均勻,可以用日期切分,每個月分一張表

  • 什么業(yè)務(wù)?所有的數(shù)據(jù)都會訪問,還是訪問新數(shù)據(jù)為主?
    答:訪問新數(shù)據(jù)為主,但是所有的數(shù)據(jù)都可能會訪問到

  • 表結(jié)構(gòu)和表數(shù)據(jù)是什么樣的?一個月消耗多少空間?
    答:字段不多,算過了,三年數(shù)據(jù)量有 3.7 億,30G
    分析:30G 沒必要分庫,浪費機器

  • 訪問量怎么樣?并發(fā)壓力大么?
    答:并發(fā)有一點吧
    分析:如果并發(fā)量不大,不用分庫,只需要在單庫分表。不用引入 Mycat 中間件 了。如果要自動路由的話可以用 Sharding-JDBC,否則就是自己拼裝表名

  • 3 張表有沒有關(guān)聯(lián)查詢之類的操作?
    答:沒有
    分析:還是拼裝表名簡單一點

Mycat 離線擴縮容

當(dāng)我們規(guī)劃了數(shù)據(jù)分片,而數(shù)據(jù)已經(jīng)超過了單個節(jié)點的存儲上線,或者需要下線節(jié) 點的時候,就需要對數(shù)據(jù)重新分片

Mycat 自帶的工具

準(zhǔn)備工作

  • mycat 所在環(huán)境安裝 mysql 客戶端程序
  • mycat 的 lib 目錄下添加 mysql 的 jdbc 驅(qū)動包
  • 對擴容縮容的表所有節(jié)點數(shù)據(jù)進(jìn)行備份,以便遷移失敗后的數(shù)據(jù)恢復(fù)

步驟

以取模分片表 sharding-by-mod 縮容為例

時間 數(shù)據(jù)
遷移前數(shù)據(jù) dn0 3,6 dn1 1,4 dn3 2,5
遷移后數(shù)據(jù) dn0 2,4,6 dn1 1,3,5
  • 復(fù)制 schema.xml、rule.xml 并重命名為 newSchema.xml、newRule.xml 放 于 conf 目錄下
  • 修改 newSchema.xml 和 newRule.xml 配置文件為擴容縮容后的 mycat 配置參數(shù)(表的節(jié)點數(shù)、數(shù)據(jù)源、路由規(guī)則)

注意: 只有節(jié)點變化的表才會進(jìn)行遷移。僅分片配置變化不會遷移

newSchema.xml

<table name="sharding_by_mod" dataNode="dn1,dn2,dn3" rule="qs-sharding-by-mod" />

改成(減少了一個節(jié)點):

<table name="sharding_by_mod" dataNode="dn1,dn2" rule="qs-sharding-by-mod" />

newRule.xml 修改 count 個數(shù)

<function name="qs-sharding-by-mod-long" class="io.mycat.route.function.PartitionByMod">
    <property name="count">2</property>
</function>
  • 修改 conf 目錄下的 migrateTables.properties 配置文件,告訴工具哪些表 需要進(jìn)行擴容或縮容,沒有出現(xiàn)在此配置文件的 schema 表不會進(jìn)行數(shù)據(jù)遷移,格 式:
    注意,不遷移的表,不要修改 dn 個數(shù),否則會報錯
  • ER 表,因為只有主表有分片規(guī)則,子表不會遷移
catmall=sharding-by-mod
  • dataMigrate.sh 中這個必須要配置
    通 過 命 令 "find / -name mysqldump" 查 找 mysqldump 路 徑 為 "/usr/bin/mysqldump",指定#mysql bin 路徑為"/usr/bin/"
#mysql bin 路徑
RUN_CMD="$RUN_CMD -mysqlBin= /usr/bin/"
  • 停止 mycat 服務(wù)
  • 執(zhí)行執(zhí)行 bin/ dataMigrate.sh 腳本
    注意:必須要配置 Java 環(huán)境變量,不能用 openjdk 7
  • 腳 本 執(zhí) 行 完 成 , 如 果 最 后 的 數(shù) 據(jù) 遷 移 驗 證 通 過 , 就 可 以 將 之 前 的 newSchema.xml 和 newRule.xml 替換之前的 schema.xml 和 rule.xml 文 件,并重啟 mycat 即可

注意事項:

  • 保證分片表遷移數(shù)據(jù)前后路由規(guī)則一致(取?!∧#?/li>
  • 保證分片表遷移數(shù)據(jù)前后分片字段一致
  • 全局表將被忽略
  • 不要將非分片表配置到 migrateTables.properties 文件中
  • 暫時只支持分片表使用 MySQL 作為數(shù)據(jù)源的擴容縮容

migrate 限制比較多,還可以使用 mysqldump

mysqldump 方式

系統(tǒng)第一次上線,把單張表遷移到 Mycat,也可以用 mysqldump
MySQL 導(dǎo)出

mysqldump -uroot -p123456 -h127.0.0.1 -P3306 -c -t --skip-extended-insert gpcat > mysql-1017.sql

-c 代表帶列名
-t 代表只要數(shù)據(jù),不要建表語句
--skip-extended-insert 代表生成多行 insert(mycat childtable 不支持多行插入 ChildTable multi insert not provided)

Mycat 導(dǎo)入

mysql -uroot -p123456 -h127.0.0.1 -P8066 catmall < mysql-1017.sql

Mycat 導(dǎo)出

mysqldump -h192.168.8.151 -uroot -p123456 -P8066 -c -t --skip-extended-insert catmall customer > mycat-cust.sql

其他導(dǎo)入方式:

load data local infile '/mycat/customer.txt' into table customer;
source sql '/mycat/customer.sql';

核心流程總結(jié)

官網(wǎng)的架構(gòu)圖:


啟動

  • MycatServer 啟動,解析配置文件,包括服務(wù)器、分片規(guī)則等
  • 創(chuàng)建工作線程,建立前端連接和后端連接

執(zhí)行 SQL

  • 前端連接接收 MySQL 命令
  • 解析 MySQL,Mycat 用的是 Druid 的 DruidParser
  • 獲取路由
  • 改寫 MySQL,例如兩個條件在兩個節(jié)點上,則變成兩條單獨的 SQL
    例如 select * from customer where id in(5000001, 10000001);
    改寫成:
    select * from customer where id = 5000001;(dn2 執(zhí)行)
    select * from customer where id = 10000001;(dn3 執(zhí)行)

又比如多表關(guān)聯(lián)查詢,先到各個分片上去獲取結(jié)果,然后在內(nèi)存中計算

  • 與后端數(shù)據(jù)庫建立連接
  • 發(fā)送 SQL 語句到 MySQL 執(zhí)行
  • 獲取返回結(jié)果
  • 處理返回結(jié)果,例如排序、計算等等
  • 返回給客戶端

源碼下載與調(diào)試環(huán)境搭建

下載源代碼,導(dǎo)入工程

git clone https://github.com/MyCATApache/Mycat-Server

配置

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">
        <table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
        <table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
        <table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
    </schema>
    <dataNode name="dn1" dataHost="localhost1" database="db1" />
    <dataNode name="dn2" dataHost="localhost1" database="db2" />
    <dataNode name="dn3" dataHost="localhost1" database="db3" />
    <dataHost name="localhost1" maxCon="20" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        <writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="123456"></writeHost>
    </dataHost>
</mycat:schema>

表結(jié)構(gòu)

本地數(shù)據(jù)庫創(chuàng)建 db1、db2、db3 數(shù)據(jù)庫,全部執(zhí)行建表腳本

CREATE TABLE `company` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `name` varchar(64) DEFAULT '',
    `market_value` bigint(20) DEFAULT '0',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

CREATE TABLE `hotnews` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `title` varchar(64) DEFAULT '',
    `content` varchar(512) DEFAULT '0',
    `time` varchar(8) DEFAULT '',
    `cat_name` varchar(10) DEFAULT '',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `travelrecord` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `city` varchar(32) DEFAULT '',
    `time` varchar(8) DEFAULT '',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

邏輯表配置

travelrecord 表配置

<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<tableRule name="auto-sharding-long">
    <rule>
        <columns>id</columns>
        <algorithm>rang-long</algorithm>
    </rule>
</tableRule>

<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
    <property name="mapFile">autopartition-long.txt</property>
</function>

hotnews 表配置

<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
<tableRule name="mod-long">
    <rule>
        <columns>id</columns>
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <!-- how many data nodes -->
    <property name="count">3</property>
</function>

company 表配置

<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />

debug 方式啟動

debug 方式啟動 main 方法
Mycat-Server-1.6.5-RELEASE\src\main\java\io\mycat\MycatStartup.java

連接本機 Mycat 服務(wù)

測試語句

insert into travelrecord(`id`, `city`, `time`) values(1, '長沙', '20191020');
insert into hotnews(`title`, `content`) values('咕泡', '盆魚宴');
insert into company(`name`, `market_value`) values('spring', 100);

調(diào)試入口

連接入口:
io.mycat.net.NIOAcceptor#accept
SQL 入口:
io.mycat.server.ServerQueryHandler#query

Step Over 可以看到上一層的調(diào)用

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容