当前位置: 首页 > news >正文

基于MyCat 中间件实现mysql集群读写分离与从库负载均衡教程(详细案例教程)

基于MyCat 中间件实现mysql集群读写分离与从库负载均衡(详细案例教程)

本案例基于 MyCat 2.0 + MySQL 8.0 一主两从集群 + Spring Boot 2.7.x 实现,通过 MyCat 作为中间件代理层,让应用无感知实现读写分离(写走主库、读走从库)与从库负载均衡,适合中大型项目或多服务集群场景。

一、案例环境准备

1. 服务器规划

角色IP地址操作系统核心软件用途说明
主库(Master)192.168.1.100Ubuntu 24.04MySQL 8.0.42处理所有写请求,同步数据到从库
从库1(Slave1)192.168.1.101Ubuntu 24.04MySQL 8.0.42处理读请求,负载均衡节点1
从库2(Slave2)192.168.1.102Ubuntu 24.04MySQL 8.0.42处理读请求,负载均衡节点2
MyCat 中间件192.168.1.200Ubuntu 24.04MyCat 2.0.1代理数据库请求,实现读写分离
应用服务器192.168.1.300Ubuntu 24.04Spring Boot 2.7.x业务应用,连接 MyCat 访问数据库

2. 前置条件

  1. 已完成 MySQL 一主两从集群配置(主从同步正常,从库 read_only=1);
  2. 所有服务器网络互通,关闭防火墙或开放关键端口:
    • MySQL 端口:3306(主从库需开放给 MyCat);
    • MyCat 端口:8066(应用连接 MyCat 的服务端口)、9066(MyCat 管理端口);
  3. 主库已创建业务数据库(如 test)和表(如 user),从库已同步该数据。

二、Step 1:部署 MyCat 中间件(192.168.1.200 服务器)

MyCat 2.0 需依赖 JDK 1.8+,先安装 JDK,再部署 MyCat。

1. 安装 JDK 1.8

# 1. 安装 OpenJDK 1.8
sudo apt update
sudo apt install openjdk-8-jdk -y# 2. 验证 JDK 安装(输出 java version "1.8.0_xxx" 即成功)
java -version

2. 下载并解压 MyCat 2.0

# 1. 进入安装目录(如 /opt)
cd /opt# 2. 下载 MyCat 2.0.1 压缩包(官方地址,也可手动下载上传)
sudo wget https://github.com/MyCATApache/MyCat2/releases/download/2.0.1/MyCat-server-2.0.1-linux.tar.gz# 3. 解压压缩包
sudo tar -zxvf MyCat-server-2.0.1-linux.tar.gz# 4. 查看解压后的目录(MyCat 核心目录:bin(启动脚本)、conf(配置文件)、lib(依赖))
ls /opt/MyCat-server-2.0.1

3. 配置 MyCat 环境变量(可选,方便命令启动)

# 1. 编辑环境变量配置文件
sudo vim /etc/profile# 2. 在文件末尾添加以下内容(MyCat 解压路径)
export MYCAT_HOME=/opt/MyCat-server-2.0.1
export PATH=$PATH:$MYCAT_HOME/bin# 3. 生效环境变量
source /etc/profile# 4. 验证(输入 mycat 命令,显示用法即成功)
mycat

三、Step 2:配置 MyCat 核心参数(实现读写分离)

MyCat 实现读写分离的核心是 3 个配置文件

  1. conf/datasources.xml:配置主从库的物理数据源(IP、账号、密码);
  2. conf/schema.xml:定义逻辑库、逻辑表、数据节点,关联物理数据源与路由规则;
  3. conf/user.xml:配置 MyCat 管理账号(应用连接 MyCat 时使用)。

1. 配置物理数据源(datasources.xml)

该文件用于告诉 MyCat:“主从库在哪里,用什么账号连接”。

# 进入 MyCat 配置目录
cd /opt/MyCat-server-2.0.1/conf# 编辑 datasources.xml(先备份原文件,避免配置错误)
sudo cp datasources.xml datasources.xml.bak
sudo vim datasources.xml

将文件内容替换为以下配置(根据实际主从库 IP、账号修改):

<?xml version="1.0" encoding="UTF-8"?>
<datasources><!-- 1. 主库数据源(master_ds 为自定义名称,需与 schema.xml 对应) --><dataSource name="master_ds" type="mysql"><property name="url">jdbc:mysql://192.168.1.100:3306/test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true</property><property name="user">root</property> <!-- 主库账号(需有读写权限) --><property name="password">Root@123456</property> <!-- 主库密码 --><property name="driverClassName">com.mysql.cj.jdbc.Driver</property><property name="initSqls">SET NAMES utf8mb4</property> <!-- 初始化字符集 --><property name="maxConn">100</property> <!-- 最大连接数 --><property name="minConn">10</property> <!-- 最小连接数 --></dataSource><!-- 2. 从库1数据源(slave1_ds 为自定义名称) --><dataSource name="slave1_ds" type="mysql"><property name="url">jdbc:mysql://192.168.1.101:3306/test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true</property><property name="user">read_user</property> <!-- 从库只读账号 --><property name="password">Read@123456</property> <!-- 从库密码 --><property name="driverClassName">com.mysql.cj.jdbc.Driver</property><property name="initSqls">SET NAMES utf8mb4</property><property name="maxConn">100</property><property name="minConn">10</property></dataSource><!-- 3. 从库2数据源(slave2_ds 为自定义名称) --><dataSource name="slave2_ds" type="mysql"><property name="url">jdbc:mysql://192.168.1.102:3306/test?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true</property><property name="user">read_user</property> <!-- 从库只读账号 --><property name="password">Read@123456</property> <!-- 从库密码 --><property name="driverClassName">com.mysql.cj.jdbc.Driver</property><property name="initSqls">SET NAMES utf8mb4</property><property name="maxConn">100</property><property name="minConn">10</property></dataSource>
</datasources>

2. 配置逻辑库与路由规则(schema.xml)

该文件是 MyCat 读写分离的核心,用于定义:

  • 逻辑库(应用连接的库名,与物理库 test 对应);
  • 数据节点(关联逻辑库与物理数据源);
  • 读写分离规则(写走主库,读走从库,从库负载均衡)。
# 备份原文件
sudo cp schema.xml schema.xml.bak
sudo vim schema.xml

替换为以下配置:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/"><!-- 1. 逻辑库配置(name:应用连接的库名,如 test_db;checkSQLschema:是否去掉 SQL 中的库名前缀) --><schema name="test_db" checkSQLschema="false" sqlMaxLimit="100"><!-- 逻辑表配置(name:物理表名 user;dataNode:关联的数据节点 dn1;rule:分片规则,这里单表无需分片,用 auto-sharding-long) --><table name="user" dataNode="dn1" rule="auto-sharding-long" /><!-- 若有其他表,继续添加 <table> 标签,如 <table name="order" dataNode="dn1" rule="auto-sharding-long" /> --></schema><!-- 2. 数据节点配置(name:数据节点名 dn1;dataHost:关联的数据源集群;database:物理数据库名 test) --><dataNode name="dn1" dataHost="mysql_cluster" database="test" /><!-- 3. 数据源集群配置(核心:定义主从、读写分离、负载均衡) --><dataHost name="mysql_cluster" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100"><!-- 心跳检测 SQL(MyCat 定期执行该 SQL 检测主从库是否存活) --><heartbeat>select 1</heartbeat><!-- 3.1 写主机(主库):处理所有写请求(INSERT/UPDATE/DELETE/DDL) --><writeHost host="master_host" url="master_ds" user="root" password="Root@123456"><!-- 3.2 读主机(从库):处理所有读请求(SELECT),balance=1 表示从库负载均衡 --><readHost host="slave1_host" url="slave1_ds" user="read_user" password="Read@123456" /><readHost host="slave2_host" url="slave2_ds" user="read_user" password="Read@123456" /></writeHost><!-- (可选)备用主库:若主库宕机,MyCat 自动切换到备用主库(需配置双主复制) --><!-- <writeHost host="backup_master_host" url="backup_master_ds" user="root" password="Root@123456"><readHost host="backup_slave_host" url="backup_slave_ds" user="read_user" password="Read@123456" /></writeHost> --></dataHost></mycat:schema>
关键参数说明(决定读写分离与负载均衡)
参数名取值/配置作用说明
balance1从库负载均衡模式:1 表示“读请求分发到所有从库(轮询)”,0 表示“读请求走主库”,2 表示“读写请求均走所有节点”
writeType0写请求路由模式:0 表示“所有写请求走第一个 writeHost(主库)”,1 表示“写请求随机分发到 writeHost”
switchType1主库故障切换模式:1 表示“基于心跳检测自动切换”,-1 表示“不自动切换”
slaveThreshold100从库延迟阈值(单位:秒):若从库同步延迟超过 100 秒,MyCat 不再将读请求分发到该从库

3. 配置 MyCat 访问账号(user.xml)

该文件用于配置应用连接 MyCat 时使用的账号和权限(类似 MySQL 的账号管理)。

# 备份原文件
sudo cp user.xml user.xml.bak
sudo vim user.xml

替换为以下配置(自定义账号密码,授予逻辑库 test_db 的权限):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:user SYSTEM "user.dtd">
<mycat:user xmlns:mycat="http://io.mycat/"><!-- 应用访问 MyCat 的账号(name:账号名;password:密码) --><user name="mycat_app" password="MyCat@123456"><!-- 授予该账号对逻辑库 test_db 的所有权限(readWrite:读写权限;db:逻辑库名) --><privileges check="false"><schema name="test_db" dml="0" showTables="true"><table name="user" dml="0" /> <!-- dml=0 表示所有 DML 权限(INSERT/UPDATE/DELETE/SELECT) --></schema></privileges></user><!-- (可选)MyCat 管理账号(用于通过 9066 端口管理 MyCat,如查看节点状态) --><user name="mycat_admin" password="Admin@123456" admin="true"><privileges check="false"><schema name="*" dml="0" /></privileges></user></mycat:user>
权限说明
  • dml="0":授予所有 DML 权限(SELECT、INSERT、UPDATE、DELETE);
  • dml="1":仅授予 SELECT 权限(只读);
  • admin="true":标记为管理账号,可通过 9066 端口执行 MyCat 管理命令(如 show @@datasource)。

四、Step 3:启动 MyCat 并验证状态

1. 启动 MyCat

# 进入 MyCat 安装目录(或直接用 mycat 命令,已配置环境变量)
cd /opt/MyCat-server-2.0.1# 启动 MyCat(start:启动;status:查看状态;stop:停止)
sudo ./bin/mycat start# 查看 MyCat 状态(显示“MyCat-server is running.” 即成功)
sudo ./bin/mycat status# 查看 MyCat 日志(若启动失败,通过日志排查错误,如端口被占用、数据源配置错误)
tail -f logs/wrapper.log

2. 验证 MyCat 数据源连接(管理端口 9066)

通过 MySQL 客户端连接 MyCat 的管理端口(9066),查看主从库数据源是否正常:

# 连接 MyCat 管理端口(账号:mycat_admin,密码:Admin@123456,端口:9066)
mysql -u mycat_admin -pAdmin@123456 -h 192.168.1.200 -P 9066# 执行 MyCat 管理命令,查看数据源状态
show @@datasource;
正常输出示例(关键看 STATUS 列)
NAMETYPEHOSTPORTDB_NAMESTATUSACTIVE_CONNIDLE_CONN
master_dsmysql192.168.1.1003306testOK010
slave1_dsmysql192.168.1.1013306testOK010
slave2_dsmysql192.168.1.1023306testOK010
  • STATUS=OK:表示数据源连接正常;
  • STATUS=ERROR:检查主从库 IP、账号密码是否正确,网络是否互通。

3. 验证 MyCat 服务端口(应用连接端口 8066)

通过 MySQL 客户端连接 MyCat 的服务端口(8066),验证逻辑库和表是否正常访问:

# 连接 MyCat 服务端口(账号:mycat_app,密码:MyCat@123456,端口:8066,逻辑库:test_db)
mysql -u mycat_app -pMyCat@123456 -h 192.168.1.200 -P 8066 -D test_db# 执行 SQL 验证(查看逻辑库中的表,应显示 user 表)
show tables;# 查看 user 表数据(应返回主库同步到从库的数据)
select * from user;
  • 若能正常显示表和数据,说明 MyCat 与主从库连接正常,逻辑库配置生效。

五、Step 4:应用对接 MyCat(Spring Boot 示例)

应用无需感知主从库,只需连接 MyCat 的服务端口(8066)和逻辑库(test_db),所有读写请求由 MyCat 自动路由。

1. 应用依赖(pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.15</version><relativePath/></parent><groupId>com.example</groupId><artifactId>mycat-demo</artifactId><version>1.0-SNAPSHOT</version><dependencies><!-- Spring Boot Web(测试接口) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- MyBatis-Plus(数据访问,也可使用 JPA) --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.4.1</version></dependency><!-- MySQL 驱动(适配 MySQL 8.0) --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version><scope>runtime</scope></dependency><!-- Lombok(简化实体类) --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>

2. 应用配置(application.yml)

核心:数据库连接地址改为 MyCat 的 IP、端口(8066)和逻辑库(test_db),无需任何读写分离相关配置(由 MyCat 处理)。

server:port: 8080spring:datasource:# 连接 MyCat(而非直接连接主从库)url: jdbc:mysql://192.168.1.200:8066/test_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=trueusername: mycat_app  # MyCat 配置的应用账号password: MyCat@123456  # MyCat 配置的应用密码driver-class-name: com.mysql.cj.jdbc.Driver# MyBatis-Plus 配置(打印 SQL 日志,方便验证读写分离)
mybatis-plus:mapper-locations: classpath*:mapper/**/*.xmltype-aliases-package: com.example.entityconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印 SQL 日志

3. 应用代码(实体类 + Mapper + Service + Controller)

代码与直接连接 MySQL 完全一致,无需任何修改。

3.1 实体类(User.java)
package com.example.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.time.LocalDateTime;@Data
@TableName("user")  // 对应 MyCat 逻辑表 user(实际是主从库的 test.user 表)
public class User {@TableId(type = IdType.AUTO)private Long id;private String username;private String password;private Integer age;private LocalDateTime createTime;
}
3.2 Mapper 接口(UserMapper.java)
package com.example.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper extends BaseMapper<User> {// 无需额外方法,BaseMapper 提供 CRUD 操作
}
3.3 Service 实现(UserServiceImpl.java)
package com.example.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.service.UserService;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {// 写操作:新增用户(MyCat 自动路由到主库)@Overridepublic boolean addUser(User user) {return save(user);}// 读操作:按 ID 查询(MyCat 自动路由到从库,负载均衡)@Overridepublic User getUserById(Long id) {return getById(id);}// 读操作:查询所有用户(MyCat 自动路由到从库,负载均衡)@Overridepublic List<User> listAllUsers() {return list();}
}
3.4 Controller 接口(UserController.java)
package com.example.controller;import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;// 写接口:新增用户(MyCat 路由到主库)@PostMapping("/add")public String addUser(@RequestBody User user) {boolean success = userService.addUser(user);return success ? "新增用户成功(MyCat 路由到主库)" : "新增失败";}// 读接口:按 ID 查询(MyCat 路由到从库)@GetMapping("/get/{id}")public User getUserById(@PathVariable Long id) {return userService.getUserById(id);}// 读接口:查询所有(MyCat 路由到从库)@GetMapping("/list")public List<User> listAllUsers() {return userService.listAllUsers();}
}

六、Step 5:验证读写分离与从库负载均衡

启动 Spring Boot 应用,通过接口测试工具(Postman、浏览器)调用接口,结合 MyCat 日志和主从库日志验证效果。

1. 验证“写操作路由到主库”

步骤1:调用新增用户接口
  • 请求方式:POST
  • URL:http://192.168.1.300:8080/user/add
  • 请求体:
    {"username": "mycat_test","password": "123456","age": 30
    }
    
步骤2:查看 MyCat 写日志

MyCat 会记录写请求路由到主库的日志:

# 查看 MyCat 业务日志(写请求会显示“route to writeHost”)
tail -f /opt/MyCat-server-2.0.1/logs/mycat.log
步骤3:验证主从数据同步
  • 主库查询:SELECT * FROM test.user; → 能看到 mycat_test
  • 从库1/2查询:SELECT * FROM test.user; → 能看到 mycat_test(主从同步正常)。

2. 验证“读操作路由到从库,负载均衡”

步骤1:多次调用查询接口
  • 请求方式:GET
  • URL:http://192.168.1.300:8080/user/get/1(假设新增用户 ID 为 1)
  • 连续调用 3 次。
步骤2:查看 MyCat 读日志

MyCat 会记录读请求路由到的从库,观察是否轮询:

tail -f /opt/MyCat-server-2.0.1/logs/mycat.log
正常日志示例(轮询负载均衡)
2025-09-10 15:00:00 [INFO] Route read request to readHost: slave1_host (slave1_ds)
2025-09-10 15:00:05 [INFO] Route read request to readHost: slave2_host (slave2_ds)
2025-09-10 15:00:10 [INFO] Route read request to readHost: slave1_host (slave1_ds)
  • 第 1 次请求:路由到 slave1
  • 第 2 次请求:路由到 slave2
  • 第 3 次请求:路由到 slave1
  • 证明从库负载均衡生效(轮询策略)。

3. 验证“从库故障自动剔除”

步骤1:模拟从库1宕机
# 在从库1(192.168.1.101)执行,停止 MySQL 服务
sudo systemctl stop mysql
步骤2:查看 MyCat 数据源状态
# 连接 MyCat 管理端口,查看数据源状态
mysql -u mycat_admin -pAdmin@123456 -h 192.168.1.200 -P 9066
show @@datasource;
输出结果(slave1_ds 状态变为 ERROR)
NAMETYPEHOSTPORTDB_NAMESTATUSACTIVE_CONNIDLE_CONN
master_dsmysql192.168.1.1003306testOK010
slave1_dsmysql192.168.1.1013306testERROR00
slave2_dsmysql192.168.1.1023306testOK010
步骤3:再次调用读接口

此时所有读请求会自动路由到 slave2(故障从库 slave1 被剔除),MyCat 日志显示:

2025-09-10 15:05:00 [INFO] Route read request to readHost: slave2_host (slave2_ds)
2025-09-10 15:05:05 [INFO] Route read request to readHost: slave2_host (slave2_ds)

七、常见问题排查

1. MyCat 启动失败,日志显示“数据源连接失败”

  • 原因:主从库 IP、账号密码错误,或主从库未开放 3306 端口给 MyCat。
  • 解决:
    1. 验证 MyCat 服务器能否 ping 通主从库:ping 192.168.1.100
    2. 验证主从库 3306 端口是否开放:telnet 192.168.1.100 3306
    3. 检查 datasources.xml 中的账号密码是否正确,主从库是否存在该账号。

2. 应用连接 MyCat 成功,但查询不到表

  • 原因:schema.xml 中逻辑表配置错误,或主从库中无该表。
  • 解决:
    1. 检查 schema.xml<table> 标签的 name 是否与主从库表名一致;
    2. 检查主从库是否存在该表:SELECT * FROM test.user;

3. 读请求未负载均衡,始终走一个从库

  • 原因:schema.xmlbalance 参数配置错误(如 balance=0)。
  • 解决:确保 dataHost 标签的 balance="1",重启 MyCat 生效:sudo mycat restart

八、总结

本案例通过 MyCat 中间件实现了 “应用无感知的读写分离与从库负载均衡”,核心优势:

  1. 应用零侵入:应用只需连接 MyCat,无需修改任何代码,后续主从库扩容/切换不影响应用;
  2. 自动负载均衡:读请求按轮询策略分发到从库,故障从库自动剔除;
  3. 可扩展性强:支持分库分表、双主故障切换等高级功能,适合中大型项目。

后续可根据业务需求优化:

  • 配置双主复制 + MyCat 自动切换,提升主库高可用;
  • 增加从库节点,通过 balance=1 自动扩展读性能;
  • 配置从库权重(MyCat 支持 readHostweight 参数),按从库性能分配读请求。
http://www.dtcms.com/a/374638.html

相关文章:

  • 密码到期导致ssh连接失败
  • 学习日记-HTML-day51-9.9
  • 硬件开发2-汇编2(ARMv7-A)
  • 基于mybatis-plus动态数据源实现mysql集群读写分离和从库负载均衡教程(详细案例)
  • Elasticsearch面试精讲 Day 14:数据写入与刷新机制
  • TDengine 选择函数 LAST_ROW() 用户手册
  • Flink 状态管理的核心能力
  • Hive实战(三)
  • git无法拉去远程仓库-connection reset
  • 计算机毕设推荐:基于Hadoop+Spark物联网网络安全数据分析系统 物联网威胁分析系统【源码+文档+调试】
  • 使用 BERT 实现意图理解和实体识别
  • QB/T 4674-2021 汽车内装饰用聚氨酯束状超细纤维合成革检测
  • spark11-sparkSQL 实现wordcount
  • 微硕双N-MOS管WST3392在汽车智能氛围灯系统中的应用
  • 小鹏汽车 vla 算法最新进展和模型结构细节
  • SpringBoot多场景中23种常用注解详解
  • 复杂PDF文档结构化提取全攻略——从OCR到大模型知识库构建
  • PySpark类库和Spark框架的比较
  • Sealos部署Rustdesk服务
  • 数据仓库详解
  • 网络编程---TCP
  • Tomcat商业部署调优(待完成)
  • GitHub SSH 连接超时解决方法 | 网络屏蔽了 GitHub 的 SSH 端口(22)
  • PyTorch自定义模型结构详解:从基础到高级实践
  • PythonSpark综合案例
  • 【Leetcode】高频SQL基础题--626.换座位
  • 字符串-14.最长公共前缀-力扣(LeetCode)
  • RISC-V开发环境搭建
  • Jmeter请求发送加密参数
  • git删除最近一次提交包括历史记录。