先说结论:学员在手机看了一半、电脑继续看却从头开始,这类进度不同步的根因是进度写入没有幂等与版本控制——多条进度同时上报,落后的覆盖了新的。解法是给每次进度更新加"版本号+时间戳",服务端只接受新版本,配合防抖合并高频上报。本文从一个 3000 学员在线平台的真实故障讲起,给出可落地的同步方案与代码。
背景交代下环境:给一家在线职业教育平台做学习进度改造,底座用的是某一站式SaaS教育系统,课程、章节、学员、后台管理这些基础能力它都承载;但"进度同步的一致性"这种跟业务强耦合的规则,通用能力覆盖不到,在业务层自己实现。故障就发生在我们自研的进度同步逻辑上,与底座本身无关。
平台接入一场万人直播课后,收到大量投诉:手机看到第 12 节,电脑打开还是第 3 节;昨天看完的课,今天显示"未完成"。查日志发现根因:进度上报接口没有幂等保护,学员的 APP 和网页端同时上报进度,旧的上报(第 3 节)晚到,把新的(第 12 节)覆盖了。进度被"回退",比"没记录"更伤体验。
进度不同步分两类,排查方向完全不同:
判断方法:用两个终端分别看到第 3 节和第 12 节,看数据库里最终存的是哪条。存了旧的就是二类,没存的就是一类。
进度同步的核心是只让新的进度生效。做法:每次进度携带版本号(或"用户+课程+章节"的业务唯一键 + 学习时长),服务端判断"新版本才覆盖":
def update_progress(user_id, course_id, chapter_no, version):
# 用业务唯一键做幂等:同一用户同一课程同一章节只保留一条
key = f'progress:{user_id}:{course_id}'
# 版本号小的上报直接丢弃,只接受更大的版本
if version <= get_current_version(key):
return 'stale_ignored'
set_current_version(key, version)
db.save(user_id, course_id, chapter_no, version)
return 'updated'学员看视频时,播放器可能每秒都在上报进度。全量入库扛不住,还会互相打架。两个手段:
// 播放器上报进度:5秒防抖 + 变化超阈值才上报
let lastReport = 0;
player.on('timeupdate', (e) => {
const now = Date.now();
if (now - lastReport < 5000) return; // 5秒内不重复上报
const pct = Math.floor(e.percent * 100);
if (Math.abs(pct - lastPct) < 5) return; // 变化小于5%不上报
lastReport = now; lastPct = pct;
reportProgress(courseId, chapterNo, pct, version++);
});实测:防抖合并后,单节课的高频上报量降为原来的 1/40,接口压力骤减,进度也不乱了。
读取时也要注意:如果 APP 和网页端各有本地缓存,必须以服务端最新进度为准,别让本地缓存把进度"卡住":
# 读取进度:服务端数据为准,本地缓存仅作展示
def get_progress(user_id, course_id):
return db.query_latest(user_id, course_id) # 只读服务端最新一条教训:早期版本网页端先读本地 localStorage,服务端更新了也显示旧的,学员以为"没同步"。一律服务端为准,本地只缓存展示层。
Q1:版本号用什么值?
答:最简单的用学习进度百分比(0-100)或章节序号——越大约新。复杂场景再单独加 version 字段。
Q2:多端同时上报会不会冲突?
答:会。但版本号机制下,两个端各自带当前版本,服务端只接受大的那个——谁新听谁的,不冲突。
Q3:进度同步需要 WebSocket 实时推吗?
答:不需要。学员切端是低频事件,用"下次打开时拉取最新进度"就够;实时推反而增加复杂度和成本。
Q4:断网期间看的进度会丢吗?
答:不会丢,但要"本地暂存 + 联网补报"。前端把断网期间的进度暂存,恢复网络后带上最新版本号补报一次。
Q5:版本号和时间戳都要吗?
答:版本号是主判据(谁新听谁),时间戳做兜底参考(同时上报时谁后到听谁)。两个字段成本很低,建议都存,排查问题时能直接定位"哪条覆盖了哪条"。
学习进度不同步,九成是"旧值覆盖新值"。版本号 + 幂等 + 防抖合并,三件套做到,进度回退归零、高频上报不炸库。这套方案不只在教育场景有效——任何"多端写同一个进度/状态"的业务(课程进度、文档协作、任务状态)都能直接套用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。