PHP项目如何配置数据库主从?

wen PHP项目 10

本文目录导读:

PHP项目如何配置数据库主从?

  1. 数据库主从环境准备(MySQL)
  2. PHP应用层配置(读写分离)
  3. 关键注意事项
  4. 高级实现:读写分离过滤器(原生PHP示例)
  5. 总结推荐方案

对于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负载均衡。

最后测试主从同步状态,确认应用层读写分离生效。

抱歉,评论功能暂时关闭!