本文目录导读:

- 数据库到数据库(主从/主主同步)
- 缓存与数据库同步(Redis/MySQL)
- 同步第三方 API(客户数据/订单系统)
- 文件同步(服务器之间)
- 复杂数据异构同步(CDC方案)
- 关键总结(如果只记一点)
- 常见陷阱与建议
PHP 数据同步是一个广泛的话题,取决于你的具体场景(数据库到数据库、文件、缓存、第三方API等),以下是针对 PHP 开发者最常用的几种数据同步方案,按场景分类:
数据库到数据库(主从/主主同步)
这是最常见的场景,通常用于读写分离或灾备。
A. 使用 MySQL 原生复制(推荐用于 MySQL)
-
原理:依赖 MySQL 的 binlog(二进制日志),PHP 不参与,由数据库引擎自动完成。
-
怎么用:在 PHP 中,只需配置两个连接,读走从库,写走主库。
-
PHP 代码示例(读写分离):
// 主库连接(写) $master = new PDO('mysql:host=master_host;dbname=my_db', 'user', 'pass'); // 从库连接(读) $slave = new PDO('mysql:host=slave_host;dbname=my_db', 'user', 'pass'); // 执行写操作 $master->exec("INSERT INTO users (name) VALUES ('张三')"); // 执行读操作(注意:刚写入的数据可能因复制延迟读不到) $stmt = $slave->query("SELECT * FROM users ORDER BY id DESC LIMIT 1"); -
优点:性能高,实时性强。
-
缺点:主从延迟问题(可通过中间件如 ProxySQL 解决,或强制走主库)。
B. 使用 MQ(消息队列)异步同步(适用于跨数据库/跨系统)
-
场景:不同种类的数据库(如 MySQL → Redis,MySQL → Elasticsearch)。
-
原理:PHP 写入主库后,发一条消息到队列(RabbitMQ/Kafka),消费者(另一个 PHP 进程)监听队列,消费后写入目标库。
-
PHP 代码示例(生产者):
// 假设使用 php-amqplib $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest'); $channel = $connection->channel(); $channel->queue_declare('user_sync', false, true, false, false); // 1. 写入MySQL $pdo->exec("INSERT INTO users (id, name) VALUES (123, '李四')"); // 2. 发送同步消息 $msg = new AMQPMessage(json_encode(['id' => 123, 'name' => '李四'])); $channel->basic_publish($msg, '', 'user_sync'); // 3. 关闭连接 ... -
PHP 代码示例(消费者脚本
php consumer.php):$callback = function($msg) use ($pdo) { $data = json_decode($msg->body, true); // 写入Elasticsearch或Redis $redis->hSet('user:'.$data['id'], 'name', $data['name']); echo "同步成功\n"; }; // 监听队列... -
优点:解耦,削峰填谷,实时性较好(秒级)。
-
缺点:需要额外维护 MQ 集群,代码复杂度增加。
缓存与数据库同步(Redis/MySQL)
这是高并发系统最常用的,逻辑通常在 PHP 业务代码中。
Cache Aside Pattern(旁路缓存)
-
读:先读 Redis,没有则读 MySQL,并回写 Redis。
-
写:先更新 MySQL,然后删除 Redis 中的 Key。
-
PHP 代码示例:
function getUser($id) { $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $key = "user:$id"; // 1. 查缓存 $user = $redis->get($key); if ($user !== false) { return json_decode($user, true); } // 2. 缓存没中,查库 $pdo = new PDO('mysql:host=...', 'user', 'pass'); $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([$id]); $user = $stmt->fetch(PDO::FETCH_ASSOC); // 3. 回写缓存(设置过期时间) $redis->setex($key, 3600, json_encode($user)); return $user; } function updateUser($id, $name) { // 1. 更新MySQL $pdo->prepare("UPDATE users SET name = ? WHERE id = ?")->execute([$name, $id]); // 2. 删除缓存(下次读取时自动回填新数据) $redis->del("user:$id"); }
同步第三方 API(客户数据/订单系统)
如果需要将数据从一个服务器同步到另一个 PHP 服务,通常使用 Webhook 或 RPC。
A. Webhook(对方推送过来)
-
PHP 脚本作为接收端(开放接口 URL)。
-
代码示例 (api/sync.php):
// 获取推送的数据(通常是 JSON) $json = file_get_contents('php://input'); $data = json_decode($json, true); // 验证签名(必须做,防止伪造) $sign = hash_hmac('sha256', $data['timestamp'].$data['body'], SECRET_KEY); if ($sign !== $data['sign']) { http_response_code(403); exit('Invalid Signature'); } // 写入本地数据库 $pdo->prepare("INSERT INTO orders (order_no, amount) VALUES (?, ?) ON DUPLICATE KEY UPDATE amount=VALUES(amount)")->execute([$data['order_no'], $data['amount']]); // 返回成功 echo "OK";
B. RPC(主动拉取)
-
PHP 脚本定时(cron job)去请求对方的 API 获取数据。
-
代码示例 (cron_sync.php):
// 调用第三方 API(例如同步昨天的订单) $ch = curl_init("http://api.customer.com/orders?date=yesterday"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $orders = json_decode($response, true); // 遍历并写入自己的库 foreach ($orders as $order) { // 使用 INSERT ... ON DUPLICATE KEY UPDATE 实现幂等 $sql = "INSERT INTO orders (id, amount) VALUES (?, ?) ON DUPLICATE KEY UPDATE amount = VALUES(amount)"; $pdo->prepare($sql)->execute([$order['id'], $order['amount']]); }
文件同步(服务器之间)
如果是指同步服务器上的文件(如上传的图片),不建议在 PHP 中读写文件实现。
- 最佳实践:
- 使用 rsync(Linux 自带)通过 cron 定时同步目录。
- 使用 云存储(OSS/S3/COS) + CDN,将文件上传到对象存储,数据库只存 URL,多台服务器共享同一个存储。
复杂数据异构同步(CDC方案)
如果数据量特别大(几千万条)或需要同步字段级变更,一定要用 CDC(Change Data Capture)。
- 工具:Canal(阿里开源)、Debezium、Flink CDC。
- 原理:监听 MySQL binlog,将变更事件解析推到 Kafka,PHP 消费者消费 Kafka 写入 Elasticsearch 或 Redis。
- 对于 PHP 来说,你只需写好消费者程序(消费 Kafka)即可,不需要在你的业务代码里写任何同步逻辑。
关键总结(如果只记一点)
- 如果你是企业级应用(局域网):用 MySQL 主从复制 + 中间件。
- 如果你是高并发互联网应用:写 MySQL 前先更新,删缓存;读请求走 Redis。
- 如果你是跨公司/跨网络:用 消息队列(异步)或 Restful API(同步)。
- 如果你同步超大表:必须上 Canal 或 Debezium,别写业务代码。
常见陷阱与建议
- 延迟一致性:PHP 写库后立即读可能读到旧数据(因为复制有延迟),解决方案:
isMaster判断,强制走主库。 - 幂等性:处理同步数据时,一定要做去重处理(如
INSERT ... ON DUPLICATE KEY UPDATE),防止重复数据造成主键冲突。 - 失败重试:如果是 MQ 消费失败,要有记录日志并重跑队列的机制,防止数据永久丢失。
推荐阅读:如果实际业务比较复杂,建议看下 Laravel 的 Broadcasting 或 Events 系统,或者使用 RabbitMQ Bundle,它们能帮你封装这些繁琐的步骤。