彻底分清:垂直分库、水平分库、垂直分表、水平分表(附 Mycat 完整配置)

Mycat 分库分表配置详解:垂直/水平 分库/分表 一网打尽

Mycat 作为经典的数据库中间件,其核心功能就是对数据进行分片,而分片又常被划分为四种典型策略:垂直分库、水平分库、垂直分表、水平分表 。很多人在配置时对 rule 的写法、分片键的位置、多表规则能否复用等问题感到困惑。本文将用最清晰的配置示例和总结,一次性讲透这四种方式,并给出可直接套用的模板。

本文基于 Mycat 1.6+ 版本,配置均以 schema.xmlrule.xml 为例。


0. 公共基础结构

在开始之前,先给出 Mycat 的 schema.xml 基本骨架,后面的所有配置都将基于此展开:

复制代码
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

  <!-- 逻辑库 -->
  <schema name="MYCAT_DB" checkSQLschema="false" sqlMaxLimit="100">
    <!-- 各种 <table> 配置放在这里 -->
  </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" dbDriver="native">
    <heartbeat>select user()</heartbeat>
    <writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="123456"/>
  </dataHost>
  <!-- host2、host3 配置类似,略 -->

</mycat:schema>

rule.xml 的基本结构:

复制代码
<?xml version="1.0"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
  <!-- 这里放 tableRule 和 function -->
</mycat:rule>

1. 垂直分库(按业务分库,整表迁移)

特点 :不同业务表放在不同的数据库节点上,不涉及数据行的拆分,不需要配置分片规则(rule)

场景:用户表、订单表、商品表分别存入不同的物理库。

schema.xml 配置

复制代码
<schema name="MYCAT_DB" checkSQLschema="false" sqlMaxLimit="100">
  <!-- 用户模块:放在 dn1 -->
  <table name="t_user"      dataNode="dn1" primaryKey="id"/>
  <table name="t_user_addr" dataNode="dn1" primaryKey="id"/>

  <!-- 订单模块:放在 dn2 -->
  <table name="t_order"     dataNode="dn2" primaryKey="order_id"/>
  <table name="t_order_item" dataNode="dn2" primaryKey="id"/>

  <!-- 商品模块:放在 dn3 -->
  <table name="t_goods"     dataNode="dn3" primaryKey="id"/>
</schema>
  • 每一张表都完整存在于某个库中,Mycat 仅负责将 SQL 路由到对应的数据节点。
  • 无 rule,因为数据没有被打散。

2. 水平分库(一张表的数据散列到多个库)

特点:同一张表的结构在多个库中都有,数据按分片键分散存储,必须配置分片规则(rule)

与水平分表的核心区别:水平分库的 <table> 里是多个 dataNode,且没有 subTables

为避免和单库分表混淆,这里用两张关联表(订单表、订单项表)同时进行水平分库的示例。

schema.xml

复制代码
<schema name="MYCAT_DB" checkSQLschema="false" sqlMaxLimit="100">
  <!-- 两张表都水平分库到 dn1,dn2,dn3,共用同一个 rule -->
  <table name="t_order"      dataNode="dn1,dn2,dn3" rule="mod_rule" primaryKey="id"/>
  <table name="t_order_item" dataNode="dn1,dn2,dn3" rule="mod_rule" primaryKey="id"/>
</schema>

rule.xml(取模分片)

复制代码
<tableRule name="mod_rule">
  <rule>
    <columns>id</columns>              <!-- 分片键 -->
    <algorithm>mod-long</algorithm>
  </rule>
</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
  <property name="count">3</property> <!-- 分为3个库 -->
</function>

说明

  • 分片键 id 写在 rule 的 <columns> 里,物理表中必须包含该字段。
  • 多张表共用 mod_rule,Mycat 会根据每张表的分片键独立路由到对应库,完全不需要重复定义 rule。
  • 因为两张表的分片规则一致,关联查询(如 JOIN ON id)时,数据大概率落在同一分片节点,避免跨库 JOIN。

3. 垂直分表(单库内大表拆小表:主表 + 扩展表)

特点 :在同一个数据库中,将一张表的字段拆分成多张表(如常用字段与大字段分离)。

核心真相:垂直分表是纯人工手动拆表,Mycat 不参与任何字段拆分或合并逻辑,它只负责声明表在哪个节点!

场景 :商品表拆分为 t_goods_main(高频小字段)和 t_goods_ext(大文本字段)。

物理建表(自己在 MySQL 中执行)

复制代码
-- 主表:常用字段
CREATE TABLE t_goods_main(
    id INT PRIMARY KEY,
    goods_name VARCHAR(50),
    price DECIMAL(10,2),
    stock INT
);

-- 扩展表:大字段、冷门字段
CREATE TABLE t_goods_ext(
    goods_id INT PRIMARY KEY,   -- 与主表 id 一一对应
    goods_detail LONGTEXT,
    goods_img TEXT
);

schema.xml 配置

复制代码
<schema name="MYCAT_DB" checkSQLschema="false" sqlMaxLimit="100">
  <!-- 两张表都在同一个数据节点 dn1,没有 rule -->
  <table name="t_goods_main" dataNode="dn1" primaryKey="id"/>
  <table name="t_goods_ext"  dataNode="dn1" primaryKey="goods_id"/>
</schema>

Mycat 这段配置只做了三件事

  1. 告诉 Mycat 这两张表都在 dn1 库中;
  2. 声明各自的主键字段;
  3. 不控制任何字段,字段如何拆分完全由建表时决定。

正因为是"纯手动",所以垂直分表不需要任何 rule,增删改查也需要自己写关联 SQL(见下文第 7 节)。


4. 水平分表(单库内,逻辑表拆多张物理子表)

特点 :在同一个数据库中,一张逻辑表对应多张物理子表(如 t_log_0, t_log_1 ......),数据按分片键自动路由到对应的子表。必须配置 rule

schema.xml

复制代码
<schema name="MYCAT_DB" checkSQLschema="false" sqlMaxLimit="100">
  <!-- 单库 dn1,分成 4 张子表:t_log_0 ~ t_log_3 -->
  <table name="t_log" subTables="t_log_$0-3" dataNode="dn1" rule="log_rule" primaryKey="id"/>
</schema>

rule.xml(单库内取模)

复制代码
<tableRule name="log_rule">
  <rule>
    <columns>id</columns>
    <algorithm>mod-long</algorithm>
  </rule>
</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
  <property name="count">4</property> <!-- 单库分4张表 -->
</function>

注意subTables="t_log_$0-3" 表示物理表名称为 t_log_0t_log_1t_log_2t_log_3,Mycat 会根据分片算法自动计算目标子表。


5. 组合:水平分库 + 水平分表(常用)

将数据先分到多个库,每个库内再分多张子表。例如订单表分 2 个库,每个库内分 2 张子表,总共 4 个分片。

schema.xml

复制代码
<table name="t_order" dataNode="dn1,dn2" subTables="t_order_$0-1" rule="order_rule" primaryKey="id"/>

rule.xml

复制代码
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
  <property name="count">4</property> <!-- 2库 × 2表 = 4 -->
</function>

6. 多表分片规则:复用与独立性

6.1 同分片规则 → 复用同一个 rule(推荐)

如果多张关联表都按照相同的分片键和算法分片,只需定义一个 rule,多次引用即可。

复制代码
<!-- schema.xml -->
<table name="t_order"      dataNode="dn1,dn2,dn3" rule="mod_rule" primaryKey="id"/>
<table name="t_order_item" dataNode="dn1,dn2,dn3" rule="mod_rule" primaryKey="id"/>

rule.xml 中只写一份 mod_rule全局复用

6.2 不同表分片规则可以不一致(允许)

对于毫无关联的业务表,可以使用完全不同的分片键和分片算法。

复制代码
<!-- schema.xml -->
<table name="t_order" dataNode="dn1,dn2,dn3" rule="mod_rule" primaryKey="order_id"/>
<table name="t_pay"   dataNode="dn1,dn2,dn3" rule="range_rule" primaryKey="user_id"/>

rule.xml 定义两套独立的规则:

复制代码
<!-- 规则1:取模 -->
<tableRule name="mod_rule">
    <rule>
        <columns>order_id</columns>
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>

<!-- 规则2:范围分片 -->
<tableRule name="range_rule">
    <rule>
        <columns>user_id</columns>
        <algorithm>range-hash</algorithm>
    </rule>
</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <property name="count">3</property>
</function>
<function name="range-hash" class="io.mycat.route.function.AutoPartitionByLong">
    <property name="mapFile">partition-hash-long.txt</property>
</function>

6.3 重要原则:同业务关联表必须统一分片字段

  • 订单、订单项、物流、支付等需要关联查询的表,必须使用相同的分片键和分片规则,否则 JOIN 将跨库,性能极差甚至报错。
  • 毫无关联的独立表(如用户表按 uid 分,日志表按 id 分)允许不同。

正确示例 :订单表和订单详情表都按 user_id 分片,且共用 user_rule,联表时数据在同一个库,性能高。

复制代码
<!-- 两张表都使用同一个 user_rule,分片键都是 user_id -->
<table name="t_order"      dataNode="dn1,dn2" rule="user_rule" primaryKey="id"/>
<table name="t_order_item" dataNode="dn1,dn2" rule="user_rule" primaryKey="id"/>

同时,物理表中必须都包含 user_id 字段,否则 Mycat 无法正确路由。

复制代码
CREATE TABLE t_order      (id INT, user_id INT, ...);
CREATE TABLE t_order_item (id INT, user_id INT, order_id INT, ...);

7. 垂直分表的插入与查询注意事项

垂直分表后,插入数据必须使用两条 INSERT 语句,不能一条 SQL 同时插入两张表

复制代码
START TRANSACTION;
INSERT INTO t_goods_main(id, goods_name, price) VALUES(1001, '手机', 2999);
INSERT INTO t_goods_ext(goods_id, goods_detail) VALUES(1001, '超长详情...');
COMMIT;

查询时只需要一条联表 SQL(因为两表在同一库,性能很好):

复制代码
SELECT m.*, e.goods_detail
FROM t_goods_main m
LEFT JOIN t_goods_ext e ON m.id = e.goods_id
WHERE m.id = 1001;

Mycat 在此处只负责将 SQL 路由到正确节点,不做任何自动拆分或合并字段的操作。


8. 四种策略本质对比总结

策略 核心操作 是否拆分数据 是否需要 rule 分片键位置 Mycat 参与度
垂直分库 不同表存不同库 不需要 按配置路由表到指定节点
水平分库 同一张表数据散列到多个库 必须 rule 的 <columns> 自动分片路由
垂直分表 同库内一张表拆成多张表(字段拆分) 不需要 只登记表位置,拆分人为完成
水平分表 同库内逻辑表拆成多张物理子表 必须 rule 的 <columns> 自动路由到正确的子表
相关推荐
techdashen3 小时前
Uber 如何让 MySQL Group Replication 在大规模生产环境中可用:自动化、故障转移与一致性保障
数据库·mysql·自动化
木木子227 小时前
# 密码强度检测深度解析:正则表达式实时分析、多维度评分算法与可视化反馈
mysql·算法·华为·正则表达式·harmonyos
一嘴一个橘子12 小时前
mysql 创建表
mysql
ywl47081208715 小时前
Mysql 平衡二叉树、红黑树、B树、B+树区别以及应用场景(五)
数据库·b树·mysql
作陪16 小时前
小白也能看懂的 SQL 零基础教程:从关系模型到事务全解
mysql
ClouGence17 小时前
MySQL 到 StarRocks 数据迁移同步:同步方案与实践指南
数据库·mysql
田超凡19 小时前
深入理解MySQL_10 Explain的用法
mysql·java-ee
cyforkk21 小时前
MySQL锁机制详解:从乐观锁到悲观锁
数据库·mysql
ShiXZ21321 小时前
指令集-SQL 常用指令速查手册
数据库·sql·mysql·oracle