一、為什么要分庫分表?
單機 MySQL 的極限大致在:
維度 | 經驗值 |
---|---|
單表行數 | ≤ 1 000 萬行(B+ 樹三層) |
單庫磁盤 | ≤ 2 TB(SSD) |
單機 QPS | ≤ 1 萬(InnoDB) |
當業務繼續增長,數據量和并發量都會突破單機天花板,此時就需要 水平拆分。
業內常見方案:
- Client 模式:ShardingSphere-JDBC、TDDL
- Proxy 模式:MyCat、ShardingSphere-Proxy、Vitess
今天的主角是 MyCat —— 輕量級、配置簡單、社區成熟,適合中小團隊快速落地。
二、MyCat 是什么?
一句話:
MyCat 是 MySQL 協議的代理中間件,對外表現為“一臺”大 MySQL,內部幫你把 SQL 路由到真正的分片。
核心概念:
名詞 | 作用 |
---|---|
schema | 邏輯庫(業務代碼看到的) |
table | 邏輯表(可配置分片規則) |
dataNode | 分片節點(邏輯庫+物理庫名) |
dataHost | 物理實例(主從/集群) |
rule | 分片算法(取模、范圍、哈希等) |
三、實戰目標
- 3 臺 MySQL 物理機
- 訂單表
t_order
按 user_id 取模 分成 6 張分表 - 商品表
t_product
數據量少 → 全局廣播表 - 配置表
t_config
全局廣播 - Java 代碼零侵入,只連 MyCat 8066 端口
四、環境準備
角色 | IP:Port | 備注 |
---|---|---|
MyCat 節點 | 192.168.1.10:8066 / 9066 | 代理端口/管理端口 |
MySQL-1 | 192.168.1.100:3306 | 主庫 |
MySQL-2 | 192.168.1.101:3306 | 主庫 |
MySQL-3 | 192.168.1.102:3306 | 主庫 |
4.1 安裝 MyCat
wget http://dl.mycat.org.cn/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
tar -zxvf Mycat-server-1.6-RELEASE-*.tar.gz
cd mycat
目錄結構:
mycat├─ bin/mycat # 啟停腳本├─ conf/*.xml # 配置文件└─ logs # 日志
五、MySQL 端建庫建表
每臺機執行:
CREATE DATABASE IF NOT EXISTS db1 DEFAULT CHARSET utf8mb4;
CREATE DATABASE IF NOT EXISTS db2 DEFAULT CHARSET utf8mb4;
CREATE DATABASE IF NOT EXISTS db3 DEFAULT CHARSET utf8mb4;-- 訂單分表
CREATE TABLE db1.t_order_0 (id BIGINT AUTO_INCREMENT PRIMARY KEY,user_id BIGINT NOT NULL,amount DECIMAL(10,2) NOT NULL,create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE db1.t_order_1 LIKE db1.t_order_0;CREATE TABLE db2.t_order_2 LIKE db1.t_order_0;
CREATE TABLE db2.t_order_3 LIKE db1.t_order_0;CREATE TABLE db3.t_order_4 LIKE db1.t_order_0;
CREATE TABLE db3.t_order_5 LIKE db1.t_order_0;-- 廣播表(每臺庫一份)
CREATE TABLE db1.t_product (id INT PRIMARY KEY,name VARCHAR(100),price DECIMAL(8,2)
);
CREATE TABLE db2.t_product LIKE db1.t_product;
CREATE TABLE db3.t_product LIKE db1.t_product;CREATE TABLE db1.t_config (k VARCHAR(50) PRIMARY KEY,v VARCHAR(200)
);
CREATE TABLE db2.t_config LIKE db1.t_config;
CREATE TABLE db3.t_config LIKE db1.t_config;
六、MyCat 配置
6.1 server.xml —— 用戶、邏輯庫
<user name="root" defaultAccount="true"><property name="password">123456</property><property name="schemas">shop</property>
</user>
6.2 schema.xml —— 邏輯表、節點、主機
<schema name="shop" checkSQLschema="false" sqlMaxLimit="100"><!-- 1) 分片表 --><table name="t_order" dataNode="dn1,dn2,dn3" rule="mod-long" /><!-- 2) 廣播表 --><table name="t_product" dataNode="dn1,dn2,dn3" type="global" /><table name="t_config" dataNode="dn1,dn2,dn3" type="global" />
</schema><!-- 數據節點 -->
<dataNode name="dn1" dataHost="host1" database="db1" />
<dataNode name="dn2" dataHost="host2" database="db2" />
<dataNode name="dn3" dataHost="host3" database="db3" /><!-- 物理主機 -->
<dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql"><heartbeat>select user()</heartbeat><writeHost host="hostM1" url="192.168.1.100:3306" user="root" password="mysql123" />
</dataHost>
<dataHost name="host2" ...> ... </dataHost>
<dataHost name="host3" ...> ... </dataHost>
6.3 rule.xml —— 取模算法
<tableRule name="mod-long"><rule><columns>user_id</columns><algorithm>mod-long</algorithm></rule>
</tableRule><function name="mod-long" class="io.mycat.route.function.PartitionByMod"><property name="count">3</property> <!-- 3 節點 × 2 表 = 6 分片 -->
</function>
七、啟動 MyCat
bin/mycat start # 啟動
tail -f logs/mycat.log # 觀察 “success”
測試連通:
mysql -uroot -p123456 -h127.0.0.1 -P8066 -Dshop
八、Java 代碼示例(零侵入)
8.1 Maven 依賴
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version>
</dependency>
8.2 連接池配置
spring:datasource:url: jdbc:mysql://192.168.1.10:8066/shop?useSSL=false&serverTimezone=UTCusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
8.3 訂單 DAO(MyBatis 示例)
@Mapper
public interface OrderMapper {@Insert("INSERT INTO t_order(user_id,amount) VALUES(#{userId},#{amount})")void insert(@Param("userId") Long userId, @Param("amount") BigDecimal amount);@Select("SELECT * FROM t_order WHERE user_id = #{userId}")List<Order> findByUserId(Long userId);
}
8.4 商品 DAO
@Select("SELECT * FROM t_product WHERE id = #{id}")
Product getProduct(Integer id);
商品表全局廣播,JOIN 時不會跨庫:
SELECT o.id, o.amount, p.name
FROM t_order o
JOIN t_product p ON o.product_id = p.id
WHERE o.user_id = 123; -- 只在 1 個分片執行
九、擴容與運維
9.1 水平擴容(從 3 → 6 節點)
- 新增 3 臺 MySQL,建
db4/db5/db6
,建相同 6 張分表t_order_6 … t_order_11
。 - 修改
rule.xml
把count
改成 6。 - 用 mysqldump / mydumper 把舊數據按
user_id mod 6
重新分布。 - 灰度切流 → 觀察 → 下線舊節點。
9.2 一致性校驗(廣播表)
# 1. 安裝 percona-toolkit
pt-table-checksum h=192.168.1.100,u=checksum_user,p=xxx \--databases=db1,db2,db3 --tables=t_product,t_config
# 2. 差異行修復
pt-table-sync --print --execute ... # 自動生成修復 SQL
十、踩坑與最佳實踐
坑 | 說明 | 解決方案 |
---|---|---|
全局序列 | 自增主鍵在分片后沖突 | 雪花算法 / MyCat 全局序列 |
深分頁 | LIMIT 1000000,10 會拉全表 | 游標分頁 / ES 搜索 |
跨分片 JOIN | MyCat 只能內存合并 | 反范式冗余或應用層拼裝 |
廣播表 DDL | 漏執行導致查詢報錯 | 統一腳本 + pt-osc |
十一、小結
- MyCat = 透明代理 + 路由規則 + 全局表 + 讀寫分離,幾分鐘就能把單機 MySQL 擴展到百節點百億行。
- 小表全局廣播,大表水平拆分,業務代碼零改動。
- 監控、擴容、一致性校驗要提前規劃,否則 3 個月后追悔莫及。
參考資料
MyCat 官方文檔 https://www.yuque.com/books/share/05b6e74e-9a1a-4e5d-a21e-4f93e9e3d5a3