网站数据库读写分离提升性能:网站数据库读写分离
为什么要做数据库读写分离
当网站用户量增长,单台数据库服务器既要处理写入(新增、修改、删除)又要处理读取(查询),很容易因为CPU、内存或磁盘I/O瓶颈导致响应变慢。读写分离通过将写入操作交给主库,读取操作分发给一个或多个从库,让主库专注于写入,从库分担读压力,整体吞吐量大幅提升。
这个方案不需要昂贵的硬件,只靠软件配置就能实现,是中小型网站性价比最高的优化手段之一。
搭建主从复制环境
实现读写分离的第一步是让主库和从库之间的数据实时同步,也就是MySQL主从复制。
1. 准备条件
- 两台服务器(或云主机),操作系统不限(本文以CentOS 7 + MySQL 8.0为例)。
- 主库IP:192.168.1.10,从库IP:192.168.1.20。
- 确保两台服务器之间网络连通(互相能ping通,防火墙放开3306端口)。
- MySQL版本尽量一致,最好都是8.0系列。
2. 配置主库
登录主库服务器,编辑MySQL配置文件(通常位于/etc/my.cnf或/etc/mysql/my.cnf),在[mysqld]段添加以下内容:
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
保存后重启MySQL服务:
systemctl restart mysqld
登录MySQL(mysql -uroot -p),创建一个专门用于复制的用户,并授予权限:
CREATE USER 'repl'@'192.168.1.%' IDENTIFIED BY 'YourPass123';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%';
FLUSH PRIVILEGES;
查看主库当前状态,记录下File和Position,后面配置从库时要用:
SHOW MASTER STATUS;
输出示例:
+------------------+----------+
| File | Position |
+------------------+----------+
| mysql-bin.000001 | 877 |
+------------------+----------+
3. 配置从库
登录从库服务器,修改配置文件,在[mysqld]段添加:
[mysqld]
server-id = 2
# 以下两行可选,用于设置只读(防止误写入从库)
read-only = 1
super-read-only = 1
重启从库MySQL:
systemctl restart mysqld
登录从库MySQL,执行以下命令连接到主库并开始同步(请根据实际IP和刚才记录的File、Position替换):
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='192.168.1.10',
SOURCE_USER='repl',
SOURCE_PASSWORD='YourPass123',
SOURCE_LOG_FILE='mysql-bin.000001',
SOURCE_LOG_POS=877;
*注意:MySQL 8.0.23之前版本使用MASTER_HOST、MASTER_USER等旧版关键字,用法相同。
*
启动复制:
START REPLICA;
查看复制状态是否正常:
SHOW REPLICA STATUS\G
重点看这两项:
Slave_IO_Running: YesSlave_SQL_Running: Yes
如果都显示Yes,说明主从复制已建立。
配置应用层读写分离
主从复制只是数据同步,网站还要知道写请求走主库、读请求走从库。
这一步通常在应用程序里配置,不需要改动MySQL自身。
以PHP的Laravel框架为例,修改.env文件:
DB_CONNECTION=mysql
DB_HOST=192.168.1.10 # 主库IP
DB_PORT=3306
DB_DATABASE=yourdb
DB_USERNAME=root
DB_PASSWORD=password
# 读写分离配置
DB_READ_HOST=192.168.1.20 # 从库IP
DB_READ_DATABASE=yourdb
DB_READ_USERNAME=root
DB_READ_PASSWORD=password
在config/database.php中配置:
'mysql' => [
'driver' => 'mysql',
'write' => [
'host' => env('DB_HOST', '192.168.1.10'),
],
'read' => [
'host' => env('DB_READ_HOST', '192.168.1.20'),
],
'database' => env('DB_DATABASE', 'yourdb'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
// 其他配置...
],
其他框架(ThinkPHP、Symfony、Yii)也有类似写法。
如果你用的是宝塔面板,可以在“数据库”菜单中直接添加主从复制,然后在网站管理的“数据库配置”里手动修改数据库连接代码。
如果不想改代码,也可以使用MySQL中间件如ProxySQL、MyCat,这类工具可以透明地在前端拦截SQL,按规则转发。
但对于零基础用户,修改应用配置是最快、风险最低的方式。
常见坑位与排查方法
1. 主从延迟导致读不到刚写的数据
用户提交表单后立即去查,可能查不到(因为从库还没同步过来)。解决:对实时性要求高的场景(如支付结果),强制读主库;
或者对从库设置较短的同步间隔。
2. 从库IO线程或SQL线程报错
运行SHOW REPLICA STATUS\G,查看Last_IO_Error或Last_SQL_Error。
常见原因:
- 防火墙没放通3306端口。
- 复制用户密码错误或IP范围不对。
- binlog格式不匹配(建议统一用ROW)。
- 主从库的
sql_mode不一致导致某些SQL执行失败。
3. 误写入从库
从库默认允许写入(除非你加了read-only=1)。
即使开启只读,super权限用户还是能写。
建议:始终在应用层明确区分读/写连接,不要依赖数据库层限制。
验证读写分离是否生效
方法一:检查主从复制状态
在从库上执行:
SHOW REPLICA STATUS\G
确认Slave_IO_Running和Slave_SQL_Running均为Yes。
方法二:追踪SQL去向
在应用服务器上临时开启MySQL通用查询日志(生产环境慎用),或者使用抓包工具tcpdump。
例如,在从库服务器上执行:
tcpdump -i any port 3306 -X
然后用浏览器访问网站的一个查询页面,观察从库是否收到查询请求。
方法三:应用层面的监控
在代码中输出当前使用的数据库连接IP(如DB::connection()->getPdo()->query('SELECT @@hostname')),运行时刷新页面,查看打印的IP。
如果读请求显示的是从库IP,写请求显示主库IP,就说明分离成功。
结尾
完成以上配置后,你的网站数据库就实现了读写分离,压力被均匀分配。
记得先在测试环境跑通,确保主从复制稳定后再上生产。
遇到同步异常时,优先检查网络、权限和binlog配置。
读写分离只是第一步,后续还可以结合缓存(Redis)、分库分表等方案继续优化。
如果你在配置中碰到了问题,欢迎留言交流。