本文目录导读:

- 案例一:基础读写分离(一主一从)
- 案例二:垂直分库(业务拆分)
- 案例三:水平分表(分库分表核心)
- 案例四:全局表(字典表冗余)
- 案例五:分片 + 读写分离组合(生产高可用)
- 高级案例:多租户(按租户 ID 分片)
- MyCat 常见注意点与性能陷阱
MyCat 作为经典的数据库中间件,主要用于分库分表、读写分离和多租户隔离,下面提供几个从入门到进阶的真实案例,涵盖核心配置和业务场景。
基础读写分离(一主一从)
场景:单库压力大,读多写少,需要将查询请求分发到从库。
核心配置 schema.xml:
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 逻辑库 -->
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- 逻辑表:指向 dataNode -->
<table name="user" primaryKey="id" dataNode="dn_user" />
</schema>
<!-- 数据节点:指向具体主机 -->
<dataNode name="dn_user" dataHost="host1" database="user_db" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
<!-- writeHost 为主库,readHost 为从库 -->
<writeHost host="192.168.1.100" url="192.168.1.100:3306" user="root" password="123456">
<readHost host="192.168.1.101" url="192.168.1.101:3306" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
使用效果:
- 执行
INSERT/UPDATE/DELETE时,MyCat 会将 SQL 路由到168.1.100(主库)。 - 执行
SELECT时,MyCat 会根据balance="1"的负载均衡算法,将请求分发到168.1.100和168.1.101,有效降低主库读压力。
垂直分库(业务拆分)
场景:将订单和用户业务拆分为独立的数据库,降低单一数据库的表数量。
核心配置 schema.xml:
<schema name="SHOP" checkSQLschema="false" sqlMaxLimit="100">
<!-- 订单表 -> 订单库 -->
<table name="orders" primaryKey="id" dataNode="dn_order" />
<!-- 用户表 -> 用户库 -->
<table name="users" primaryKey="id" dataNode="dn_user" />
<!-- 关联表:跨库查询(需全局表或ER Join) -->
<table name="order_items" primaryKey="id" dataNode="dn_order,dn_user" rule="sharding-by-intfile" />
</schema>
<dataNode name="dn_order" dataHost="host1" database="order_db" />
<dataNode name="dn_user" dataHost="host2" database="user_db" />
<dataHost name="host1" ...>...</dataHost>
<dataHost name="host2" ...>...</dataHost>
使用效果:
- 订单相关的 SQL 自动发往
host1的order_db。 - 用户相关的 SQL 自动发往
host2的user_db。 - 注意:
SELECT * FROM orders o JOIN users u ON o.user_id = u.id这种跨库关联查询在分库后无法直接执行,通常需要修改为应用层拼接,或者在 MyCat 中配置全局表或ER Join。
水平分表(分库分表核心)
场景:订单表数据量极大(亿级),需要按规则将数据分散到多个 MySQL 实例中。
核心配置 schema.xml:
<schema name="BIGDATA" checkSQLschema="false" sqlMaxLimit="100">
<!-- 水平拆分:订单表按 id 取模 3,数据分布到 3 个数据节点 -->
<table name="order_info" primaryKey="id" dataNode="dn1,dn2,dn3" rule="mod-long" />
</schema>
<dataNode name="dn1" dataHost="host1" database="db1" />
<dataNode name="dn2" dataHost="host2" database="db2" />
<dataNode name="dn3" dataHost="host3" database="db3" />
核心配置 rule.xml:
<!-- 取模分片规则:按 id % 3 分配 -->
<rule name="mod-long">
<columns>id</columns>
<algorithm>mod-long</algorithm>
</rule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<property name="count">3</property> <!-- 分片数量 -->
</function>
使用效果:
INSERT INTO order_info (id, name) VALUES (1, 'a')-> 数据写入dn1(因为 1%3=1)。SELECT * FROM order_info WHERE id = 5-> MyCat 直接路由到dn2,查询速度极快。SELECT * FROM order_info WHERE name = 'x'(无分片键)-> MyCat 会广播到所有 3 个节点进行查询(全表扫描),然后合并结果。
全局表(字典表冗余)
场景:分库后,每个分片数据库都有自己的 dict(字典表),避免跨库 JOIN。status_name 表。
核心配置 schema.xml:
<schema name="BIGDATA" checkSQLschema="false" sqlMaxLimit="100">
<table name="order_info" primaryKey="id" dataNode="dn1,dn2,dn3" rule="mod-long" />
<!-- type="global" 表示全局表,数据自动冗余到所有分片 -->
<table name="dict" primaryKey="code" dataNode="dn1,dn2,dn3" type="global" />
</schema>
使用效果:
- 当执行
INSERT INTO dict (code, name) VALUES ('01', '待支付')时,MyCat 会将这条数据同时插入到dn1、dn2、dn3三个库中。 - 查询时,
SELECT * FROM order_info o JOIN dict d ON o.status = d.code无需跨库,因为每个分片库中都有完整的dict表,Join 操作可以直接在本地完成。
分片 + 读写分离组合(生产高可用)
场景:每个分片再有主从同步,实现水平扩展 + 高可用。
核心配置 schema.xml:
<!-- 分片1:使用 host1(主)和 host2(从) -->
<dataNode name="dn1" dataHost="shard1" database="db1" />
<!-- 分片2:使用 host3(主)和 host4(从) -->
<dataNode name="dn2" dataHost="shard2" database="db2" />
<dataNode name="dn3" dataHost="shard3" database="db3" />
<dataHost name="shard1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
<writeHost host="host1" url="host1:3306" user="root" password="123">
<readHost host="host2" url="host2:3306" user="root" password="123" />
</writeHost>
</dataHost>
<dataHost name="shard2" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
<writeHost host="host3" url="host3:3306" user="root" password="123">
<readHost host="host4" url="host4:3306" user="root" password="123" />
</writeHost>
</dataHost>
<!-- shard3 配置类似... -->
使用效果:
- 写入
dn1分片时,只写host1,从库host2自动复制。 - 读取
dn1分片时,可以在host1和host2之间负载均衡。 host1宕机,MyCat 自动将写操作切到host2(如果开启了故障切换),保证高可用。
高级案例:多租户(按租户 ID 分片)
场景:SaaS 系统,不同租户的数据隔离,且租户间数据量差异大。
配置 schema.xml 和 rule.xml:
- 规则:使用
PartitionByString或PartitionByPrefixPattern,根据tenant_id字符串的特征(如首字符或哈希)进行分片。 - 所有 SQL 必须强制携带
tenant_id,否则 MyCat 会广播所有分片,导致性能问题。
<table name="biz_data" primaryKey="id" dataNode="dn1,dn2,dn3,dn4" rule="hash-tenancy" />
<function name="hash-tenancy" class="io.mycat.route.function.PartitionByHash">
<property name="partitionCount">4</property> <!-- 4 个分区 -->
</function>
使用效果:
- 查询时:
WHERE tenant_id = 'T001',MyCat 秒速定位到特定分片。 - 查询时:
WHERE name = 'x'(缺少租户条件),MyCat 需要扫描 4 个分片,性能极差(在这个场景下需要业务层强制限制)。
MyCat 常见注意点与性能陷阱
- 分片键必须出现在 WHERE 中:
SELECT * FROM order WHERE id = 100很快(直接路由);SELECT * FROM order WHERE name = 'xx'会全分片扫描(慢)。 - 跨分片 JOIN 限制:MyCat 支持跨分片 JOIN(如
order和order_item使用ER Join),但如果跨分片且无关联关系,必须使用全局表或应用层处理。 - 分布式事务弱化:MyCat 默认
SET AUTOCOMMIT=0的分布式事务有性能开销(XA 协议),通常建议业务尽量使用本地事务。 - 计数限制:
COUNT(*)在大数据分片下,MyCat 需要聚合所有分片结果,数据量巨大时建议在应用层通过Redis维护计数。
如果需要针对上述某个具体案例提供 具体的 server.xml(账号权限)配置 或 启动/连接测试命令,可以随时告诉我,我可以继续补充。