本文目录导读:

对于PHP项目配置数据库主从(读写分离),主要分为应用层配置和数据库层配置两部分,以下是一个逐步的完整解决方案,适用于大多数PHP框架(如Laravel、ThinkPHP)以及原生PHP。
数据库主从环境准备(MySQL)
主库配置(Master)
编辑 MySQL 配置文件 /etc/my.cnf(或 /etc/mysql/mysql.conf.d/mysqld.cnf):
[mysqld] server-id = 1 log_bin = /var/log/mysql/mysql-bin.log binlog-do-db = your_database_name # 可选:指定要同步的数据库 binlog-ignore-db = mysql # 忽略系统库
从库配置(Slave)
[mysqld] server-id = 2 relay-log = /var/log/mysql/mysql-relay-bin.log read_only = 1
主从同步设置
在主库创建同步用户:
CREATE USER 'repl'@'%' IDENTIFIED BY 'password'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES;
查看主库状态:
SHOW MASTER STATUS;
在从库执行同步:
CHANGE MASTER TO MASTER_HOST='主库IP', MASTER_USER='repl', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000001', -- 从SHOW MASTER STATUS获得 MASTER_LOG_POS=154; -- 从SHOW MASTER STATUS获得 START SLAVE; -- 检查状态(Slave_IO_Running和Slave_SQL_Running均为Yes) SHOW SLAVE STATUS\G
PHP应用层配置(读写分离)
原生PHP(使用PDO)
<?php
class DatabaseManager {
private $master;
private $slave;
public function __construct() {
// 主库连接
$this->master = new PDO(
'mysql:host=主库IP;dbname=your_db;charset=utf8mb4',
'username',
'password'
);
// 从库连接(多个从库可随机选择)
$slaveConfig = [
['host' => '从库1_IP', 'port' => 3306],
['host' => '从库2_IP', 'port' => 3306]
];
$selected = $slaveConfig[array_rand($slaveConfig)];
$this->slave = new PDO(
"mysql:host={$selected['host']};port={$selected['port']};dbname=your_db;charset=utf8mb4",
'username',
'password'
);
}
// 写操作使用主库
public function write($sql, $params = []) {
return $this->master->prepare($sql)->execute($params);
}
// 读操作使用从库
public function read($sql, $params = []) {
$stmt = $this->slave->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
Laravel框架(内置支持)
在 .env 文件中配置:
DB_CONNECTION=mysql DB_HOST=主库IP DB_PORT=3306 DB_DATABASE=your_db DB_USERNAME=root DB_PASSWORD=密码 # 从库配置(支持多个) DB_SLAVE1_HOST=从库1_IP DB_SLAVE1_PORT=3306 DB_SLAVE2_HOST=从库2_IP
在 config/database.php 中完善:
'mysql' => [
'driver' => 'mysql',
'read' => [
'host' => [
env('DB_SLAVE1_HOST', '127.0.0.1'),
env('DB_SLAVE2_HOST', '127.0.0.1'),
],
],
'write' => [
'host' => [
env('DB_HOST', '127.0.0.1'),
],
],
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
// 其他配置...
],
Laravel会自动将读操作分发到从库,写操作发往主库。
ThinkPHP框架
在 config/database.php 中:
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => '主库IP', // 主库
'database' => 'your_db',
'username' => 'root',
'password' => '密码',
'hostport' => '3306',
// 从库配置
'slave' => [
[
'hostname' => '从库1_IP',
'hostport' => '3306',
],
[
'hostname' => '从库2_IP',
'hostport' => '3306',
],
],
'rw_separate' => true, // 启用读写分离
],
],
使用数据库中间件(推荐生产环境)
- ProxySQL:透明代理,应用层只需连接ProxySQL,自动分发读写请求。
- MySQL Router:官方方案,支持故障转移。
安装ProxySQL后配置查询规则:
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup) VALUES (1, 1, '^SELECT', 1); -- SELECT到从库组 INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup) VALUES (2, 1, '.*', 2); -- 其他到主库组 LOAD MYSQL QUERY RULES TO RUN;
关键注意事项
| 问题 | 解决方案 |
|---|---|
| 主从延迟 | 读操作判断延迟:查询从库 Seconds_Behind_Master,延迟大时切换到主库读取 |
| 事务一致性 | 事务内读写必须使用主库(例如开启事务后强制走主库) |
| 负载均衡 | 从库间使用轮询或最少连接算法 |
| 故障转移 | 监控脚本自动切换从库为新主库,或使用MySQL Group Replication |
| 连接池 | 使用长连接池(Swoole/Workerman)避免频繁创建连接 |
高级实现:读写分离过滤器(原生PHP示例)
<?php
class DatabaseRouter {
private $connections = [];
private $useMaster = false;
public function __construct() {
// 初始化主从连接...
}
public function query($sql, $params = []) {
$isSelect = stripos(trim($sql), 'SELECT') === 0;
// 事务中强制走主库
if ($this->inTransaction || !$isSelect) {
return $this->executeOnMaster($sql, $params);
}
// 检查从库延迟
if ($this->getSlaveSecondsBehind() > 10) {
return $this->executeOnMaster($sql, $params);
}
return $this->executeOnSlave($sql, $params);
}
// 记录当前操作需要强制读主库(例如用户资料更新后读取)
public function forceMaster() {
$this->useMaster = true;
}
}
总结推荐方案
- 小型项目:使用框架内置读写分离(Laravel/ThinkPHP)。
- 中型项目:PHP代码层使用路由逻辑 + 数据库连接池。
- 大型项目/高并发:使用ProxySQL或Mycat中间件,结合LVS负载均衡。
最后测试主从同步状态,确认应用层读写分离生效。