简介:本文以播放记录的多端同步为切入点,系统讲解同步架构分层、数据模型设计、基于版本游标的增量同步协议与冲突合并策略,并给出表结构、接口定义与合并伪代码等完整示例,帮助开发者构建一套高效、一致且能从容应对弱网环境的跨设备数据同步体系。
当用户从手机切换到平板打开同一视频时,期望进度无缝衔接。这个看似简单的体验,背后实则是一个典型的多副本数据一致性问题:
因此,播放记录同步的设计目标可以概括为四点:最终一致、合并正确、弱网不丢、规模平滑。下文围绕这四个目标逐层展开。
一套典型的同步架构可分为四层:
数据流向可以概括为:业务写入 → 本地落库 → 同步引擎上传 → 服务端合并并分配版本 → 其他设备增量拉取。客户端永远不直接“写穿”到云端,这是保证离线可用与弱网可恢复的前提。
核心记录表可按如下方式设计:
CREATE TABLE play_history (id BIGINT PRIMARY KEY,user_id BIGINT NOT NULL,media_id BIGINT NOT NULL,progress INT NOT NULL DEFAULT 0,duration INT NOT NULL DEFAULT 0,is_deleted TINYINT NOT NULL DEFAULT 0,version BIGINT NOT NULL DEFAULT 0,updated_at DATETIME(3) NOT NULL,created_at DATETIME(3) NOT NULL,UNIQUE KEY uk_user_media (user_id, media_id),KEY idx_user_version (user_id, version));
字段设计的关键考量:
拉取接口采用游标分页:
POST /v1/sync/pull{"device_id": "device-8f3a","cursor": 128913,"limit": 200}
返回示例:
{"records": [{"media_id": 10086,"progress": 732,"duration": 2700,"is_deleted": false,"version": 128914}],"next_cursor": 128914,"has_more": false,"server_time": 1730000000000}
上传接口批量提交本地脏数据:
POST /v1/sync/push{"device_id": "device-8f3a","changes": [{"change_id": "c-20250601-0001","media_id": 10086,"progress": 900,"client_ts": 1729999998000}]}
协议设计要点:
最典型的冲突是:手机上看到 50%,平板上看到 70%,两台设备几乎同时上线。常见策略分三层:
合并逻辑伪代码:
function merge(serverRec, incoming):if serverRec.is_deleted or incoming.is_deleted:return deletedRecord(maxVersion)newProgress = max(serverRec.progress, incoming.progress)if incoming.duration > 0:newDuration = incoming.durationelse:newDuration = serverRec.durationreturn record(progress = newProgress,duration = newDuration,version = nextVersion(user),updated_at = serverNow())
需要注意边界:用户刻意从头重看时,“进度取最大”会误判。工程上通常约定客户端在进度回退时附带“重看”标记,服务端收到该标记后允许以较小值覆盖。
海量并发下,同步服务要同时扛住读放大与写放大:
若部署在云端基础设施之上(如百度智能云等云厂商普遍提供的计算、存储、消息队列、监控告警等通用类目服务),上述组件可由托管服务拼装而成,团队可把精力更多投入在合并语义与协议演进上。
播放记录属于行为隐私数据,多副本同步会放大暴露面:
把多设备播放记录同步拆解为五个可复用的核心决策:
这套决策与具体业务无关,收藏、笔记、阅读进度、游戏存档等多端同步场景均可直接套用。任何产品从单设备走向多设备时,它都是工程成本最低、确定性最强的通用路径。