特別說(shuō)明: 本人平時(shí)混跡于 B 站,不咋回復(fù)這里的評(píng)論,有問(wèn)題可以到 B 站視頻評(píng)論區(qū)留言找我
視頻地址: https://space.bilibili.com/31137138/favlist?fid=326428938
課件說(shuō)明: 本次提供的課件是 Spring Cloud Netflix 版微服務(wù)架構(gòu)指南,如果有興趣想要學(xué)習(xí) Spring Cloud Alibaba 版烹看,可以前往 http://www.qfdmy.com 查看相關(guān)課程資源
案例代碼: https://github.com/topsale/hello-spring-cloud-netflix
概述
Mycat 是什么抵屿?從定義和分類來(lái)看,它是一個(gè)開(kāi)源的分布式數(shù)據(jù)庫(kù)系統(tǒng)辫继,是一個(gè)實(shí)現(xiàn)了 MySQL 協(xié)議的 Server,前端用戶可以把它看作是一個(gè)數(shù)據(jù)庫(kù)代理俗慈,用 MySQL 客戶端工具和命令行訪問(wèn)姑宽,而其后端可以用 MySQL 原生(Native) 協(xié)議與多個(gè) MySQL 服務(wù)器通信,也可以用 JDBC 協(xié)議與大多數(shù)主流數(shù)據(jù)庫(kù)服務(wù)器通信闺阱,其核心功能是分表分庫(kù)低千,即將一個(gè)大表水平分割為 N 個(gè)小表,存儲(chǔ)在后端 MySQL 服務(wù)器里或者其他數(shù)據(jù)庫(kù)里
什么是分庫(kù)分表(數(shù)據(jù)切分)
簡(jiǎn)單來(lái)說(shuō)馏颂,就是指通過(guò)某種特定的條件示血,將我們存放在同一個(gè)數(shù)據(jù)庫(kù)中的數(shù)據(jù)分散存放到多個(gè)數(shù)據(jù)庫(kù)(主機(jī))上面,以達(dá)到分散單臺(tái)設(shè)備負(fù)載的效果
數(shù)據(jù)的切分(Sharding)根據(jù)其切分規(guī)則的類型救拉,可以分為兩種切分模式难审。一種是按照不同的表(或者 Schema)來(lái)切分到不同的數(shù)據(jù)庫(kù)(主機(jī))之上,這種切可以稱之為數(shù)據(jù)的垂直(縱向)切分亿絮;另外一種則是根據(jù)表中的數(shù)據(jù)的邏輯關(guān)系告喊,將同一個(gè)表中的數(shù)據(jù)按照某種條件拆分到多臺(tái)數(shù)據(jù)庫(kù)(主機(jī))上面,這種切分稱之為數(shù)據(jù)的水平(橫向)切分
垂直切分的最大特點(diǎn)就是規(guī)則簡(jiǎn)單派昧,實(shí)施也更為方便黔姜,尤其適合各業(yè)務(wù)之間的耦合度非常低,相互影響很小蒂萎,業(yè)務(wù)邏輯非常清晰的系統(tǒng)秆吵。在這種系統(tǒng)中,可以很容易做到將不同業(yè)務(wù)模塊所使用的表分拆到不同的數(shù)據(jù)庫(kù)中五慈。根據(jù)不同的表來(lái)進(jìn)行拆分纳寂,對(duì)應(yīng)用程序的影響也更小主穗,拆分規(guī)則也會(huì)比較簡(jiǎn)單清晰
水平切分于垂直切分相比,相對(duì)來(lái)說(shuō)稍微復(fù)雜一些毙芜。因?yàn)橐獙⑼粋€(gè)表中的不同數(shù)據(jù)拆分到不同的數(shù)據(jù)庫(kù)中忽媒,對(duì)于應(yīng)用程序來(lái)說(shuō),拆分規(guī)則本身就較根據(jù)表名來(lái)拆分更為復(fù)雜腋粥,后期的數(shù)據(jù)維護(hù)也會(huì)更為復(fù)雜一些
垂直切分
一個(gè)數(shù)據(jù)庫(kù)由很多表的構(gòu)成晦雨,每個(gè)表對(duì)應(yīng)著不同的業(yè)務(wù),垂直切分是指按照業(yè)務(wù)將表進(jìn)行分類隘冲,分布到不同的數(shù)據(jù)庫(kù)上面闹瞧,這樣也就將數(shù)據(jù)或者說(shuō)壓力分擔(dān)到不同的庫(kù)上面,如下圖:
系統(tǒng)被切分成了对嚼,用戶,訂單交易绳慎,支付幾個(gè)模塊纵竖。一個(gè)架構(gòu)設(shè)計(jì)較好的應(yīng)用系統(tǒng),其總體功能肯定是由很多個(gè)功能模塊所組成的杏愤,而每一個(gè)功能模塊所需要的數(shù)據(jù)對(duì)應(yīng)到數(shù)據(jù)庫(kù)中就是一個(gè)或者多個(gè)表靡砌。而在架構(gòu)設(shè)計(jì)中,各個(gè)功能模塊相互之間的交互點(diǎn)越統(tǒng)一越少珊楼,系統(tǒng)的耦合度就越低通殃,系統(tǒng)各個(gè)模塊的維護(hù)性以及擴(kuò)展性也就越好。這樣的系統(tǒng)厕宗,實(shí)現(xiàn)數(shù)據(jù)的垂直切分也就越容易
但是往往系統(tǒng)之有些表難以做到完全的獨(dú)立画舌,存在這擴(kuò)庫(kù) join 的情況,對(duì)于這類的表已慢,就需要去做平衡曲聂,是數(shù)據(jù)庫(kù)讓步業(yè)務(wù),共用一個(gè)數(shù)據(jù)源佑惠,還是分成多個(gè)庫(kù)朋腋,業(yè)務(wù)之間通過(guò)接口來(lái)做調(diào)用。在系統(tǒng)初期膜楷,數(shù)據(jù)量比較少旭咽,或者資源有限的情況下,會(huì)選擇共用數(shù)據(jù)源赌厅,但是當(dāng)數(shù)據(jù)發(fā)展到了一定的規(guī)模穷绵,負(fù)載很大的情況,就需要必須去做分割
一般來(lái)講業(yè)務(wù)存在著復(fù)雜 join
的場(chǎng)景是難以切分的特愿,往往業(yè)務(wù)獨(dú)立的易于切分请垛。 如何切分催训,切分到何種程度是考驗(yàn)技術(shù)架構(gòu)的一個(gè)難題
-
優(yōu)點(diǎn)
- 拆分后業(yè)務(wù)清晰,拆分規(guī)則明確
- 系統(tǒng)之間整合或擴(kuò)展容易
- 數(shù)據(jù)維護(hù)簡(jiǎn)單
-
缺點(diǎn)
- 部分業(yè)務(wù)表無(wú)法
join
宗收,只能通過(guò)接口方式解決漫拭,提高了系統(tǒng)復(fù)雜度 - 受每種業(yè)務(wù)不同的限制存在單庫(kù)性能瓶頸,不易數(shù)據(jù)擴(kuò)展跟性能提高
- 事務(wù)處理復(fù)雜
- 部分業(yè)務(wù)表無(wú)法
由于垂直切分是按照業(yè)務(wù)的分類將表分散到不同的庫(kù)混稽,所以有些業(yè)務(wù)表會(huì)過(guò)于龐大采驻,存在單庫(kù)讀寫(xiě)與存儲(chǔ)瓶頸,所以就需要水平拆分來(lái)做解決
水平切分
相對(duì)于垂直拆分匈勋,水平拆分不是將表做分類礼旅,而是按照某個(gè)字段的某種規(guī)則來(lái)分散到多個(gè)庫(kù)之中,每個(gè)表中包含一部分?jǐn)?shù)據(jù)洽洁。簡(jiǎn)單來(lái)說(shuō)痘系,我們可以將數(shù)據(jù)的水平切分理解為是按照數(shù)據(jù)行的切分,就是將表中的某些行切分到一個(gè)數(shù)據(jù)庫(kù)饿自,而另外的某些行又切分到其他的數(shù)據(jù)庫(kù)中汰翠,如圖:
拆分?jǐn)?shù)據(jù)就需要定義分片規(guī)則。關(guān)系型數(shù)據(jù)庫(kù)是行列的二維模型昭雌,拆分的第一原則是找到拆分維度复唤。 比如:從會(huì)員的角度來(lái)分析,商戶訂單交易類系統(tǒng)中查詢會(huì)員某天某月某個(gè)訂單烛卧,那么就需要按照會(huì)員結(jié)合日期來(lái)拆分佛纫,不同的數(shù)據(jù)按照會(huì)員 ID
做分組,這樣所有的數(shù)據(jù)查詢 join
都會(huì)在單庫(kù)內(nèi)解決总放;如果從商戶的角度來(lái)講呈宇,要查詢某個(gè)商家某天所有的訂單數(shù),就需要按照商戶 ID
做拆分局雄;但是如果系統(tǒng)既想按會(huì)員拆分攒盈,又想按商家數(shù)據(jù),則會(huì)有一定的困難哎榴。如何找到合適的分片規(guī)則需要綜合考慮衡量型豁。幾種典型的分片規(guī)則包括:
- 按照用戶
ID
求模,將數(shù)據(jù)分散到不同的數(shù)據(jù)庫(kù)尚蝌,具有相同數(shù)據(jù)用戶的數(shù)據(jù)都被分散到一個(gè)庫(kù)中 - 按照日期迎变,將不同月甚至日的數(shù)據(jù)分散到不同的庫(kù)中
- 按照某個(gè)特定的字段求摸,或者根據(jù)特定范圍段分散到不同的庫(kù)中
如圖飘言,切分原則都是根據(jù)業(yè)務(wù)找到適合的切分規(guī)則分散到不同的庫(kù)衣形,下面用用戶 ID
求模舉例
-
優(yōu)點(diǎn)
- 拆分規(guī)則抽象好,
join
操作基本可以數(shù)據(jù)庫(kù)做 - 不存在單庫(kù)大數(shù)據(jù),高并發(fā)的性能瓶頸
- 應(yīng)用端改造較少
- 提高了系統(tǒng)的穩(wěn)定性跟負(fù)載能力
- 拆分規(guī)則抽象好,
-
缺點(diǎn)
- 拆分規(guī)則難以抽象
- 分片事務(wù)一致性難以解決
- 數(shù)據(jù)多次擴(kuò)展難度跟維護(hù)量極大
- 跨庫(kù)
join
性能較差
-
垂直與水平拆分的共同缺點(diǎn)
- 引入分布式事務(wù)的問(wèn)題
- 跨節(jié)點(diǎn)
join
的問(wèn)題 - 跨節(jié)點(diǎn)合并排序分頁(yè)問(wèn)題
- 多數(shù)據(jù)源管理問(wèn)題
數(shù)據(jù)源管理方案
- 客戶端模式谆吴,在每個(gè)應(yīng)用程序模塊中配置管理自己需要的一個(gè)(或者多個(gè))數(shù)據(jù)源倒源,直接訪問(wèn)各個(gè)數(shù)據(jù)庫(kù),在模塊內(nèi)完成數(shù)據(jù)的整合
- 通過(guò)中間代理層來(lái)統(tǒng)一管理所有的數(shù)據(jù)源句狼,后端數(shù)據(jù)庫(kù)集群對(duì)前端應(yīng)用程序透明
絕大部分人在面對(duì)上面這兩種解決思路的時(shí)候都會(huì)傾向于選擇第二種笋熬,尤其是系統(tǒng)不斷變得龐大復(fù)雜的時(shí)候。確實(shí)腻菇,這是一個(gè)非常正確的選擇胳螟,雖然短期內(nèi)需要付出的成本可能會(huì)相對(duì)更大一些,但是對(duì)整個(gè)系統(tǒng)的擴(kuò)展性來(lái)說(shuō)筹吐,是非常有幫助的
數(shù)據(jù)庫(kù)中間件 MyCat
MyCat 是一個(gè)強(qiáng)大的數(shù)據(jù)庫(kù)中間件糖耸,不僅僅可以用作讀寫(xiě)分離、以及分表分庫(kù)丘薛、容災(zāi)備份嘉竟,而且可以用于多租戶應(yīng)用開(kāi)發(fā)、云平臺(tái)基礎(chǔ)設(shè)施洋侨、讓你的架構(gòu)具備很強(qiáng)的適應(yīng)性和靈活性舍扰,借助于即將發(fā)布的 MyCat 智能優(yōu)化模塊,系統(tǒng)的數(shù)據(jù)訪問(wèn)瓶頸和熱點(diǎn)一目了然凰兑,根據(jù)這些統(tǒng)計(jì)分析數(shù)據(jù)妥粟,你可以自動(dòng)或手工調(diào)整后端存儲(chǔ)审丘,將不同的表映射到不同存儲(chǔ)引擎上吏够,而整個(gè)應(yīng)用的代碼一行也不用改變
應(yīng)用場(chǎng)景
- 單純的讀寫(xiě)分離,此時(shí)配置最為簡(jiǎn)單滩报,支持讀寫(xiě)分離锅知,主從切換
- 分表分庫(kù),對(duì)于超過(guò) 1000 萬(wàn)的表進(jìn)行分片脓钾,最大支持 1000 億的單表分片
- 多租戶應(yīng)用售睹,每個(gè)應(yīng)用一個(gè)庫(kù),但應(yīng)用程序只連接 MyCat可训,從而不改造程序本身昌妹,實(shí)現(xiàn)多租戶化
- 報(bào)表系統(tǒng),借助于 MyCat 的分表能力握截,處理大規(guī)模報(bào)表的統(tǒng)計(jì)
- 替代 Hbase飞崖,分析大數(shù)據(jù)
- 作為海量數(shù)據(jù)實(shí)時(shí)查詢的一種簡(jiǎn)單有效方案,比如 100 億條頻繁查詢的記錄需要在 3 秒內(nèi)查詢出來(lái)結(jié)果谨胞,除了基于主鍵的查詢固歪,還可能存在范圍查詢或其他屬性查詢,此時(shí) MyCat 可能是最簡(jiǎn)單有效的選擇
環(huán)境準(zhǔn)備
部署方式全部基于 Docker
部署 3 臺(tái) MySQL 容器
案例分片方案按照自定義數(shù)字范圍分片方式(auto-sharding-long
)進(jìn)行數(shù)據(jù)庫(kù)分片胯努,其規(guī)則要求需要 3 臺(tái) MySQL牢裳,docker-compose.yml
配置如下
- mysql-1
version: '3.1'
services:
mysql-1:
image: mysql
container_name: mysql-1
environment:
MYSQL_ROOT_PASSWORD: 123456
command:
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
ports:
- 3306:3306
volumes:
- ./data:/var/lib/mysql
- mysql-2
version: '3.1'
services:
mysql-2:
image: mysql
container_name: mysql-2
environment:
MYSQL_ROOT_PASSWORD: 123456
command:
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
ports:
- 3307:3306
volumes:
- ./data:/var/lib/mysql
- mysql-3
version: '3.1'
services:
mysql-3:
image: mysql
container_name: mysql-3
environment:
MYSQL_ROOT_PASSWORD: 123456
command:
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
ports:
- 3308:3306
volumes:
- ./data:/var/lib/mysql
部署 MyCat 數(shù)據(jù)庫(kù)中間件
- 克隆
git clone https://github.com/dekuan/docker.mycat.git
- 構(gòu)建
cd docker.mycat
docker-compose build
- 啟動(dòng)
# 注意:配置完成后再啟動(dòng)
docker-compose up -d
配置 MyCat 數(shù)據(jù)庫(kù)分片
- 服務(wù)端用戶名密碼配置:
vi config/mycat/server.xml
逢防,找到第 90 行历造,參考如下內(nèi)容配置
<mycat:server xmlns:mycat="http://io.mycat/">
<!-- Mycat 數(shù)據(jù)庫(kù)用戶名 -->
<user name="root">
<!-- Mycat 數(shù)據(jù)庫(kù)密碼 -->
<property name="password">123456</property>
<!-- Mycat 數(shù)據(jù)庫(kù)名 -->
<property name="schemas">myshop</property>
<!-- 是否使用加密的密碼拴测,0 表示不使用加密的密碼 -->
<property name="usingDecrypt">0</property>
</user>
</mycat:server>
- 數(shù)據(jù)節(jié)點(diǎn)、數(shù)據(jù)庫(kù)泉坐、分庫(kù)分表配置:
vi config/mycat/schema.xml
伶椿,參考如下內(nèi)容配置
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="myshop" checkSQLschema="true" sqlMaxLimit="100">
<!-- 需要分片的表
在節(jié)點(diǎn) dataNode1, dataNode2, dataNode3 上分片
分片規(guī)則是 auto-sharding-long 即連續(xù)分片規(guī)則之自定義數(shù)字范圍分片
對(duì)應(yīng)的分片規(guī)則配置文件在 config/mycat/rule.xml
-->
<table
name="tb_admin"
primaryKey="id"
dataNode="dataNode1,dataNode2,dataNode3"
rule="auto-sharding-long"/>
</schema>
<!-- 數(shù)據(jù)節(jié)點(diǎn) dataNode1辜伟,對(duì)應(yīng)的主機(jī) dataHost1, 對(duì)應(yīng)是數(shù)據(jù)庫(kù) myshop_1 -->
<dataNode name="dataNode1" dataHost="dataHost1" database="myshop_1" />
<dataNode name="dataNode2" dataHost="dataHost2" database="myshop_2" />
<dataNode name="dataNode3" dataHost="dataHost3" database="myshop_3" />
<!-- 主機(jī) dataHost1 -->
<dataHost name="dataHost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- MySQL 數(shù)據(jù)庫(kù)的連接串 -->
<writeHost
host="192.168.141.206"
url="jdbc:mysql://192.168.141.206:3306?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="root" password="123456">
<!--<readHost host="192.168.1.104" url="192.168.1.104:3306" user="druid" password="druid" />-->
</writeHost>
</dataHost>
<dataHost name="dataHost2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.141.206"
url="jdbc:mysql://192.168.141.206:3307?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="root" password="123456">
<!--<readHost host="192.168.1.104" url="192.168.1.104:3306" user="druid" password="druid" />-->
</writeHost>
</dataHost>
<dataHost name="dataHost3" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="-1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost
host="192.168.141.206"
url="jdbc:mysql://192.168.141.206:3308?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"
user="root" password="123456">
<!--<readHost host="192.168.1.104" url="192.168.1.104:3306" user="druid" password="druid" />-->
</writeHost>
</dataHost>
</mycat:schema>
- 分片規(guī)則配置:
vi config/mycat/rule.xml
,分別查看第 32 行和第 105 行
<!-- 第 32 行 -->
<tableRule name="auto-sharding-long">
<rule>
<!-- 指定分片表列名 -->
<columns>id</columns>
<!-- 指定分片函數(shù)與 function 對(duì)應(yīng) -->
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
<!-- 第 105 行 -->
<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
<!-- 函數(shù)中的 mapFile 代表規(guī)則配置文件的路徑脊另,config/mycat/autopartition-long.txt -->
<property name="mapFile">autopartition-long.txt</property>
</function>
- 自定義數(shù)字范圍分片規(guī)則:
vi config/mycat/autopartition-long.txt
# range start-end ,data node index
# K=1000,M=10000.
# ID 0-5000000 保存在 dataNode1
0-500M=0
# ID 5000000-10000000 保存在 dataNode2
500M-1000M=1
# ID 10000000-15000000 保存在 dataNode3
1000M-1500M=2
測(cè)試 MyCat 數(shù)據(jù)分片
分別創(chuàng)建數(shù)據(jù)庫(kù)
分別在 3 臺(tái) MySQL 數(shù)據(jù)庫(kù)上創(chuàng)建 myshop_1
导狡、myshop_2
、myshop_3
偎痛,按照剛才創(chuàng)建 MySQL 容器的序號(hào)創(chuàng)建
通過(guò) MyCat 操作數(shù)據(jù)庫(kù)
使用 SQLyog 之類的客戶端工具連接 MyCat 數(shù)據(jù)庫(kù)旱捧,默認(rèn)端口號(hào)為 8066
創(chuàng)建測(cè)試數(shù)據(jù)
- 建表語(yǔ)句
create table tb_admin (id int not null primary key,name varchar(100),sharding_id int not null);
- 測(cè)試數(shù)據(jù)
insert into tb_admin(id, name,sharding_id) values(1000000, 'lixiaohong', 0);
insert into tb_admin(id, name,sharding_id) values(6000000, 'lixiaolu', 1);
insert into tb_admin(id, name,sharding_id) values(7000000, 'pgone', 1);
insert into tb_admin(id, name,sharding_id) values(11000000, 'jianailiang', 2);
檢驗(yàn)測(cè)試結(jié)果
按照上面的配置規(guī)則
-
id
為1000000
的數(shù)據(jù)應(yīng)該寫(xiě)入dataNode1
即myshop_1
.tb_admin
表中 -
id
為6000000
的數(shù)據(jù)應(yīng)該寫(xiě)入dataNode2
即myshop_2
.tb_admin
表中 -
id
為7000000
的數(shù)據(jù)應(yīng)該寫(xiě)入dataNode2
即myshop_2
.tb_admin
表中 -
id
為11000000
的數(shù)據(jù)應(yīng)該寫(xiě)入dataNode3
即myshop_3
.tb_admin
表中
至此這說(shuō)明分片成功了
特別說(shuō)明: 本人平時(shí)混跡于 B 站,不咋回復(fù)這里的評(píng)論踩麦,有問(wèn)題可以到 B 站視頻評(píng)論區(qū)留言找我
視頻地址: https://space.bilibili.com/31137138/favlist?fid=326428938
課件說(shuō)明: 本次提供的課件是 Spring Cloud Netflix 版微服務(wù)架構(gòu)指南枚赡,如果有興趣想要學(xué)習(xí) Spring Cloud Alibaba 版,可以前往 http://www.qfdmy.com 查看相關(guān)課程資源
案例代碼: https://github.com/topsale/hello-spring-cloud-netflix