尚拙

一个分享技术、学习成长的个人博客网站

0%

mysql主从数据库配置指南

在高并发的 Web 应用场景中,单一数据库服务器往往难以承受巨大的读写压力。读写分离 是一种经典的数据库架构优化方案:主库负责“写”操作,从库负责“读”操作,以此分担负载并提升系统可用性。

适用场景:

  • 操作系统:Linux(CentOS / Ubuntu)
  • 数据库版本:MySQL 8.0
  • 服务端框架:Django
  • 目标:实现读写分离、提高系统性能与可用性

一、为什么要使用 MySQL 主从架构?

在生产环境中,随着业务增长,数据库会逐渐成为性能瓶颈。常见问题:

  • 查询压力过大
  • 并发请求高
  • 单点故障风险

主从架构(Master-Slave)可以解决:

问题 解决方式
读压力大 读写分离,查询走从库
数据安全 从库可做备份
高可用 主库异常可切换
扩展能力 可扩展多个从库

二、主从复制原理

MySQL 主从复制基于 binlog(二进制日志)

流程如下:

  1. 主库写入数据
  2. 主库记录 binlog
  3. 从库 IO 线程拉取主库 binlog
  4. 从库 SQL 线程执行 relay log
  5. 从库数据同步

结构示意:


Client

Master(写 + binlog)

Slave(relay log → 执行)


三、环境准备

1️⃣ 安装 MySQL 8.0(主从都需安装)

Ubuntu:

sudo apt update
sudo apt install mysql-server
````

CentOS:

```bash
yum install mysql-server

确认版本:

mysql -V

四、配置主库(Master)

假设:

  • 主库IP:192.168.1.10
  • 从库IP:192.168.1.20

1️⃣ 修改主库配置文件

编辑:

vim /etc/my.cnf
# 或
vim /etc/mysql/mysql.conf.d/mysqld.cnf

在[mysqld]节点下添加以下配置(原有配置保留,新增如下内容):

[mysqld]
# 主库唯一标识(1-2^32-1,不可与从库重复)
server-id = 1
# 开启二进制日志(核心,记录主库所有写操作,文件名前缀为mysql-bin)
log-bin = mysql-bin
# 二进制日志格式(mixed:混合模式,推荐,兼顾性能与兼容性)
binlog_format = mixed
# 指定需要同步的数据库(若不指定,同步所有数据库;适配Django项目,填写项目所用数据库)
binlog-do-db = django_project_db
# 指定不需要同步的数据库(可选)
binlog-ignore-db = mysql
binlog-ignore-db = information_schema
# 禁止自动删除二进制日志(避免同步中断,可按需设置过期时间)
expire_logs_days = 30
# 主库写入二进制日志后,无需等待从库确认(提升主库性能,按需调整)
sync_binlog = 1

说明:

参数 说明
server-id 主从必须唯一
log-bin 开启 binlog
binlog_format 建议 ROW
binlog-do-db 指定需要同步的数据库(若不指定,同步所有数据库)
binlog-ignore-db 指定不需要同步的数据库(可选)

重启MySQL,使配置生效:

systemctl restart mysql

2️⃣ 创建复制账号

登录 MySQL:

CREATE USER 'repl_user'@'%' IDENTIFIED BY 'StrongP@ss123!';  -- 生产环境替换为强密码
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%';
FLUSH PRIVILEGES;

安全提示'%'允许任意IP连接,生产环境应替换为从库IP(如'repl_user'@'192.168.1.100')。


3️⃣ 查看主库状态

SHOW MASTER STATUS;

输出示例

+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 | 154 | | |
+------------------+----------+--------------+------------------+

**记录FilePosition**(后续配置从库必用)。

例如:

mysql-bin.000003
120

⚠️ 注意:此时不要关闭MySQL终端,也不要在主库执行任何写操作(如增删改),否则Position会变化,影响后续从库配置。

4️⃣ 导出主库现有数据(确保数据一致性)

# 导出所有数据库(生产环境建议只导出应用数据库)
mysqldump -u root -p --all-databases --single-transaction > /tmp/full_backup.sql

关键参数--single-transaction避免锁表,保证一致性。

也可以通过其他mysql远程工具导出某个数据库的数据(比如Navicat For MYSQL的转储SQL文件功能)。


五、配置从库(Slave)


1️⃣ 修改从库配置文件

[mysqld]
# 从库唯一标识(不可与主库重复,此处设为2)
server-id = 2
# 关闭从库二进制日志(若从库无需作为其他从库的主库,可关闭;若需级联同步,可开启)
log-bin = OFF
# 中继日志(核心,用于接收主库的二进制日志,再解析执行)
relay-log = mysql-relay-bin
# 从库只读(禁止手动写操作,避免主从数据不一致,root用户不受限制)
read-only = 1
# 忽略同步的数据库(与主库保持一致,可选)
replicate-ignore-db = mysql
replicate-ignore-db = information_schema
# 指定需要同步的数据库(与主库保持一致,仅同步Django项目数据库)
replicate-do-db = django_project_db
# 忽略mysql库下的user表同步(格式:库名.表名)
replicate-ignore-table =history.user
# 注意:若主从复制是ROW格式(MySQL8.0默认),需加下面一行(避免表名大小写/通配符问题)
replicate-wild-ignore-table = history.user

如果某个表数据不走从库,可以通过replicate-ignore-table =history.user忽略该表同步

重启:

systemctl restart mysql

2️⃣ 导入主库数据

导入所有数据

mysql -uroot -p < dump.sql

导入某个数据库

mysql -u 用户名 -p new_database < /path/to/backup.sql

3️⃣ 初始化主从关系

登录从库:

mysql -uroot -p

执行:

CHANGE MASTER TO
MASTER_HOST='主库IP',
MASTER_USER='repl',
MASTER_PASSWORD='repl123456',
MASTER_LOG_FILE='mysql-bin.000003',
MASTER_LOG_POS=157;

启动复制:

START SLAVE;

4️⃣ 查看同步状态

SHOW SLAVE STATUS\G

重点关注:

Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0

说明:

字段 说明
Slave_IO_Running IO线程是否正常
Slave_SQL_Running SQL线程是否正常
Seconds_Behind_Master 延迟时间

若出现错误:

STOP SLAVE;
RESET SLAVE;

六、Django 配置读写分离


1️⃣ 配置多个数据库

编辑 settings.py

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'your_db',
'USER': 'root',
'PASSWORD': 'password',
'HOST': '主库IP',
'PORT': '3306',
},
'slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'your_db',
'USER': 'root',
'PASSWORD': 'password',
'HOST': '从库IP',
'PORT': '3306',
}
}

2️⃣ 创建数据库路由

新建:

project/utils/db_router.py
from .slave_switch import SLAVE_ENABLED

class MasterSlaveDBRouter:
READ_REPLICA_MODELS = {
'history_app.FigureList',
'history_app.FigureEvent',
'history_app.Wuqiannian',
'history_app.WuqiannianDetail',
'history_app.War',
'history_app.TodayOnHistoryList',
'history_app.HistoryCollectionList'
}

def db_for_read(self, model, **hints):
if not SLAVE_ENABLED:
return 'default'

model_key = f"{model._meta.app_label}.{model.__name__}"

if model_key in self.READ_REPLICA_MODELS:
if self.slave_available():
return 'slave'

return 'default'

def db_for_write(self, model, **hints):
return 'default'

def allow_relation(self, obj1, obj2, **hints):
return True

def allow_migrate(self, db, app_label, model_name=None, **hints):
return db == 'default'

def slave_available(self):
"""
判断从库是否可用
"""
try:
from django.db import connections
conn = connections['slave']
conn.ensure_connection()
return True
except Exception:
return False

其中SLAVE_ENABLED为是否开启从库开关;READ_REPLICA_MODELS为配置哪些模型走从库。


3️⃣ 注册路由

settings.py中添加:

DATABASE_ROUTERS = ['your_app.routers.DatabaseRouter']  # 替换your_app为实际应用名

重启 Django 服务生效。

4️⃣ 测试Django项目读写分离

验证读写路由

# 写操作(应走主库)
obj = YourModel.objects.create(name="Test")

# 读操作(应走从库)
objs = YourModel.objects.all() # 通过日志确认查询在从库执行

检查实际路由(在Django shell中):

from django.db import connections
print(connections['default'].settings_dict['HOST']) # 主库IP
print(connections['replica'].settings_dict['HOST']) # 从库IP

输出示例

192.168.1.50
192.168.1.100

八、常见问题排查

配置过程中,最常见的问题是“从库同步失败”(Slave_IO_Running或Slave_SQL_Running为No),以下是高频问题及解决方案:

1️⃣ Slave_IO_Running: No

原因:从库无法连接主库,或主库同步用户权限不足、二进制日志文件名/位置错误。

# 解决方案:
1. 检查主从库网络连通性(从库ping主库IP,确保能ping通)
ping 192.168.1.100

2. 检查主库同步用户权限(主库登录MySQL,查看权限)
mysql -u root -p'MySql@123456'
show grants for 'slave_user'@'192.168.1.101';
# 若权限不足,重新授权(参考主库2.4步骤)

3. 检查从库配置的主库二进制日志文件名/位置(与主库show master status输出一致)
# 从库登录MySQL,查看配置
show slave status\G;
# 对比MASTER_LOG_FILE和MASTER_LOG_POS与主库是否一致,不一致则重新配置(参考从库3.4步骤)
stop slave;
CHANGE MASTER TO ...; # 重新填写正确的File和Position
start slave;

2️⃣ Slave_SQL_Running: No

原因:主从数据不一致(从库缺少主库的某些表/数据)、从库手动执行了写操作、SQL语句不兼容。

# 解决方案:
1. 重新同步主从数据(最常用,适合数据量不大的场景)
# 主库重新导出数据,传输到从库,重新导入
mysqldump -u root -p'MySql@123456' --databases django_project_db > django_project_db.sql
scp django_project_db.sql root@192.168.1.101:/root/

# 从库重新导入数据,重启同步
mysql -u root -p'MySql@123456'
source /root/django_project_db.sql;
stop slave;
CHANGE MASTER TO ...; # 重新配置主库信息(确保File和Position正确)
start slave;

2. 若从库手动执行了写操作,删除从库新增的数据,重启同步
# 从库登录MySQL,删除手动新增的数据,然后:
stop slave;
start slave;

3️⃣ Django项目无法连接主从库

原因:MySQL用户远程登录权限不足、防火墙未开放3306端口、数据库配置参数错误。

# 解决方案:
1. 检查MySQL用户远程权限(主从库均需配置,参考1.2.2步骤,允许Django项目所在IP连接)
GRANT ALL PRIVILEGES ON *.* TO 'root'@'Django项目IP' IDENTIFIED BY 'MySql@123456' WITH GRANT OPTION;
FLUSH PRIVILEGES;

2. 检查防火墙(确保3306端口开放,参考1.2.3步骤)

3. 检查Django settings.py中的数据库配置(HOST、PORT、USER、PASSWORD、NAME是否正确)

5.4 主库二进制日志丢失/过期

原因:主库配置了expire_logs_days,二进制日志过期删除,导致从库同步中断。

# 解决方案:
1. 调整主库my.cnf中的expire_logs_days参数,延长日志保存时间(如30天)
expire_logs_days = 30

2. 重新同步主从数据