Skip to content

MyCat — MySQL 分布式中间件

架构概览

MyCat 是基于 MySQL 协议的透明数据库代理,应用无需修改代码即可实现分库分表。

应用 ──► MyCat(MySQL 协议代理)──► MySQL1(分片1)
                               ──► MySQL2(分片2)
                               ──► MySQL3(分片3)

MyCat 核心配置文件:
  server.xml    → 用户、端口、全局配置
  schema.xml    → 逻辑库、逻辑表、数据节点
  rule.xml      → 分片规则

核心配置

schema.xml

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

  <!-- 逻辑库 -->
  <schema name="order_db" checkSQLschema="false" sqlMaxLimit="100">
    
    <!-- 分片表 -->
    <table name="order" dataNode="dn1,dn2,dn3,dn4" 
           rule="order-sharding-rule" primaryKey="order_id">
      <!-- 子表(与父表同分片) -->
      <childTable name="order_item" joinKey="order_id" parentKey="order_id"/>
    </table>
    
    <!-- 全局表(每个节点都有完整数据) -->
    <table name="province" type="global" dataNode="dn1,dn2,dn3,dn4"/>
    
    <!-- 不分片的表(只在 dn1) -->
    <table name="config" dataNode="dn1"/>
    
  </schema>

  <!-- 数据节点 -->
  <dataNode name="dn1" dataHost="host1" database="order_db"/>
  <dataNode name="dn2" dataHost="host2" database="order_db"/>
  <dataNode name="dn3" dataHost="host3" database="order_db"/>
  <dataNode name="dn4" dataHost="host4" database="order_db"/>

  <!-- 数据主机(支持主从) -->
  <dataHost name="host1" maxCon="1000" minCon="10" balance="1"
            writeType="0" dbType="mysql" dbDriver="native"
            switchType="1" slaveThreshold="100">
    <heartbeat>select user()</heartbeat>
    <writeHost host="hostM1" url="mysql1:3306" user="root" password="password">
      <readHost host="hostS1" url="mysql1-slave:3306" user="root" password="password"/>
    </writeHost>
  </dataHost>

</mycat:schema>

rule.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">

  <tableRule name="order-sharding-rule">
    <rule>
      <columns>user_id</columns>
      <algorithm>mod-long</algorithm>
    </rule>
  </tableRule>

  <!-- 取模分片 -->
  <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <property name="count">4</property>  <!-- 分片数 -->
  </function>

  <!-- 范围分片(按 ID 范围) -->
  <function name="range-long" class="io.mycat.route.function.AutoPartitionByLong">
    <property name="mapFile">autopartition-long.txt</property>
  </function>

  <!-- 一致性哈希 -->
  <function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
    <property name="seed">0</property>
    <property name="count">4</property>
    <property name="virtualBucketTimes">160</property>
  </function>

  <!-- 按日期分片 -->
  <function name="sharding-by-month" class="io.mycat.route.function.PartitionByMonth">
    <property name="dateFormat">yyyy-MM-dd</property>
    <property name="sBeginDate">2024-01-01</property>
  </function>

</mycat:rule>

server.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">

  <system>
    <property name="serverPort">8066</property>      <!-- 数据端口 -->
    <property name="managerPort">9066</property>     <!-- 管理端口 -->
    <property name="processors">8</property>
    <property name="processorBufferPool">409600</property>
    <property name="sequnceHandlerType">2</property> <!-- 全局序列类型:2=时间戳方式 -->
  </system>

  <user name="mycat_user" defaultAccount="true">
    <property name="password">mycat_password</property>
    <property name="schemas">order_db</property>
  </user>

</mycat:server>

全局序列(分布式 ID)

xml
<!-- server.xml 配置序列类型 -->
<!-- 0: 本地文件方式(不推荐,重启后可能重复) -->
<!-- 1: 数据库方式 -->
<!-- 2: 时间戳方式(18位,推荐) -->
<property name="sequnceHandlerType">2</property>
sql
-- 使用全局序列
INSERT INTO order (order_id, user_id, amount) 
VALUES (next value for MYCATSEQ_ORDER, 123, 99.9);

读写分离配置

xml
<dataHost name="host1" balance="1" writeType="0">
  <!-- balance 参数:
    0: 不开启读写分离,所有读写都到 writeHost
    1: 全部读操作随机分发到 readHost(writeHost 不参与读)
    2: 所有读操作随机分发到 writeHost 和 readHost
    3: 所有读操作随机分发到 readHost(writeHost 不参与读,与1类似)
  -->
  
  <!-- writeType 参数:
    0: 所有写操作发送到第一个 writeHost
    1: 所有写操作随机发送到 writeHost(不推荐)
  -->
  
  <writeHost host="M1" url="mysql-master:3306" user="root" password="pwd">
    <readHost host="S1" url="mysql-slave1:3306" user="root" password="pwd"/>
    <readHost host="S2" url="mysql-slave2:3306" user="root" password="pwd"/>
  </writeHost>
</dataHost>

管理命令

bash
# 连接管理端口
mysql -h127.0.0.1 -P9066 -umycat -pmycat_password

# 查看数据源状态
show @@datasource;

# 查看连接池
show @@connection;

# 查看后端连接
show @@backend;

# 重新加载配置(不重启)
reload @@config;
reload @@config_all;

# 查看慢查询
show @@slow where schema='order_db';

# 切换主从(主库宕机时手动切换)
switch @@datasource where name='host1' and index=1;

故障处理案例

案例一:主库宕机,自动切换失败

现象:主库宕机后,MyCat 未自动切换到备库。

排查

bash
# 查看数据源状态
mysql -P9066 -e "show @@datasource;"

# 手动切换
mysql -P9066 -e "switch @@datasource where name='host1' and index=1;"

配置自动切换

xml
<dataHost switchType="1">  <!-- 1: 自动切换 -->
  <!-- 心跳检测 -->
  <heartbeat>select user()</heartbeat>
</dataHost>

案例二:跨分片 JOIN 性能问题

现象:涉及多个分片的 JOIN 查询非常慢。

原因:MyCat 需要从多个分片获取数据,在内存中做笛卡尔积 JOIN。

解决

  1. 使用 ER 表(子表与父表同分片)
  2. 使用全局表(字典表每个分片都有)
  3. 在应用层做 JOIN(分两次查询)

案例三:连接池耗尽

现象:应用报 No connections available

解决

xml
<dataHost maxCon="1000" minCon="10">
  <!-- 增大连接池 -->
</dataHost>
xml
<!-- server.xml 增大处理线程 -->
<property name="processors">16</property>
<property name="processorExecutor">32</property>

MyCat vs ShardingSphere

维度MyCatShardingSphere
接入方式代理(语言无关)JDBC(Java)/ 代理
性能有代理开销JDBC 模式无额外开销
功能基础分库分表功能更丰富(数据迁移、加密等)
社区活跃度较低Apache 顶级项目,活跃
适用场景老项目改造,多语言新项目,Java 生态

PaaS 中间件生态系统深度学习文档