SiYuan 数据同步冲突预防:PHP 实现最佳实践
·
一、数据同步冲突的成因与危害
在 SiYuan 笔记系统的多设备协同场景中,数据同步冲突是常见问题。当多个客户端同时修改同一资源、网络延迟导致同步顺序错乱或设备时钟不一致时,极易引发数据覆盖、内容丢失等问题。这些冲突不仅影响用户体验,更可能造成重要笔记数据的损坏。
冲突产生的核心原因包括:
- 并发编辑:多设备同时修改同一文档
- 网络不稳定:同步请求延迟导致版本混乱
- 设备差异:本地缓存与服务器数据不同步
- 同步策略缺陷:缺乏有效的版本校验机制
二、冲突预防的核心原则
针对 SiYuan 数据同步场景,我们需要遵循以下原则设计冲突预防机制:
- 版本追踪:为每个资源维护唯一版本标识
- 增量同步:仅传输修改部分而非完整数据
- 原子操作:确保同步过程的完整性
- 冲突检测:提前发现潜在的同步冲突
- 明确策略:定义清晰的冲突解决规则
三、PHP 实现的核心技术方案
1. 数据结构设计
首先设计基础数据结构,包含必要的版本控制字段:
php
<?php
/**
* SiYuan笔记数据模型
* 包含版本控制所需的核心字段
*/
class NoteData {
// 笔记唯一标识
public $noteId;
// 笔记内容
public $content;
// 版本号(用于乐观锁)
public $version;
// 最后修改时间戳
public $lastModified;
// 最后修改设备ID
public $lastModifiedBy;
// 数据校验哈希
public $contentHash;
/**
* 生成内容哈希用于快速比对
* @return string
*/
public function generateHash() {
return sha1($this->content . $this->lastModified);
}
}
?>
2. 版本控制实现
采用乐观锁机制实现版本控制,确保数据更新的原子性:
php
<?php
/**
* 笔记同步管理器
* 实现版本控制与冲突检测
*/
class NoteSyncManager {
private $db; // 数据库连接
/**
* 初始化数据库连接
*/
public function __construct($dbConnection) {
$this->db = $dbConnection;
}
/**
* 尝试更新笔记,使用乐观锁防止冲突
* @param NoteData $note 待更新的笔记数据
* @return array 包含操作结果和冲突信息的数组
*/
public function updateNote(NoteData $note) {
try {
// 开启数据库事务
$this->db->beginTransaction();
// 查询当前服务器上的笔记版本
$stmt = $this->db->prepare("
SELECT version, last_modified FROM notes
WHERE note_id = :noteId FOR UPDATE
");
$stmt->execute([':noteId' => $note->noteId]);
$serverNote = $stmt->fetch(PDO::FETCH_ASSOC);
// 检查是否存在版本冲突
if ($serverNote && $serverNote['version'] != $note->version) {
// 发现冲突,回滚事务
$this->db->rollBack();
return [
'success' => false,
'conflict' => true,
'serverVersion' => $serverNote['version'],
'message' => '检测到版本冲突,请先同步最新数据'
];
}
// 计算新的版本号和哈希值
$newVersion = $serverNote ? $serverNote['version'] + 1 : 1;
$note->version = $newVersion;
$note->lastModified = time();
$note->contentHash = $note->generateHash();
// 执行更新或插入操作
if ($serverNote) {
// 更新现有笔记
$updateStmt = $this->db->prepare("
UPDATE notes SET
content = :content,
version = :version,
last_modified = :lastModified,
last_modified_by = :lastModifiedBy,
content_hash = :contentHash
WHERE note_id = :noteId AND version = :oldVersion
");
$updateStmt->execute([
':content' => $note->content,
':version' => $newVersion,
':lastModified' => $note->lastModified,
':lastModifiedBy' => $note->lastModifiedBy,
':contentHash' => $note->contentHash,
':noteId' => $note->noteId,
':oldVersion' => $serverNote['version']
]);
} else {
// 插入新笔记
$insertStmt = $this->db->prepare("
INSERT INTO notes
(note_id, content, version, last_modified, last_modified_by, content_hash)
VALUES (:noteId, :content, :version, :lastModified, :lastModifiedBy, :contentHash)
");
$insertStmt->execute([
':noteId' => $note->noteId,
':content' => $note->content,
':version' => $newVersion,
':lastModified' => $note->lastModified,
':lastModifiedBy' => $note->lastModifiedBy,
':contentHash' => $note->contentHash
]);
}
// 提交事务
$this->db->commit();
return [
'success' => true,
'conflict' => false,
'newVersion' => $newVersion,
'message' => '笔记更新成功'
];
} catch (PDOException $e) {
// 发生错误时回滚
$this->db->rollBack();
return [
'success' => false,
'conflict' => false,
'message' => '数据库错误:' . $e->getMessage()
];
}
}
}
?>
3. 增量同步实现
为减少数据传输量,实现增量同步机制:
php
<?php
/**
* 增量同步处理器
* 仅传输和处理修改的数据部分
*/
class IncrementalSyncProcessor {
private $syncManager;
public function __construct(NoteSyncManager $manager) {
$this->syncManager = $manager;
}
/**
* 获取指定时间点后的所有变更
* @param int $sinceTimestamp 起始时间戳
* @return array 变更的笔记列表
*/
public function getChangesSince($sinceTimestamp) {
// 实际项目中这里会查询数据库获取变更记录
// 仅返回修改过的笔记ID和版本信息,而非完整内容
$changes = [
// 示例数据结构
/*
[
'noteId' => 'note123',
'version' => 3,
'lastModified' => 1620000000,
'isDeleted' => false
],
*/
];
return $changes;
}
/**
* 处理客户端提交的增量变更
* @param array $localChanges 本地变更列表
* @param int $clientLastSyncTime 客户端最后同步时间
* @return array 同步结果
*/
public function processLocalChanges($localChanges, $clientLastSyncTime) {
$results = [];
$serverChanges = $this->getChangesSince($clientLastSyncTime);
// 检测冲突:比较客户端和服务器的变更
$conflicts = $this->detectConflicts($localChanges, $serverChanges);
if (!empty($conflicts)) {
// 存在冲突,返回冲突信息给客户端
return [
'success' => false,
'conflicts' => $conflicts,
'serverChanges' => $serverChanges,
'message' => '检测到同步冲突,请解决后重试'
];
}
// 无冲突,处理所有本地变更
foreach ($localChanges as $change) {
$note = new NoteData();
// 填充笔记数据...
$result = $this->syncManager->updateNote($note);
$results[] = $result;
}
return [
'success' => true,
'results' => $results,
'serverChanges' => $serverChanges,
'newSyncTime' => time()
];
}
/**
* 检测客户端与服务器之间的变更冲突
* @param array $localChanges 本地变更
* @param array $serverChanges 服务器变更
* @return array 冲突列表
*/
private function detectConflicts($localChanges, $serverChanges) {
$conflicts = [];
$serverNoteIds = array_column($serverChanges, 'noteId');
// 检查本地修改的笔记是否在服务器上也被修改
foreach ($localChanges as $localChange) {
if (in_array($localChange['noteId'], $serverNoteIds)) {
$conflicts[] = $localChange['noteId'];
}
}
return $conflicts;
}
}
?>
4. 冲突解决策略
当检测到冲突时,需要有明确的解决策略:
php
<?php
/**
* 冲突解决处理器
* 提供多种冲突解决策略
*/
class ConflictResolver {
/**
* 解决笔记冲突的方法
* @param NoteData $localNote 本地笔记
* @param NoteData $serverNote 服务器笔记
* @param string $strategy 解决策略
* @return NoteData 解决后的笔记
*/
public function resolveConflict(NoteData $localNote, NoteData $serverNote, $strategy = 'server_wins') {
switch ($strategy) {
case 'server_wins':
// 服务器版本优先
return $serverNote;
case 'local_wins':
// 本地版本优先
return $localNote;
case 'merge':
// 尝试自动合并(适用于支持分段的文档)
return $this->autoMergeNotes($localNote, $serverNote);
case 'create_copy':
// 创建副本保留两个版本
$copyNote = clone $serverNote;
$copyNote->noteId = $serverNote->noteId . '_conflict_copy_' . time();
$copyNote->content = "【冲突副本】\n" . $serverNote->content;
return $copyNote;
default:
return $serverNote;
}
}
/**
* 自动合并两个笔记内容
* 简单实现:保留双方修改并标记冲突区域
*/
private function autoMergeNotes(NoteData $localNote, NoteData $serverNote) {
$merged = new NoteData();
$merged->noteId = $localNote->noteId;
$merged->version = max($localNote->version, $serverNote->version) + 1;
$merged->lastModified = time();
// 实际应用中需要更复杂的合并逻辑
// 这里仅做简单示例
$merged->content = "【自动合并结果】\n\n" .
"本地修改:\n{$localNote->content}\n\n" .
"服务器修改:\n{$serverNote->content}\n\n" .
"【请检查并编辑合并内容】";
return $merged;
}
}
?>
四、最佳实践总结
-
分层防御策略
- 前端:实时检测本地修改,定期自动保存
- 传输层:使用增量同步减少冲突概率
- 服务层:实现严格的版本控制和原子操作
- 存储层:保留历史版本,支持数据回溯
-
用户体验优化
- 提前预警:当检测到其他设备正在编辑时提示用户
- 自动同步:设置合理的自动同步间隔
- 清晰提示:冲突发生时提供明确的解决选项
- 简化操作:提供一键合并和版本对比功能
-
系统设计建议
- 实现定期全量同步作为增量同步的补充
- 对重要操作保留详细日志,便于问题排查
- 设计合理的缓存策略,减少重复同步
- 考虑分布式锁机制处理高并发场景
通过上述机制,我们可以在 SiYuan 笔记系统中有效预防和处理数据同步冲突,保障用户数据的完整性和一致性。实际应用中,还需要根据具体业务场景调整策略,平衡性能与数据安全性。
更多推荐


所有评论(0)