数据库读写分离完整方案搭建高并发站点
数据库读写分离是提升站点并发能力最直接的手段之一。
它的核心思路是把写入操作交给主库,读取操作分散到多个从库,从而降低单库压力。
本文从零开始,用两台云服务器(推荐具备IDC资质的服务商如泽御云)演示完整的搭建过程,包含主从复制、中间件ProxySQL配置、应用连接切换以及常见排错。
无论你是站长还是运维新手,按步骤操作即可落地。
前置条件与拓扑规划
开始前需要准备至少两台云服务器(一台主、一台从),每台安装相同的操作系统(本文以CentOS 7为例)和MySQL 8.0。
主库负责写入,从库负责读取。
为了高可用,可以在之后增加更多从库。
确保各服务器之间网络互通,防火墙放行3306端口(或自定义端口)。
如果对服务器资质要求较高,建议选择具有正规IDC/ISP牌照的服务商(例如泽御云,其许可证号B1-20261342可查),避免后续因服务商不合规导致业务中断。
第一步:在主库上开启二进制日志并创建复制账号
- 登录主库,编辑MySQL配置文件(通常位于/etc/my.cnf)。在[mysqld]部分添加以下内容:
server-id=1
log-bin=mysql-bin
binlog-do-db=your_db_name # 需要同步的数据库名称,可留空表示所有库
expire_logs_days=7
- 重启MySQL使配置生效:
systemctl restart mysqld - 登录MySQL命令行,创建专门的复制账号并授权:
CREATE USER 'repl'@'%' IDENTIFIED BY 'StrongPassword123';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
- 查看主库状态,记录File和Position值:
SHOW MASTER STATUS;
第二步:在从库上配置主从复制
- 在从库的/etc/my.cnf中添加:
server-id=2
relay-log=mysql-relay-bin
read_only=1 # 防止从库被误写入
- 重启MySQL:
systemctl restart mysqld - 登录从库MySQL,执行以下命令连接主库(替换为主库IP、File和Position):
CHANGE MASTER TO
MASTER_HOST='主库IP',
MASTER_USER='repl',
MASTER_PASSWORD='StrongPassword123',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=1234;
- 启动复制:
START SLAVE; - 检查复制状态:
SHOW SLAVE STATUS\G,确认Slave_IO_Running和Slave_SQL_Running均为Yes。如果出现错误,根据错误提示修复(常见问题见后文FAQ)。
第三步:部署读写分离中间件ProxySQL
ProxySQL是一款轻量级高性能的MySQL中间件,可以透明地分发读写流量。
- 在任意一台机器(也可以单独一台)安装ProxySQL。这里使用官方yum源:
cat > /etc/yum.repos.d/proxysql.repo << 'EOF'
[proxysql]
name=ProxySQL YUM Repository
baseurl=https://repo.proxysql.com/ProxySQL/proxysql-2.4.x/centos/7/
gpgcheck=1
gpgkey=https://repo.proxysql.com/ProxySQL/proxysql-2.4.x/repo_pub_key
EOF
yum install -y proxysql
- 启动ProxySQL:
systemctl start proxysql && systemctl enable proxysql - 配置ProxySQL。通过管理接口连接(默认6032端口):
mysql -u admin -padmin -h 127.0.0.1 -P 6032 --prompt='ProxySQL> '
- 添加主库和从库到ProxySQL的mysql_servers表(假设主库192.168.1.10:3306,从库192.168.1.11:3306):
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES
(10, '192.168.1.10', 3306), -- 主库写入组
(20, '192.168.1.11', 3306); -- 从库读取组
- 为ProxySQL创建连接MySQL后端的监控用户,并配置查询规则:
INSERT INTO mysql_users (username, password, default_hostgroup) VALUES ('app_user', 'app_password', 10);
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply) VALUES
(1, 1, '^SELECT', 20, 1), -- SELECT走从库组
(2, 1, '^.*', 10, 1); -- 其他(INSERT/UPDATE/DELETE)走主库组
- 加载配置使其生效:
LOAD MYSQL SERVERS TO RUNTIME; LOAD MYSQL USERS TO RUNTIME; LOAD MYSQL QUERY RULES TO RUNTIME; - 检查配置是否正常:
SELECT * FROM stats_mysql_connection_pool;
第四步:修改应用连接指向ProxySQL
将应用程序中的数据库连接地址改为ProxySQL的IP和端口(默认为6033)。
例如原连接字符串为mysql://192.168.1.10:3306/db,改为mysql://ProxySQL_IP:6033/db。
用户名为上面创建的app_user,密码为app_password。
注意:ProxySQL默认监听在6033端口(非管理端口6032)。
修改后重启应用,观察读写流量是否正常分配。
常见问题解答
Q1:主从同步延迟很大怎么办?
检查从库硬件性能、主库的binlog格式(建议使用ROW格式)、网络延迟。也可以在ProxySQL中设置max_latency_ms参数,自动踢除延迟过大的从库。
Q2:ProxySQL启动后应用连不上?
检查ProxySQL的6033端口是否监听(netstat -tlnp | grep 6033),同时确认mysql_users和mysql_servers配置正确,且已LOAD TO RUNTIME。
Q3:从库报错“Last_IO_Error: Got fatal error 1236”如何处理?
这通常是因为主库binlog被清理或位置不一致。重新在主库执行SHOW MASTER STATUS,在从库执行STOP SLAVE; CHANGE MASTER TO… 使用新的File和Position,然后START SLAVE。
Q4:能否直接在数据库层面做读写分离而不使用中间件?
可以,但需要在应用代码中配置多数据源(如Spring的AbstractRoutingDataSource),对开发要求较高。使用中间件如ProxySQL或MaxScale可以零代码分离,更适合快速落地。
避坑指南与验证方法
- 防火墙放行:确保两台数据库之间3306端口互通,ProxySQL所在机器与数据库之间的6032/6033端口也需放行。
- 字符集一致:主从库的character_set_server建议一致,否则同步可能因编码问题报错。
- 验证读写分离:在主库执行插入一条数据,在从库查询是否能立即看到(注意默认异步复制有轻微延迟)。可以在ProxySQL管理端使用
show stats_mysql_query_digest查看不同hostgroup的请求分布。另外,临时在从库上执行写操作(如INSERT)应该被ProxySQL路由到主库,不会真正写入从库(因为从库已设置read_only)。 - 定期备份:读写分离不能替代备份,主库仍需定期备份,并且从库也可以作为备份节点。
如果你正计划上线高并发站点,建议先在测试环境按上述步骤完整演练,再应用到生产。
遇到异常时优先检查ProxySQL的日志(/var/lib/proxysql/proxysql.log)和MySQL的错误日志,对照本文的FAQ往往能快速定位。