基于Docker和Sharding-JDBC在云服务器上实现MySQL主从复制与读写分离
其他 24

在分布式系统架构中,读写分离是一种常见的数据库优化策略。通过将读操作分发至多个从库,不仅可以有效缓解主库压力,还能显著提升系统的整体性能。但对于许多在校的学生党而言,由于缺乏真实的业务场景和实际部署经验,所以在学习这些概念性知识的时候常常觉得抽象难懂。为了更好地理解主从复制与读写分离的底层原理,我们可以借助 云服务器和 Docker 容器 搭建一个简易的 MySQL 主从架构,并结合 Sharding-JDBC 实现读写分离。本篇博客将从环境搭建到配置实现,手把手带你构建这一基础架构,帮助你将理论转化为实践。

主从架构搭建

# 主库配置目录
mkdir -p /usr/local/mysql/master1/conf
mkdir -p /usr/local/mysql/master1/data

# 从库配置目录
mkdir -p /usr/local/mysql/slave1/conf
mkdir -p /usr/local/mysql/slave1/data

首先我们需要在上述目录下手动创建配置目录,一会我们创建docker就将这个路径映射到容器内的Mysql配置路径下

接下来使用编辑器进行手动配置

主数据库配置

主库配置/usr/local/mysql/master1/conf/my.cnf

[mysqld]
datadir = /var/lib/mysql
character-set-server = utf8
lower-case-table-names = 1
server-id = 1
# 启用二进制日志 
log-bin = mysql-bin
# 设置logbin格式 
binlog_format = STATEMENT

从数据库配置

从库配置 /usr/local/mysql/slave1/conf/my.cnf

[mysqld]
datadir = /var/lib/mysql
character-set-server = utf8
lower-case-table-names = 1
server-id = 2
# 启用中继日志 
relay-log = mysql-relay

这里要注意server-id是一个关键的配置属性

上述路径等参数根据实际进行手动替换

授权配置目录权限

chmod -R 777 /usr/local/mysql

启动docker容器

这里默认大家已经下载好了Mysql的镜像,这一部分比较基础不过多赘述

主库容器

docker run --name=mysql-master \
--privileged=true \
-p 8808:3306 \
-v /usr/local/mysql/master1/data:/var/lib/mysql \
-v /usr/local/mysql/master1/conf/my.cnf:/etc/mysql/my.cnf \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:8.0 --lower_case_table_names=1

从库容器

docker run --name=mysql-slave \
--privileged=true \
-p 8809:3306 \
-v /usr/local/mysql/slave1/data:/var/lib/mysql \
-v /usr/local/mysql/slave1/conf/my.cnf:/etc/mysql/my.cnf \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:8.0 --lower_case_table_names=1

这里我们分别在云服务器的8808和8809两个端口映射到容器内部的3306端口(Mysql服务的默认端口)

此部分启动容器的指令大差不差,如果不熟悉docker的朋友直接按照我的配置即可

之后我们在代码中通过这两个端口访问主从数据库

配置主从复制机制

我们通过指令进入容器内部并对mysql进行配置

主库配置

# 进入主库容器
docker exec -it mysql-master bash
mysql -uroot -p
# 输入密码进入mysql

# 登录成功后可以看到如下的信息
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.xx MySQL Community Server - GPL

mysql>

# 然后执行如下指令创建同步账号
CREATE USER 'repl_user'@'%' IDENTIFIED WITH mysql_native_password BY 'your_password';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%';
FLUSH PRIVILEGES;

# 查看binlog位置(用于从库配置)
SHOW MASTER STATUS;

这里查看binlog的位置之后要记住这个参数,在从库中配置的时候需要填写

File: mysql-bin.000001
Position: 157

输出结果可能如上

从库配置

# 首先同样的操作进入mysql,这里就不重复说明了
# 停止 slave
STOP SLAVE;
RESET SLAVE;

-- 设置主库连接参数(请替换主库IP、账号和位置)
CHANGE MASTER TO
  MASTER_HOST='主库IP',
  MASTER_PORT=8808,
  MASTER_USER='repl_user',
  MASTER_PASSWORD='your_password',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=157;

-- 启动同步
START SLAVE;

-- 查看同步状态
SHOW SLAVE STATUS\G

这里注意MASTER_LOG_FIL和MASTER_LOG_POS就是主库中查询到的结果

这里查看同步状态会返回一条行数据

如果你们看到如下两个字段显示Yes就说明同步成功了

使用Sharding-JDBC 实现读写分离

<dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core</artifactId>
            <version>5.3.2</version>
 </dependency>

首先你需要在maven中导入坐标,具体的版本根据实际需要进行修改

sharding-jdbc.yaml示例配置

我们需要在resources目录下创建一个jdbc的.yaml配置文件

dataSources:
  master:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://<主库IP>:8808/your_db?useUnicode=true&characterEncoding=utf8
    username: root
    password: root

  slave:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://<主库IP>:8809/your_db?useUnicode=true&characterEncoding=utf8
    username: root
    password: root

rules:
  - !READWRITE_SPLITTING
    dataSources:
      ds:
        staticStrategy:
          writeDataSourceName: master
          readDataSourceNames:
            - slave

  - !SINGLE
    defaultDataSource: ds

  - !SHARDING
    tables:
      t_user:
        actualDataNodes: ds.t_user_${0..99}
        tableStrategy:
          standard:
            shardingColumn: user_id
            shardingAlgorithmName: t_user-inline

    shardingAlgorithms:
      t_user-inline:
        type: INLINE
        props:
          algorithm-expression: t_user_${user_id % 100}

props:
  sql-show: true

这里进行了简单地配置,比如写走的是master数据库

读操作走的是从数据库,在readDataSourceNames下可以设置多个从数据库,具体分到哪个数据库有不同的策略(比如负载均衡)

我的案例中还进行了分表设计,将user分成0-99的100张表,通过user_id % 100决定分配至哪一张表

SpringBoot配置示例

spring:
  datasource:
    # 使用 ShardingSphere 的驱动代理数据库操作
    driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
    # 加载自定义的 ShardingSphere 配置文件,请根据实际情况替换文件名
    url: jdbc:shardingsphere:classpath:your-sharding-config.yaml
    # 连接池配置:建议使用高性能的 HikariCP,可根据实际项目选择其他实现
    hikari:
      pool-name: user-pool
      minimum-idle: 10
      maximum-pool-size: 50
      connection-init-sql: SELECT 1
      connection-timeout: 3000
      max-lifetime: 60000
      idle-timeout: 60000

说明:
本文中示例使用了 HikariCP 作为连接池(HikariDataSource),ShardingSphere 的配置文件名为 sharding-jdbc.yaml如果你在实际项目中使用的是不同的连接池、数据库名或配置文件名,请将其根据自己的需求进行替换。关键是理解其作用而非死记路径

验证

我们可以连接主库手动在一张表中插入一条数据

插入后检查从库中相同表中是否有数据同步,如果数据出现说明同步成功

注意事项

  • MySQL 主从同步对网络要求较高,延迟大时可能造成读写不一致;

  • binlog 格式推荐为 ROW 模式(当前示例为 STATEMENT,实际项目可根据需求修改);

  • server-id 必须全局唯一;

  • 若主库重启后binlog变化,需重新配置从库位点。

基于Docker和Sharding-JDBC在云服务器上实现MySQL主从复制与读写分离
https://talk2zbw.com/archives/master-slave
作者
zbw
发布于
更新于
许可