在分布式系统架构中,读写分离是一种常见的数据库优化策略。通过将读操作分发至多个从库,不仅可以有效缓解主库压力,还能显著提升系统的整体性能。但对于许多在校的学生党而言,由于缺乏真实的业务场景和实际部署经验,所以在学习这些概念性知识的时候常常觉得抽象难懂。为了更好地理解主从复制与读写分离的底层原理,我们可以借助 云服务器和 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变化,需重新配置从库位点。