面试场景题:百万人同时点赞如何实现
今天咱们聊聊一个经典的面试题:如果有一场大型活动,比如明星演唱会直播,百万人同时点赞,你该如何设计系统来应对这种极端并发场景?
问题分析
这个题目看似简单,实际上考察的是你对高并发系统设计的全面理解。百万人同时点赞意味着什么?我们来算一笔账:
- 百万QPS(每秒查询率)
- 每秒产生百万条点赞记录
- 数据库写入压力巨大
- 用户体验要求实时反馈
这可不是简单的"加个缓存就完事"那么简单,需要从多个维度考虑。
解决方案思路
面对这种极端并发场景,我们需要采用"分层削峰"的策略,把流量层层过滤,最终到达数据库的请求量控制在可承受范围内。
1. 客户端优化:防抖与批量提交
首先从客户端入手,这是第一道防线:
- 防抖处理:用户连续点击只发送一次请求
- 批量提交:客户端缓存多个点赞操作,定期批量提交
- 本地反馈:先在本地显示点赞效果,异步同步到服务器
这样可以将原始的百万QPS降低到十万级别。
2. CDN加速:静态资源分离
把点赞按钮等静态资源放到CDN,减轻源站压力。虽然这个场景主要是写请求,但CDN的边缘计算能力可以在一定程度上分担压力。
3. API网关层:限流与熔断
在网关层面实施严格的限流策略:
- 令牌桶算法:限制每秒处理的请求数量
- IP限流:防止恶意刷赞
- 熔断机制:当系统负载过高时,暂时拒绝部分请求
# 示例配置
rate-limiter:
default: 1000 # 默认每秒1000次
user-limit: 10 # 每个用户每分钟最多10次
ip-limit: 100 # 每个IP每分钟最多100次
4. 缓存层:Redis计数器
使用Redis的原子操作来处理点赞计数:
// Lua脚本保证原子性
String luaScript =
"local count = redis.call('incr', KEYS[1]) " +
"if tonumber(count) == 1 then " +
"redis.call('expire', KEYS[1], ARGV[1]) " +
"end " +
"return count";
Long newCount = (Long) redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
Collections.singletonList("like_count:" + contentId),
"60" // 有效期60秒
);
这种方式可以支撑很高的QPS,同时利用Redis的持久化特性保证数据不丢失。
5. 消息队列:异步处理
对于非实时性要求的场景,可以使用消息队列进行异步处理:
- 将点赞请求放入消息队列
- 消费者按批次处理,写入数据库
- 可以根据数据库承载能力调节消费速率
6. 数据库优化:分库分表
如果确实需要实时写入数据库,需要做好分库分表:
- 水平分表:按时间或内容ID分表
- 读写分离:写主库,读从库
- 批量插入:定时批量处理点赞记录
7. 最终一致性:定时任务补偿
设计定时任务,定时将缓存中的数据同步到数据库,确保最终一致性:
- 每分钟统计一次缓存中的点赞数
- 与数据库中的实际数量对比
- 差异部分批量更新到数据库
架构设计图
用户客户端 -> CDN -> API网关 -> Redis缓存 -> 消息队列 -> 数据库
| | | |
| | | |
防抖批量 限流熔断 原子计数 异步处理
实际案例参考
微博的"转发评论赞"功能、抖音的点赞功能,都是类似的高并发场景。它们的解决方案包括:
- 客户端预处理,减少无效请求
- 多级缓存架构
- 异步化处理
- 数据分片存储
面试要点总结
回答这类问题时,记住以下几点:
- 分层思维:从客户端到服务端,每一层都有对应的优化策略
- 数据一致性:明确说明最终一致性的保障措施
- 容错机制:考虑系统异常时的降级方案
- 成本考量:平衡性能与成本,选择最适合的方案
总结
百万人同时点赞这种极端场景,考验的是你对整个技术栈的理解和运用能力。记住,高并发问题没有标准答案,关键是展示你的思考过程和技术深度。
希望这篇文章对你有所帮助!如果你觉得有用,欢迎关注【服务端技术精选】公众号,获取更多后端技术干货。
原文首发于 www.jiangyi.space
转载请注明出处
0 评论