本文目录导读:

在PHP项目中实现MySQL主从同步,主要涉及数据库层面的配置和应用层面的读写分离,以下是完整的实现步骤:
数据库主从配置
主服务器配置 (Master)
# /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 # 忽略的数据库
创建复制用户:
CREATE USER 'replication'@'%' IDENTIFIED BY 'password'; GRANT REPLICATION SLAVE ON *.* TO 'replication'@'%'; FLUSH PRIVILEGES;
查看主服务器状态:
SHOW MASTER STATUS; -- 记录 File 和 Position 值
从服务器配置 (Slave)
# /etc/mysql/mysql.conf.d/mysqld.cnf [mysqld] server-id = 2 relay_log = /var/log/mysql/mysql-relay-bin.log log_bin = /var/log/mysql/mysql-bin.log
配置从服务器连接主服务器:
CHANGE MASTER TO MASTER_HOST='master_ip', MASTER_USER='replication', MASTER_PASSWORD='password', MASTER_LOG_FILE='mysql-bin.000001', -- 从主服务器获取 MASTER_LOG_POS=123; -- 从主服务器获取 START SLAVE; -- 检查状态 SHOW SLAVE STATUS\G -- 确保 Slave_IO_Running 和 Slave_SQL_Running 都为 Yes
PHP应用层读写分离实现
基础连接配置
<?php
// config/database.php
return [
'master' => [
'host' => '192.168.1.100',
'port' => 3306,
'database' => 'your_database',
'username' => 'write_user',
'password' => 'write_password',
'charset' => 'utf8mb4',
],
'slaves' => [
[
'host' => '192.168.1.101',
'port' => 3306,
'database' => 'your_database',
'username' => 'read_user',
'password' => 'read_password',
'charset' => 'utf8mb4',
],
[
'host' => '192.168.1.102',
'port' => 3306,
'database' => 'your_database',
'username' => 'read_user',
'password' => 'read_password',
'charset' => 'utf8mb4',
],
],
];
PDO连接管理器
<?php
// lib/DatabaseManager.php
class DatabaseManager
{
private static $instances = [];
private $config;
private $masterConnection;
private $slaveConnection;
public function __construct($config)
{
$this->config = $config;
}
// 获取主库连接(写操作)
public function getMasterConnection()
{
if (!$this->masterConnection) {
$this->masterConnection = $this->createConnection($this->config['master']);
}
return $this->masterConnection;
}
// 获取从库连接(读操作)
public function getSlaveConnection()
{
// 随机选择一个从库
$slaves = $this->config['slaves'];
$slave = $slaves[array_rand($slaves)];
$key = $slave['host'] . ':' . $slave['port'];
if (!isset(self::$instances[$key])) {
self::$instances[$key] = $this->createConnection($slave);
}
return self::$instances[$key];
}
private function createConnection($dbConfig)
{
$dsn = sprintf(
"mysql:host=%s;port=%d;dbname=%s;charset=%s",
$dbConfig['host'],
$dbConfig['port'],
$dbConfig['database'],
$dbConfig['charset']
);
try {
$pdo = new PDO($dsn, $dbConfig['username'], $dbConfig['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
return $pdo;
} catch (PDOException $e) {
throw new Exception("Database connection failed: " . $e->getMessage());
}
}
}
读写分离的查询构建器
<?php
// lib/QueryBuilder.php
class QueryBuilder
{
private $dbManager;
public function __construct(DatabaseManager $dbManager)
{
$this->dbManager = $dbManager;
}
// 写操作 - 使用主库
public function insert($table, $data)
{
$pdo = $this->dbManager->getMasterConnection();
$columns = implode(', ', array_keys($data));
$placeholders = ':' . implode(', :', array_keys($data));
$sql = "INSERT INTO {$table} ({$columns}) VALUES ({$placeholders})";
$stmt = $pdo->prepare($sql);
return $stmt->execute($data);
}
// 读操作 - 使用从库
public function select($table, $conditions = [], $fields = '*')
{
$pdo = $this->dbManager->getSlaveConnection();
$sql = "SELECT {$fields} FROM {$table}";
if (!empty($conditions)) {
$where = [];
foreach ($conditions as $key => $value) {
$where[] = "{$key} = :{$key}";
}
$sql .= " WHERE " . implode(' AND ', $where);
}
$stmt = $pdo->prepare($sql);
$stmt->execute($conditions);
return $stmt->fetchAll();
}
// 更新操作 - 使用主库
public function update($table, $data, $conditions)
{
$pdo = $this->dbManager->getMasterConnection();
$setParts = [];
foreach ($data as $key => $value) {
$setParts[] = "{$key} = :set_{$key}";
}
$whereParts = [];
foreach ($conditions as $key => $value) {
$whereParts[] = "{$key} = :where_{$key}";
}
$sql = "UPDATE {$table} SET " . implode(', ', $setParts)
. " WHERE " . implode(' AND ', $whereParts);
$params = [];
foreach ($data as $key => $value) {
$params["set_{$key}"] = $value;
}
foreach ($conditions as $key => $value) {
$params["where_{$key}"] = $value;
}
$stmt = $pdo->prepare($sql);
return $stmt->execute($params);
}
// 删除操作 - 使用主库
public function delete($table, $conditions)
{
$pdo = $this->dbManager->getMasterConnection();
$whereParts = [];
foreach ($conditions as $key => $value) {
$whereParts[] = "{$key} = :{$key}";
}
$sql = "DELETE FROM {$table} WHERE " . implode(' AND ', $whereParts);
$stmt = $pdo->prepare($sql);
return $stmt->execute($conditions);
}
}
使用方法
<?php
// index.php
require_once 'config/database.php';
require_once 'lib/DatabaseManager.php';
require_once 'lib/QueryBuilder.php';
// 初始化
$config = include 'config/database.php';
$dbManager = new DatabaseManager($config);
$queryBuilder = new QueryBuilder($dbManager);
// 写操作 - 自动使用主库
$userId = $queryBuilder->insert('users', [
'name' => '张三',
'email' => 'zhangsan@example.com',
'created_at' => date('Y-m-d H:i:s')
]);
// 读操作 - 自动使用从库
$users = $queryBuilder->select('users', ['status' => 1], 'id, name, email');
// 更新操作 - 自动使用主库
$queryBuilder->update('users',
['status' => 2, 'updated_at' => date('Y-m-d H:i:s')],
['id' => $userId]
);
// 删除操作 - 自动使用主库
$queryBuilder->delete('users', ['id' => $userId]);
高级优化方案
连接池实现(使用Swoole)
<?php
// lib/ConnectionPool.php
class ConnectionPool
{
private $pool;
private $maxConnections;
private $connectionConfig;
public function __construct($config, $maxConnections = 10)
{
$this->connectionConfig = $config;
$this->maxConnections = $maxConnections;
$this->pool = new SplQueue();
}
public function getConnection()
{
if ($this->pool->count() > 0) {
return $this->pool->dequeue();
}
return $this->createConnection();
}
public function releaseConnection($connection)
{
if ($this->pool->count() < $this->maxConnections) {
$this->pool->enqueue($connection);
} else {
$connection = null;
}
}
private function createConnection()
{
// 创建连接逻辑
}
}
主从延迟处理
<?php
// 对于关键数据的读取,强制使用主库
class CriticalDataReader
{
private $dbManager;
public function getLatestUserData($userId)
{
// 某些场景下,读最新数据需要强制用主库
$pdo = $this->dbManager->getMasterConnection();
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$userId]);
return $stmt->fetch();
}
}
监控和告警
<?php
// lib/Monitor.php
class Monitor
{
public function checkReplicationStatus()
{
// 检查主从复制状态
$pdo = $this->dbManager->getSlaveConnection();
$stmt = $pdo->query("SHOW SLAVE STATUS");
$status = $stmt->fetch();
if ($status['Slave_IO_Running'] !== 'Yes' ||
$status['Slave_SQL_Running'] !== 'Yes') {
// 发送告警
$this->sendAlert('Replication is down!');
}
// 检查延迟
$secondsBehind = $status['Seconds_Behind_Master'];
if ($secondsBehind > 60) {
$this->sendAlert("Replication lag: {$secondsBehind} seconds");
}
}
private function sendAlert($message)
{
// 发送邮件、短信或使用告警系统
error_log($message);
// 实际项目中可以使用第三方服务
}
}
注意事项
- 事务处理:事务中的读写都应该使用主库
- 主从延迟:对实时性要求高的场景,考虑强制使用主库
- 连接管理:使用连接池减少连接开销
- 负载均衡:合理分配读请求到多个从库
- 监控告警:定期检查主从同步状态
- 容错处理:从库故障时自动切换到其他从库或主库
这个实现方案提供了完整的MySQL主从同步解决方案,从数据库配置到PHP应用层实现,涵盖了读写分离、连接管理、延迟处理等核心功能。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播