〇、前言
在短视频平台后端架构中,视频播放量(Views)计数是最基础也最核心的需求之一。看似简单的"每播放一次计数+1",在头部爆款视频的瞬时高并发场景下,很容易出现数据库崩溃、数据丢失、计数错乱等问题。
本文将围绕"如何实现一个高可用、高性能、数据安全的视频播放量计数系统"展开,从业务痛点出发,拆解完整链路设计、技术选型、数据库更新方式、异常处理及热点优化,给出可直接落地的工业级方案,帮你避开落地过程中的所有坑。
核心目标:支撑每秒万级甚至十万级并发写入,保证数据实时性(秒级延迟)、持久化(不丢数),解决爆款视频带来的系统瓶颈。
一、核心业务背景与痛点拆解
在动手设计系统前,先明确核心业务场景和痛点,这是所有设计的出发点:
业务背景:负责短视频平台后端架构,需实现视频播放量(Views)计数系统,用户点击播放视频后,播放量需实时更新(允许秒级延迟),且数据必须最终落库,不能因系统重启、故障丢失。
核心痛点:头部爆款视频瞬时流量极高,若每次播放都直接执行MySQL的UPDATE操作,会导致数据库行锁竞争严重,IOPS瞬间打满,进而引发数据库卡顿、接口超时,甚至整个服务雪崩。
基于这个痛点,我们确定核心设计原则:不直接让高并发请求穿透到MySQL,通过多级缓存、异步处理、批量落库等方式,分流减压,同时保证数据一致性和实时性。
二、完整链路设计:从用户播放到数据落库
整个计数系统的核心链路,核心是"分流减压、异步持久化",避免高并发直接冲击数据库,完整链路如下(从用户点击播放到数据最终落库):
用户播放请求
→ API服务(校验合法性)
→ 本地内存缓存(1s窗口合并计数)
→ Redis集群(原子自增计数)
→ 异步任务(定时/定量刷库)
→ MySQL(最终持久化)
各环节核心作用拆解,帮你理解每一步的价值:
-
API服务:校验视频合法性(避免无效播放计数),接收用户播放请求,不直接处理计数逻辑,快速响应前端;
-
本地内存缓存(Caffeine/Guava):做1秒窗口的计数合并,比如1秒内1000次播放,只合并成1次写Redis,大幅降低Redis的QPS压力;
-
Redis集群:核心计数组件,通过INCR/INCRBY原子命令实现高性能计数,单实例可支撑10万+ QPS,扛住高并发写入;
-
异步任务(XXL-Job/Quartz):定时(推荐3秒)或定量(累计增量达一定阈值)从Redis读取增量数据,批量刷写到MySQL,实现异步削峰;
-
MySQL:最终持久化存储播放量,只接收批量增量更新,避免高频单条更新带来的行锁竞争。
关键亮点:不直接写数据库,通过"本地缓存+Redis"双重抗流量,异步批量落库解放MySQL,既保证高性能,又满足实时性和持久化需求。
三、核心技术选型:各组件作用与选型建议
技术选型的核心是"适配高并发、易落地、可扩展",根据平台规模,分为最简必选方案和高可用增强方案,各组件作用清晰可落地:
1. 必选组件(中小平台,支撑10万+ QPS)
| 组件 | 核心作用 | 选型建议 |
|---|---|---|
| Redis(主从/集群) | 实时计数核心,通过INCR原子命令实现高性能写入,存储实时播放量 | Redis 6.0+,开启主从复制,避免单点故障 |
| 本地内存缓存 | 1秒窗口合并计数,削峰减压,降低Redis请求量 | Caffeine(性能优于Guava,推荐) |
| 定时任务 | 定时从Redis读取增量,批量刷写到MySQL,保证数据持久化 | XXL-Job(易部署、可监控,适配分布式场景) |
| MySQL | 最终持久化存储播放量,支撑后续数据统计、查询 | MySQL 8.0+,合理建索引(视频ID主键) |
2. 高可用增强组件(大厂/头部平台,支撑百万级 QPS)
若平台经常出千万级爆款、瞬时流量脉冲极强,可新增以下组件,进一步提升系统稳定性:
-
消息队列(Kafka/RocketMQ):做流量削峰填谷,用户播放请求先写入MQ,再由消费端批量处理写入Redis,避免瞬时洪峰冲击Redis;同时留存播放行为流水,用于对账、防刷、数据分析;
-
分布式锁(Redisson):防止多服务实例重复刷库,保证刷库任务幂等性,避免数据重复累加;
-
Redis集群分片:拆分热点Key,分散单节点压力,支撑更高并发。
四、核心细节:数据库更新方式选型(增量 vs 覆盖)
在异步刷库环节,数据库更新方式的选择,直接决定数据是否会丢失、错乱,这也是很多开发者踩坑的重点------必须用增量更新,绝对不能用覆盖更新,具体拆解如下:
1. 两种更新方式定义(以MySQL为例)
(1)增量更新(推荐,工业级标准)
核心逻辑:不直接写总播放量,只计算"这段时间内新增的播放量差值",累加到数据库原有值上。
标准SQL示例:
sql
-- @incr 是这段时间的新增播放量差值,@vid 是视频ID
UPDATE video SET views = views + @incr WHERE id = @vid;
(2)覆盖更新(禁止使用,踩坑重灾区)
核心逻辑:从Redis中读取当前的"总播放量",直接覆盖数据库中该视频的原有播放量,完全替代旧值。
错误SQL示例:
sql
-- @total 是从Redis读取的当前总播放量
UPDATE video SET views = @total WHERE id = @vid;
2. 为什么禁止覆盖更新?(4个致命问题)
-
多实例并发刷库,数据覆盖丢失:多个服务实例同时读取Redis总播放量,先后执行覆盖更新,后执行的会抹掉前执行后新增的播放量;
-
异步延迟导致数据丢失:Redis与MySQL存在秒级延迟,读取的Redis总播放量是旧值,覆盖后会抹掉延迟期间的新增播放量;
-
故障重试导致数据错乱:刷库失败重试时,再次读取Redis总播放量(已新增),重复覆盖会导致计数错乱;
-
放大行锁竞争:覆盖更新需"读Redis→写MySQL",多实例并发争抢行锁,回到"直接写MySQL"的原始痛点。
3. 增量更新的核心优势(完美适配场景)
-
天然支持多实例并发:加法可交换,无论多少实例同时刷库,累加结果永远正确,不丢数;
-
适配故障重试:即使刷库重试,重复累加增量也可通过对账修正,绝不会少数据;
-
降低数据库压力:行内累加,SQL执行更快,行锁持有时间短,避免锁竞争;
-
适配异步延迟:无需强一致,只需累加新增差值,秒级延迟完全不影响数据准确性。
4. 增量更新标准实现(避免重复累加)
工业级标准做法,确保增量计算准确,不重复、不遗漏:
-
记录上一次同步时的Redis播放量基数(可存在Redis或MySQL中);
-
本次同步时,计算增量 = 当前Redis总播放量 - 上一次基数;
-
执行增量更新SQL,将增量累加到MySQL原有值;
-
更新基数记录,作为下一次同步的基准。
五、异常处理设计:保证数据不丢、最终一致
高并发系统中,故障不可避免,需针对核心异常场景做兜底,确保数据不丢、最终一致(允许秒级延迟):
1. 场景1:Redis宕机/重启
解决方案:本地内存日志 + Redis持久化兜底
-
写入Redis前,同时写一份本地日志文件(类似AOF思想),记录播放量增量;
-
Redis开启RDB+AOF混合持久化,重启后自动恢复数据;
-
若Redis彻底宕机,直接将本地日志中的增量写入MySQL,避免数据丢失。
2. 场景2:异步刷库失败(网络/MySQL宕机)
解决方案:重试机制 + 失败队列兜底
-
刷库失败时,不删除Redis中的增量数据,下一轮定时任务继续重试(最多重试5次);
-
若多次重试失败,将增量数据写入失败队列,触发告警,人工兜底处理,确保数据最终落库。
3. 场景3:服务全部重启
解决方案:Redis持久化 + 刷库任务兜底
Redis的RDB+AOF持久化确保重启后数据不丢失,服务启动后,定时刷库任务自动将Redis中未同步的增量数据写入MySQL,无需人工干预。
六、热点处理:解决爆款视频单Key高并发瓶颈
爆款视频会导致Redis单Key热点,10万+ QPS打满单个Redis节点,需在缓存层做优化,彻底解决单点瓶颈,推荐4层优化方案:
1. 本地内存一级缓存(最有效、最廉价)
API服务本地内存做1秒窗口合并计数,同一视频1秒内多次播放,只合并成1次写Redis,将Redis QPS从10万+压到1次/秒。
2. Redis热点Key分片(集群级优化)
将单个视频的Key拆分成多个分片,例如:video:views:10086:0 ~ video:views:10086:9,写入时随机选择一个分片执行INCR,读取时求和所有分片的值,将单节点压力分散到多个Redis节点。
3. Redis主从读写分离
写入操作走Redis主节点,读取操作走从节点,爆款视频的高频读取压力不会影响写入性能,进一步提升并发支撑能力。
4. 热点自动识别 + 动态降级
系统自动识别QPS超阈值的视频(热点视频),自动开启分片+本地缓存合并策略,无需人工干预,适配流量动态变化。
七、方案总结与落地建议
本文给出的播放量计数系统方案,是抖音、快手等主流短视频平台的工业级实践,核心优势的是"高性能、高可用、易落地、可扩展",总结如下:
-
核心链路:用户播放 → 本地缓存合并 → Redis计数 → 异步批量刷库 → MySQL持久化;
-
核心组件:Redis(计数核心)+ 本地缓存(削峰)+ 定时任务(刷库)+ MySQL(持久化);
-
关键细节:数据库必须用增量更新,禁止覆盖更新;热点Key用分片+本地缓存优化;
-
异常兜底:本地日志 + Redis持久化 + 重试机制,确保数据不丢、最终一致。
落地建议
-
中小平台:优先采用最简方案(Redis+本地缓存+定时任务+MySQL),足够支撑10万+ QPS,开发成本低、易维护;
-
头部平台:新增Kafka做流量削峰、分布式锁保证幂等性,Redis集群分片支撑百万级QPS;
-
上线后:新增对账脚本,每日对比Redis和MySQL的播放量差值,自动修复数据,确保一致性。