摘要:本文圍繞分庫分表展開,先分析單庫性能瓶頸,介紹垂直與水平拆分策略及實現技術,再詳述 MyCat 中間件的概述、環境準備、目錄結構,講解其入門配置與測試,深入說明核心配置文件,最后演示垂直和水平拆分實操,為數據庫性能優化提供方案。
思維導圖
1. 介紹
1.1 問題分析
隨著互聯網及移動互聯網的發展,應用系統的數據量呈指數式增長,若采用單數據庫進行數據存儲,存在以下性能瓶頸:
-
IO 瓶頸:熱點數據過多,數據庫緩存不足,產生大量磁盤 IO,效率較低;請求數據太多,帶寬不夠,出現網絡 IO 瓶頸。
-
CPU 瓶頸:排序、分組、連接查詢、聚合統計等 SQL 會耗費大量的 CPU 資源,請求數太多時,CPU 出現瓶頸。
為解決上述問題,需對數據庫進行分庫分表處理。
分庫分表的中心思想是將數據分散存儲,減小單一數據庫 / 表的數據量,緩解單一數據庫的性能問題,從而提升數據庫性能。
1.2 拆分策略
分庫分表主要有垂直拆分和水平拆分兩種形式,拆分粒度又分為分庫和分表,最終組成的拆分策略如下:
1.3 垂直拆分
1. 垂直分庫
-
定義:以表為依據,根據業務將不同表拆分到不同庫中。
-
特點:
* ? 每個庫的表結構都不一樣。
?
* ? 每個庫的數據也不一樣。
?
* ? 所有庫的并集是全量數據。
2. 垂直分表
-
定義:以字段為依據,根據字段屬性將不同字段拆分到不同表中。
-
特點:
* ? 每個表的結構都不一樣。
?
* ? 每個表的數據也不一樣,一般通過一列(主鍵 / 外鍵)關聯。
?
* ? 所有表的并集是全量數據。
1.4 水平拆分
1. 水平分庫
-
定義:以字段為依據,按照一定策略,將一個庫的數據拆分到多個庫中。
-
特點:
* ? 每個庫的表結構都一樣。
?
* ? 每個庫的數據都不一樣。
?
* ? 所有庫的并集是全量數據。
2. 水平分表
-
定義:以字段為依據,按照一定策略,將一個表的數據拆分到多個表中。
-
特點:
* ? 每個表的表結構都一樣。
?
* ? 每個表的數據都不一樣。
?
* ? 所有表的并集是全量數據。
在業務系統中,為緩解磁盤 IO 及 CPU 的性能瓶頸,需根據具體業務需求分析選擇垂直拆分或水平拆分,以及具體是分庫還是分表。
1.5 實現技術
技術 | 原理 | 支持語言 | 性能 |
---|---|---|---|
ShardingJDBC | 基于 AOP 原理,在應用程序中對本地執行的 SQL 進行攔截、解析、改寫、路由處理,需自行編碼配置實現 | 僅 Java | 較高 |
MyCat | 數據庫分庫分表中間件,不用調整代碼即可實現分庫分表 | 多種語言 | 不及 ShardingJDBC |
本次課程選擇 MyCat 數據庫中間件完成分庫分表操作。
2. MyCat 概述
2.1 介紹
MyCat 是開源、活躍、基于 Java 語言編寫的 MySQL 數據庫中間件。開發人員可像使用 MySQL 一樣使用 MyCat,無需關心底層數據庫數量及數據存儲情況,分庫分表策略僅需在 MyCat 中配置即可。
優勢:
-
性能可靠穩定
-
強大的技術團隊
-
體系完善
-
社區活躍
2.2 下載
-
下載地址:http://dl.mycat.org.cn/
-
可下載版本包括 Mycat1.6 數據庫中間件、Mycat-2.0 - 源碼、Mycat-server-1.6.7.6 版本等,同時提供 GitHub 代碼倉庫、相關文檔及問題搜索等資源。
2.3 安裝
MyCat 支持 Windows 和 Linux 運行環境,以下介紹 Linux 環境搭建,需在準備好的服務器中安裝 MySQL、JDK、MyCat。
服務器 | 安裝軟件 | 說明 |
---|---|---|
192.168.200.210 | JDK、MyCat、MySQL | MyCat 中間件服務器、分片服務器 |
192.168.200.213 | MySQL | 分片服務器 |
192.168.200.214 | MySQL | 分片服務器 |
具體安裝步驟省略。
2.4 目錄介紹
目錄 | 說明 |
---|---|
bin | 存放可執行文件,用于啟動、停止 MyCat |
conf | 存放 MyCat 的配置文件 |
lib | 存放 MyCat 的項目依賴包(jar) |
logs | 存放 MyCat 的日志文件 |
2.5 概念介紹
在MyCat的整體結構中,分為兩個部分:上面的邏輯結構、下面的物理結構。
在MyCat的邏輯結構主要負責邏輯庫、邏輯表、分片規則、分片節點等邏輯結構的處理,而具體的數據 存儲還是在物理結構,也就是數據庫服務器中存儲的。 在后面講解MyCat入門以及MyCat分片時,還會講到上面所提到的概念。
3. MyCat 入門
3.1 需求
由于 tb_order
表數據量很大,磁盤 IO 及容量達瓶頸,需對 tb_order
表進行數據分片,分為三個數據節點,每個節點主機位于不同服務器。
3.2 環境準備
準備 3 臺服務器:
-
192.168.200.210:MyCat 中間件服務器,同時作為第一個分片服務器。
-
192.168.200.213:第二個分片服務器。
-
192.168.200.214:第三個分片服務器。
在上述 3 臺數據庫中創建數據庫 db01
。
3.3 配置
1. schema.xml
在 schema.xml
中配置邏輯庫、邏輯表、數據節點、節點主機、數據庫等相關信息,具體配置如下:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/"><schema name="DB01" checkSQLschema="true" sqlMaxLimit="100"><table name="TB_ORDER" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"
/></schema><dataNode name="dn1" dataHost="dhost1" database="db01" /><dataNode name="dn2" dataHost="dhost2" database="db01" /><dataNode name="dn3" dataHost="dhost3" database="db01" /><dataHost name="dhost1" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><writeHost host="master" url="jdbc:mysql://192.168.200.210:3306?
useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"
user="root" password="1234" /></dataHost><dataHost name="dhost2" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><writeHost host="master" url="jdbc:mysql://192.168.200.213:3306?
useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"
user="root" password="1234" />
</dataHost><dataHost name="dhost3" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><writeHost host="master" url="jdbc:mysql://192.168.200.214:3306?
useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"
user="root" password="1234" /></dataHost>
</mycat:schema>
2. server.xml
在 server.xml
中配置用戶名、密碼及用戶訪問權限信息,具體配置如下:
<user name="root" defaultAccount="true"><property name="password">123456</property><property name="schemas">DB01</property><!-- 表級 DML 權限設置 --><!-- <privileges check="true"><schema name="DB01" dml="0110" > <table name="TB_ORDER" dml="1110"></table></schema></privileges> -->
</user>
<user name="user"><property name="password">123456</property><property name="schemas">DB01</property><property name="readOnly">true</property>
</user>
上述配置定義了兩個用戶 root
和 user
,均能訪問 DB01
邏輯庫,密碼均為 123456
。root
用戶對 DB01
邏輯庫可讀可寫,user
用戶對 DB01
邏輯庫只讀。
3.4 測試
3.4.1 啟動
配置完后,先啟動 3 臺分片服務器,再啟動 MyCat 服務器。切換到 MyCat 安裝目錄,如下指令:
//啟動
bin/mycat start//停止
bin/mycat stop
MyCat 啟動后占用端口號 8066,可查看 logs
目錄下的啟動日志(如 wrapper.log
),確認 MyCat 是否啟動成功,若日志中出現 “MyCAT Server startup successfully” 則啟動成功。
3.4.2 測試
1. 連接 MyCat
通過如下指令連接并登錄 MyCat:
mysql -h 192.168.200.210 -P 8066 -uroot -p123456
MyCat 底層模擬了 MySQL 協議,因此可通過 MySQL 指令連接。
2. 數據測試
在 MyCat 中創建表并插入數據,查看數據在 MySQL 中的分布情況:
CREATE TABLE TB_ORDER (id BIGINT(20) NOT NULL,
title VARCHAR(100) NOT NULL ,PRIMARY KEY (id)
) ENGINE=INNODB DEFAULT CHARSET=utf8 ;
INSERT INTO TB_ORDER(id,title) VALUES(1,'goods1');
INSERT INTO TB_ORDER(id,title) VALUES(2,'goods2');
INSERT INTO TB_ORDER(id,title) VALUES(3,'goods3');INSERT INTO TB_ORDER(id,title) VALUES(1,'goods1');
INSERT INTO TB_ORDER(id,title) VALUES(2,'goods2');
INSERT INTO TB_ORDER(id,title) VALUES(3,'goods3');
INSERT INTO TB_ORDER(id,title) VALUES(5000000,'goods5000000');
INSERT INTO TB_ORDER(id,title) VALUES(10000000,'goods10000000');
INSERT INTO TB_ORDER(id,title) VALUES(10000001,'goods10000001');
INSERT INTO TB_ORDER(id,title) VALUES(15000000,'goods15000000');
INSERT INTO TB_ORDER(id,title) VALUES(15000001,'goods15000001');
測試結果:
-
id 在 1-500w 之間,數據存儲在第一個分片數據庫。
-
id 在 500w-1000w 之間,數據存儲在第二個分片數據庫。
-
id 在 1000w-1500w 之間,數據存儲在第三個分片數據庫。
-
id 超出 1500w,插入數據時報錯。
數據存儲的分片服務器由邏輯表配置的 rule
參數(分片規則)決定,后續會講解分片規則配置。
`name`:指定自定義的邏輯庫庫名。`checkSQLschema`:SQL 語句操作指定數據庫名稱時,是否自動去除;`true` 自動去除,`false` 不自動去除。`sqlMaxLimit`:未指定 `limit` 查詢時,列表查詢模式返回的記錄數。
4. MyCat 配置
4.1 schema.xml
schema.xml
是 MyCat 最重要的配置文件之一,涵蓋 MyCat 的邏輯庫、邏輯表、分片規則、分片節點及數據源配置,主要包含 schema
、dataNode
、dataHost
三組標簽。
4.1.1 schema 標簽
1. 定義邏輯庫
核心屬性:
`name`:指定自定義的邏輯庫庫名。`checkSQLschema`:SQL 語句操作指定數據庫名稱時,是否自動去除;`true` 自動去除,`false` 不自動去除。`sqlMaxLimit`:未指定 `limit` 查詢時,列表查詢模式返回的記錄數。
MyCat 中邏輯庫概念等同于 MySQL 中的 database
,操作某邏輯庫下的表需切換邏輯庫,一個 MyCat 實例可通過多個 schema
標簽劃分不同邏輯庫。
2. 定義邏輯表
核心屬性:
`name`:定義邏輯表表名,在該邏輯庫下唯一。`dataNode`:定義邏輯表所屬的 `dataNode`,需與 `dataNode` 標簽中 `name` 對應;多個 `dataNode` 用逗號分隔。`rule`:分片規則的名字,在 `rule.xml` 中定義。`primaryKey`:邏輯表對應真實表的主鍵。`type`:邏輯表類型,目前有全局表和普通表,未配置則為普通表;全局表配置為 `global`。
所有需要拆分的表都需在 table
標簽中定義。
4.1.2 dataNode 標簽
核心屬性:
`name`:定義數據節點名稱。`dataHost`:數據庫實例主機名稱,引用自 `dataHost` 標簽中 `name` 屬性。`database`:定義分片所屬數據庫。
4.1.3 dataHost 標簽
該標簽是 MyCat 邏輯庫的底層標簽,直接定義具體的數據庫實例、讀寫分離、心跳語句。
核心屬性:
`name`:唯一標識,供上層標簽使用。`maxCon/minCon`:最大連接數 / 最小連接數。`balance`:負載均衡策略,取值 0、1、2、3。`writeType`:寫操作分發方式(0:寫操作轉發到第一個 `writeHost`,第一個掛了切換到第二個;1:寫操作隨機分發到配置的 `writeHost`)。`dbDriver`:數據庫驅動,支持 `native`、`jdbc`。
4.2 rule.xml
rule.xml
中定義所有拆分表的規則,可靈活使用分片算法或對同一分片算法使用不同參數,實現分片過程可配置化,主要包含 tableRule
、Function
兩類標簽:
4.3 server.xml
server.xml
配置文件包含 MyCat 的系統配置信息,主要有 system
、user
兩個重要標簽。
4.3.1 system 標簽
主要配置MyCat中的系統配置信息,對應的系統配置項及其含義,如下:
屬性 | 取值 | 含義 |
---|---|---|
charset | utf8 | 設置 Mycat 的字符集,字符集需要與 MySQL 的字符集保持一致 |
nonePasswordLogin | 0,1 | 0 為需要密碼登陸、1 為不需要密碼登陸,默認 為 0,設置為 1 則需要指定默認賬戶 |
useHandshakeV10 | 0,1 | 使用該選項主要的目的是為了能夠兼容高版本 的 jdbc 驅動,是否采用 HandshakeV10Packet 來與 client 進行通信,1: 是,0: 否 |
useSqlStat | 0,1 | 開啟 SQL 實時統計,1 為開啟,0 為關閉;開啟之后,MyCat 會自動統計 SQL 語句的執行情況;可通過?mysql -h 127.0.0.1 -P 9066 -u root -p ?查看 MyCat 執行的 SQL,包括執行效率較低的 SQL、SQL 的整體執行情況、讀寫比例等;支持指令:show @@sql 、show @@sql.slow 、show @@sql.sum |
useGlobleTableCheck | 0,1 | 是否開啟全局表的一致性檢測,1 為開啟,0 為關閉 |
sqlExecuteTimeout | 1000 | SQL 語句執行的超時時間,單位為 s |
sequnceHandlerType | 0,1,2 | 用來指定 Mycat 全局序列類型,0 為本地文件,1 為數據庫方式,2 為時間戳列方式,默認使用本地文件方式,文件方式主要用于測試 |
sequnceHandlerPattern | 正則表達式 | 必須帶有 MYCATSEQ 或者 mycatseq 進入序列匹配流程,注意 MYCATSEQ_ 有空格的情況 |
subqueryRelationshipCheck | true,false | 子查詢中存在關聯查詢的情況下,檢查關聯字段中是否有分片字段,默認 false |
useCompression | 0,1 | 開啟 mysql 壓縮協議,0:關閉,1:開啟 |
fakeMySQLVersion | 5.5,5.6 | 設置模擬的 MySQL 版本號 |
4.3.2 user 標簽
配置 MyCat 中的用戶、訪問密碼,以及用戶針對于邏輯庫、邏輯表的權限信息,具體配置示例及說明如下:
在測試權限操作時,我們只需要將 privileges 標簽的注釋放開。 在 privileges 下的schema 標簽中配置的dml屬性配置的是邏輯庫的權限。 在privileges的schema下的table標簽的dml屬性 中配置邏輯表的權限。
5 MyCat 分片
5.1 垂直拆分
5.1.1 場景
現在考慮將其進行垂直分庫操作,將商品相關的表拆分到一個數據庫服務器,訂單表拆分的一個數據庫 服務器,用戶及省市區表拆分到一個服務器。最終結構如下:
5.1.2 準備
準備三臺服務器,IP 地址及角色如下:
并且在192.168.200.210,192.168.200.213, 192.168.200.214上面創建數據庫 shopping。
5.1.3 配置
1. schema.xml
配置邏輯庫、邏輯表與數據節點的關聯,具體如下:
<schema name="SHOPPING" checkSQLschema="true" sqlMaxLimit="100"><table name="tb_goods_base" dataNode="dn1" primaryKey="id" /><table name="tb_goods_brand" dataNode="dn1" primaryKey="id" /><table name="tb_goods_cat" dataNode="dn1" primaryKey="id" /><table name="tb_goods_desc" dataNode="dn1" primaryKey="goods_id" /><table name="tb_goods_item" dataNode="dn1" primaryKey="id" /><table name="tb_order_item" dataNode="dn2" primaryKey="id" /><table name="tb_order_master" dataNode="dn2" primaryKey="order_id" /><table name="tb_order_pay_log" dataNode="dn2" primaryKey="out_trade_no" /><table name="tb_user" dataNode="dn3" primaryKey="id" /><table name="tb_user_address" dataNode="dn3" primaryKey="id" /><table name="tb_areas_provinces" dataNode="dn3" primaryKey="id"/>
<table name="tb_areas_city" dataNode="dn3" primaryKey="id"/><table name="tb_areas_region" dataNode="dn3" primaryKey="id"/>
</schema><dataNode name="dn1" dataHost="dhost1" database="shopping" />
<dataNode name="dn2" dataHost="dhost2" database="shopping" />
<dataNode name="dn3" dataHost="dhost3" database="shopping" /><dataHost name="dhost1" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><writeHost host="master" url="jdbc:mysql://192.168.200.210:3306?
useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"
user="root" password="1234" />
</dataHost><dataHost name="dhost2" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><writeHost host="master" url="jdbc:mysql://192.168.200.213:3306?
useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"
user="root" password="1234" /></dataHost>
<dataHost name="dhost3" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><heartbeat>select user()</heartbeat><writeHost host="master" url="jdbc:mysql://192.168.200.214:3306?
useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"
user="root" password="1234" />
</dataHost>
2. server.xml
配置訪問 SHOPPING
邏輯庫的用戶及權限,具體如下:
<user name="root" defaultAccount="true"><property name="password">123456</property><property name="schemas">SHOPPING</property></user>
<user name="user"><property name="password">123456</property><property name="schemas">SHOPPING</property><property name="readOnly">true</property>
</user>
5.1.4 測試
上傳腳本與導入數據:
1.將測試 SQL 腳本(shopping-table.sql
表結構腳本、shopping-insert.sql
數據腳本)上傳到服務器 /root
目錄。
2.重啟 MyCat 后,登錄 MyCat 命令行,通過 source
指令導入腳本:
source /root/shopping-table.sql;
?
source /root/shopping-insert.sql;
驗證跨表查詢(問題發現):
執行多表聯查 SQL(如查詢訂單及對應省市區地址):
SELECT order_id , payment ,receiver, province , city , area FROM tb_order_master o
, tb_areas_provinces p , tb_areas_city c , tb_areas_region r WHERE
o.receiver_province = p.provinceid AND o.receiver_city = c.cityid AND
o.receiver_region = r.areaid ;
問題:執行報錯。原因是訂單表(tb_order_master
)存儲在 192.168.200.213,省市區表存儲在 192.168.200.214,沒有任何一個數據庫服務器同時包含這兩類表,導致 MyCat 無法路由 SQL。
5.1.5 全局表
1. 全局表介紹
省市區表(tb_areas_provinces
、tb_areas_city
、tb_areas_region
)屬于數據字典表,在多個業務模塊中被引用,可將其設置為全局表。全局表會在所有關聯的數據節點中創建副本,確保每個分片服務器都包含該表,從而支持跨分片聯查。
解決跨分片聯查問題
2. 全局表配置
在邏輯表配置中增加 type="global"
屬性,指定全局表類型,具體如下:(修改 schema.xml)
<table name="tb_areas_provinces" dataNode="dn1,dn2,dn3" primaryKey="id"
type="global"/>
<table name="tb_areas_city" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
<table name="tb_areas_region" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
5.2 水平拆分
5.2.1 場景
業務系統中的日志表(tb_log
)每天產生大量數據,單臺服務器存儲及處理能力有限,需對 tb_log
表進行水平拆分,將數據分散到多臺分片服務器,減輕單庫壓力。
5.2.2 準備
準備三臺服務器,IP 地址及角色如下:
并且,在三臺數據庫服務器中分表創建一個數據庫itcast。
5.2.3 配置
1. schema.xml
配置邏輯表 tb_log
與多個數據節點的關聯,并指定水平分片規則(rule="mod-long"
),具體如下:
<schema name="ITCAST" checkSQLschema="true" sqlMaxLimit="100"><table name="tb_log" dataNode="dn4,dn5,dn6" primaryKey="id" rule="mod-long" />
</schema>
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />
?
2. server.xml
配置用戶可同時訪問 SHOPPING
和 ITCAST
邏輯庫,具體如下:
<user name="root" defaultAccount="true"><property name="password">123456</property><property name="schemas">SHOPPING,ITCAST</property>
</user>
5.2.4 測試
創建表與插入數據:
重啟 MyCat 后,登錄 MyCat 命令行,執行 SQL 創建 tb_log
表并插入測試數據,查看分片情況
大功告成!