从 0 到 1:用 MyCat 打造可水平扩展的 MySQL 分库分表架构

一、为什么要分库分表?

单机 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

bash 复制代码
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 端建库建表

每台机执行:

sql 复制代码
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 ------ 用户、逻辑库

xml 复制代码
<user name="root" defaultAccount="true">
    <property name="password">123456</property>
    <property name="schemas">shop</property>
</user>

6.2 schema.xml ------ 逻辑表、节点、主机

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 ------ 取模算法

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

bash 复制代码
bin/mycat start   # 启动
tail -f logs/mycat.log  # 观察 "success"

测试连通:

bash 复制代码
mysql -uroot -p123456 -h127.0.0.1 -P8066 -Dshop

八、Java 代码示例(零侵入)

8.1 Maven 依赖

xml 复制代码
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>

8.2 连接池配置

yaml 复制代码
spring:
  datasource:
    url: jdbc:mysql://192.168.1.10:8066/shop?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

8.3 订单 DAO(MyBatis 示例)

java 复制代码
@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

java 复制代码
@Select("SELECT * FROM t_product WHERE id = #{id}")
Product getProduct(Integer id);

商品表全局广播,JOIN 时不会跨库:

sql 复制代码
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 节点)

  1. 新增 3 台 MySQL,建 db4/db5/db6,建相同 6 张分表 t_order_6 ... t_order_11
  2. 修改 rule.xmlcount 改成 6。
  3. mysqldump / mydumper 把旧数据按 user_id mod 6 重新分布。
  4. 灰度切流 → 观察 → 下线旧节点。

9.2 一致性校验(广播表)

bash 复制代码
# 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

相关推荐
凤凰战士芭比Q19 分钟前
LNMP环境部署 KodBox私有云盘
linux·架构
小猪咪piggy2 小时前
【微服务】(4) 负载均衡
微服务·云原生·架构
小白银子2 小时前
零基础从头教学Linux(Day 60)
linux·数据库·mysql·oracle
憋问我,我也不会2 小时前
MYSQL 命令
数据库·mysql
短视频矩阵源码定制3 小时前
矩阵系统哪个好?2025年全方位选型指南与品牌深度解析
java·人工智能·矩阵·架构·aigc
无泡汽水3 小时前
MySQL入门练习50题
数据库·mysql
00后程序员张4 小时前
iOS 上架费用全解析 开发者账号、App 审核、工具使用与开心上架(Appuploader)免 Mac 成本优化指南
android·macos·ios·小程序·uni-app·cocoa·iphone
来来走走4 小时前
Android开发(Kotlin) 扩展函数和运算符重载
android·开发语言·kotlin
wuwu_q4 小时前
用通俗易懂 + Android 开发实战的方式,详细讲解 Kotlin Flow 中的 retryWhen 操作符
android·开发语言·kotlin
云外天ノ☼4 小时前
待办事项全栈实现:Vue3 + Node.js (Koa) + MySQL深度整合,构建生产级任务管理系统的技术实践
前端·数据库·vue.js·mysql·vue3·koa·jwt认证