总结遇到的面试题-内存篇
1. Redis基本数据结构和底层
https://zhuanlan.zhihu.com/p/148562122
介绍一下跳表:跳表是一种多层索引的链表结构,查找、插入、删除的平均复杂度是 O(logn)。在 ZSet 中,Redis 同时用哈希表存储成员到分值的映射,用跳表存储分值到成员的映射,这样既能快速定位单个元素,又能高效支持范围查询。
2. Redis为什么快
这个问题大致来说可以从两个方向来回答:内存读写相比于磁盘读写速度要快上一个数量级,磁盘读写由于物理读写方式访问速度比较慢;另一个方面就可以对Redis的设计具体展开,包括但不限于:Redis单线程模型、Redis数据结构设计、Redis的I/O模型,这一部分可以对自己更熟悉的部分深入讲解。例如就单线程模型而言,Redis使用单线程避免了锁的竞争和多线程的复杂,同时Redis使用 I/O 多路复用机制,一个线程就能同时处理大量连接。
3. 如何估算某个业务场景下Redis内存占用大小
这是一个相对来说开放的情景题,如果没有非常贴的实习经历可能没有办法给出准确而深入的回答,不过也可以尽力从基础知识出发回答(至少展现有基础)。很容易知道总占用内存是并发用户 * 每个用户的信息占用Redis,并发用户可以通过业务API的QPS判断,每个用户的信息占用Redis可以举例一个相对具体的场景:例如在登录场景中,可以用String存储session相关信息,通过Hash来存储用户信息,除此之外需要考虑Redis数据结构的开销,对象头、指针、对齐方面的开销;最后从整体来看还需要考虑一部分的冗余,Redis的内存碎片、主从/集群冗余提高可用性;在登陆场景中,用户登陆的验证码或者是session不会永久存储,需要设置TTL例如30min,对于整体数据的淘汰策略通常选择
allkeys-lru 或 volatile-lru控制内存占用。实际应用场景中也可以通过监控来查看内存的使用情况。此外Redis持久化缓冲区可能会暂时占用内存,不过持久化用到的RDB快照和AOF日志都是存储到磁盘中的。总的来说主要要考虑使用什么数据结构,采取什么淘汰策略,围绕Redis的设计来谈(所以其实也可以认为回到了问题1)。
4. Redis除了缓存还可以用于什么
Redis除了用于缓存还可以作为分布式锁、消息队列来使用,这里可以选择一个自己相对熟悉的方面来展开。如果是从消息队列的角度展开,Redis作为消息队列相比于专业的消息队列的优点是简单快速轻量级,但根本上来说由于Redis本身是基于内存的中间件,
内存是易失性存储,所以基于 redis 实现的 mq 一定是存在消息丢失的风险的。
从数据结构选取来说,Redis实现消息队列时,可以用Redis中list这个数据结构,天然符合队列模型,如果是延时消息队列则可以用zset实现,score是时间戳;如果消费采取的是pull模型,可以用brpop指令阻塞式获取;为了保证消息不丢,Redis streams中实现了ACK机制,通过ack和引入持久化来保证可靠性。由于Redis是内存存储,通过Redis streams持久化也是异步执行的,所以依然有数据丢失的风险,为了优化这一点可以基于观察者模式设计Redis监控系统,XPENDING 用于统计未确认消息和失败次数,根据需求设置重试策略,如果某条消息多次失败,可以通过 XPENDING 获取其 delivery count,当超过阈值时,将消息写入数据库并触发提醒。另外Redis不支持消息分区,相比于专业的MQ吞吐量会小,再一次说明了Redis作为队列适用轻量级场景。
这篇文章对Redis作为消息队列解析得非常透彻https://zhuanlan.zhihu.com/p/651758438
PHP对Redis作为队列的支持https://docs.golaravel.com/docs/9.x/queues#dealing-with-failed-jobs
5. Redis是CP模型还是AP模型?Redis是如何实现高可用的(Availability)?
Redis 默认采用异步复制,主节点写入后立即返回,从节点异步接收数据。在网络分区或主从延迟时,可能出现读到旧数据的情况,因此 Redis 更倾向于AP系统。
一般来说高可用都可以从以下这几个方面回答:数据冗余、故障转移(根据备份的数据或者状态机设计)、负载均衡、分布式系统设计。对于Redis而言有好几种高可用的机制:如果是主从复制,主负责写从负责读,则是提供了数据冗余和读扩展能力;如果是哨兵机制,则进一步提供了故障转移能力,哨兵具有监控、选主和通知这三个功能,选择leader并不是通过Paxos或者Raft,而是通过投票+简单多数的原则实现的,选出leader后由leader选出主节点完成故障转移/自动化修复,在投票-选举-选主整个过程也可以理解为状态机状态转移,而且这个期间Redis集群会有短暂写不可用的期间,读可用但数据不一致,印证了CAP理论;如果是Redis cluster,则是将数据分片存储在多个节点上,实现水平扩展,提供自动分区和故障转移。除了Redis本身之外的高可用则可以考虑业务上负载均衡、限流熔断等等的实现。
6. Redis单实例可承受的QPS?
主播没测过,问copilot得到的答案是:单实例 Redis 的 QPS 在简单读写操作下可以达到几十万到百万级别,通常受限于网络和内存带宽,而不是 CPU。实际生产环境中,稳定的 QPS 一般在几十万左右。如果需要更高吞吐量,可以通过 Redis Cluster 分片扩展。
7. Redis持久化
这篇文章解析得很详细https://www.cnblogs.com/xiaokang-coding/articles/18531836
有几个细节可能会被拷问:例如RDB的原理、AOF日志重写和混合持久化如果没被拷问知道也可以详细展开体现八股功力深厚(bushi
8. 在一个秒杀系统中,有一个消费券的库存ID,每秒可能有百万级别的访问,要怎么设计这个系统来确保库存ID不会超额扣减以及访问该数据的可用性/一致性?(用Redis作为缓存)
在秒杀系统中,用 Redis 存储库存 ID,并通过 DECR 命令保证单实例下的原子性扣减,避免超卖。如果采用 Redis Cluster,需要保证库存 ID 路由到同一个分片,否则可能出现读到旧数据的问题。对于百万级 QPS,通常要在入口层做限流和排队,结合消息队列异步消费,应用层用令牌桶减少 Redis 压力。同时采用异地多活和单元化部署,将库存和用户请求按单元划分,避免全局竞争。最终在数据库层做一次库存校验,保证数据一致性。