面试官:爆火视频点赞每秒 10w,数据库行锁直接卡死,你怎么破?
最近有个兄弟去面某短视频大厂前面的架构设计聊得挺嗨结果面试官最后抛出一个看似简单的“点赞”题直接把他问到了自闭。面试官“现在有个爆火视频比如春晚刘谦变魔术一秒钟有 10 万人同时点赞。你代码里写 UPDATE video_statistics SET like_count like_count 1 WHERE video_id 1001;数据库会发生什么”兄弟“那就加 Redis 缓存啊异步写库。”面试官冷笑“如果这 10 万人都点赞同一个视频Redis 的单 Key 会被打爆Hot Key吗异步写库时如果 MQ 消费者还是一条条更新数据库的行锁排队能解决吗”这兄弟支支吾吾半天没答上来。其实这道题考的不是“点赞”而是高并发架构中极其硬核的“写热点Write Hotspot”问题。今天 Fox 带你三步拆解教你如何用架构师的思维优雅地消除数据库行锁地雷。第一招青铜回答——直接操作数据库自杀式打法很多人的第一反应是数据库不是有索引吗直接更新不就行了-- 看起来人畜无害的 SQL UPDATE video_statistics SET like_count like_count 1 WHERE video_id 1001;Fox 深度解析这是典型的“行锁地雷”。在 InnoDB 引擎中更新这一行必须获取排他锁X 锁。当 10 万个请求同时涌入时数据库内部会发生以下惨剧行锁竞争只有一个线程能拿到锁其余 99,999 个线程全部进入等待队列。上下文切换随着队列变长操作系统的 CPU 资源会全部浪费在线程的唤醒和挂起上。死锁检测MySQL 内部的死锁检测机制会疯狂扫描这个巨大的等待队列导致 CPU 瞬时飙升到 100%。结论在高并发面前单行更新就是系统的死穴。此时的吞吐量TPS会掉到个位数。第二招黄金回答——Redis 计数 异步落库稍微有点经验的会说不能直接压数据库得用内存挡一下。方案利用 Redis 的 INCR 命令点赞先在 Redis 里加 1然后通过 MQ 异步同步回 MySQL。瓶颈如果是超级热点比如热搜第一10w QPS 全部压在 Redis 的一个 Key 上单机 Redis 吞吐量也会到极限。而且如果 MQ 消费端依然是“拿一条消息、调一次 1 库操作”数据库的行锁压力依然没有任何缓解只是把压力从前端推迟到了后端。第三招王者回答——多级分层聚合 批量写合并真正的工业级方案核心思想只有四个字分而治之。1. 接入层本地缓冲Local Coalescing不要让每一个点赞都变成一次网络请求策略在 Web Server 的内存里利用 AtomicLong 开辟一个 200ms 的时间窗口。效果这 200ms 内的 500 次点赞在内存里直接合并成一个“点赞 500”的请求再发给后端服务。2. Redis 热点 Key 拆分Sharding模仿 Java 中 LongAdder 的设计思想。策略将 video_id_9527 拆分成多个子 Keycount:9527:1、count:9527:2 ... count:9527:10。分流请求按用户 ID 取模分散到不同的 Key 和不同的 Redis 节点上。查询查总数时把所有子 Key 的值求和即可3. 消费端批量写合并Batch Update—— 绝杀行锁这是解决数据库行锁的终极手段。操作MQ 消费者开启批量消费一次取 500 条消息在内存里按 video_id 进行聚合。执行最终在数据库里只执行一条合并后的 SQL-- 将 500 次锁竞争转化为 1 次 SQL 执行 UPDATE video_statistics SET like_count like_count 500 WHERE video_id 1001;由于加锁频率降低了三个数量级数据库的行锁竞争瞬间消失吞吐量原地起飞。面试满分模板直接背诵面试官“如何设计一个支撑 10w QPS 点赞的热点系统”你“我会采取‘层层聚合异步合并’的架构端侧聚合利用本地内存做微小的窗口聚合如 100ms将高频散点请求转化为低频批量请求减少 RPC 调用。缓存分片针对热点 Key采用Redis 计数器分片方案将写压力分摊到多个分片规避单点 Hot Key 问题。批量写合并这是核心。通过 MQ 异步削峰并在消费端实现批量聚合。将同一行数据的多次更新在内存中累加最终以一次 UPDATE ... SET count count N 的形式更新数据库。容灾兜底配合定期快照和 Binlog 日志确保在追求极致性能的同时满足最终一致性。”写在最后架构师的价值就在于面对“物理限制”如行锁、IOPS时能够利用内存和异步化手段把矛盾转移。