Post

常见场景题

常见场景题

面试鸭后端场景面试题

场景设计题答题模版:核心待解决问题点 + 解决方式

如何设计一个秒杀功能?

需要解决的问题点:

  • 瞬时流量承接
  • 防止超卖
  • 避免对正常服务影响
  • 兜底方案(数据库兜底不超卖)

一般的设计是:

  • 秒杀页面静态化,通过 CDN 减少请求压力
  • 网关层做限流和防刷
  • 库存提前加载到 Redis,通过 DECR 原子扣减库存
  • 扣库存成功后把下单请求写入 MQ,实现削峰填谷
  • 订单服务异步消费 MQ 创建订单
  • 数据库通过 update … where stock > 0 再次保证不超卖

缓存和数据库一致性如何保证?

方案 1:Cache Aside(旁路缓存)

  • 读:先查缓存,缓存未命中再查数据库,查询结果写入缓存
  • 写:先更新数据库,后删除缓存

优点:实现简单;缺点:可能存在短暂的缓存不一致

方案 2:延时双删

  • 写:先更新数据库,删除缓存;sleep(100ms)二次删除缓存

优点:解决并发写导致脏数据问题,缺点:需要 sleep,不够优雅

方案 3:消息队列异步更新缓存(最终一致性)

  • 写:先更新数据库,发送消息到 MQ;异步消费 MQ 删除缓存

优点:解耦,可重试,最终一致性

方案 4:Binlog + Canal

  • 写:Mysql -> Binlog -> Canal 监听 -> Cache Update Service -> Redis

优点:数据驱动,不侵入业务,最终一致性

有一张表里面有三个字段,分别是(id,开始时间,结束时间),表中数据量为 5000W,找出任意时刻最大并发数?

解题思路:把区间问题转成“事件点”问题;然后按时间排序,做一次 前缀和扫描(Sweep Line 算法)

例子:

idstartend
115
227
346

第一步:转换为事件

timevalue
1+1
5-1
2+1
7-1
4+1
6-1

第二步:按时间排序

timevalue
1+1
2+1
4+1
5-1
6-1
7-1

第三步:前缀和统计

timevaluecurrentmax
1+111
2+122
4+133
5-123
6-113
7-103

所以最大并发 = 3

分布式锁一般都怎样实现?

分布式锁

现在有 40 亿个 QQ 号,给你 1G 的内存,如何实现去重?

  • 方案1位图(Bitmap): 原理是每个QQ号对应1位,若存在则置 1,否则置 0

    需要知道 QQ 号的范围

  • 方案2归并: 将 40 亿个 QQ 号分成多块文件,用map去重后,归并结果
  • 方案3布隆过滤器(Bloom Filter): 通过多个哈希函数将 QQ 号映射到一个位数组中,判断是否存在

    假阳性;可能存在误判(false positive),但不会漏判(false negative)

如果要实现一个抢红包的功能,红包金额是如何计算的?

方案 A:平均分 + 小随机偏移

1
2
3
4
原理:
1、平均分 = totalAmount / n
2、每个红包加上随机波动:amount_i = average ± random(0, maxOffset)
3、最后一个红包用剩余金额补齐

优点是实现简单,缺点是红包差异不大,随机感低

方案 B:动态随机法(微信红包算法)

1
2
3
4
原理:
1、第 i 个人抢红包时,剩余金额为 R,剩余人数为 k
2、随机生成一个金额:
3、更新剩余金额和人数:

优点是随机性好,正态分布。

如果项目需要你实现敏感词过滤功能,如何实现?

方案1: 字典树(Trie / 前缀树)

方案2: Bloom Filter

实现一个算法,展示直播间送礼top 10用户

方案1: 小顶堆 Heap<user_id, total_value> + 哈希表 Map<user_id, total_value>

  • 小顶堆快速维护 Top 10;堆操作时间复杂度 O(logK) ,整体时间复杂度 O(N logK); 堆空间复杂度 O(K)
  • 哈希表快速查询某个用户总礼物金额

方案2: Redis ZSet

  • ZINCRBY live:<room_id> <gift_value> <user_id> → 增加礼物总额
  • ZREVRANGE live:<room_id> 0 9 WITHSCORES → 获取 Top 10

设计一个并发安全,lru缓存

数据结构:哈希表 + 双向链表

  • 哈希表提供 O(1) 的访问时间,存储 key, value 是指向双向链表节点
  • 双向链表维护访问顺序,头部是最近访问的元素,尾部是最久未访问的元素
1
2
3
优化:
1 、空间换时间:多 shard 分片锁,减少锁竞争
2 、借鉴 Redis,近似 LRU,候选池优化采样效果

使用redis实现延时队列

核心需求:延时消息需要根据延时时间排序,所以使用ZSet

使用ZSet。score为执行时间戳,member为消息


需要优化问题点:

消费端轮询空转

  • 获取最后一条消息执行时间戳,sleep

重复消费问题

  • lua脚本原子化操作:获取消息 + 删除消息
  • 消费者幂等

消息保证不丢

  • 到期任务 → 放入 ready_queue(List)

高并发优化

  • 分片

百万规模消息优化

  • 使用时间轮,每个 slot = 一个时间片(比如 1 秒),slot是list数据结构 ``` 一个圆盘(时间轮):

slot_0 slot_1 slot_2 … slot_59 ```

This post is licensed under CC BY 4.0 by the author.