滴滴花小猪打车后端开发一面面经
《面试题目》
- 自我介绍
- Redis 的数据结构和使用场景有哪些?
- Redis 快的原因是什么?
- MySQL 的 B+ 树索引和哈希索引有什么区别?
- 什么情况下索引会失效?
- MySQL 的事务隔离级别有哪些?默认是哪个?
- MySQL 的幻读是什么现象?
- 可重复读隔离级别下会出现幻读吗?
- Java 的内存模型 JMM 划分了哪些区域?
- Java 里堆和方法区分别存放什么资源?
- 项目中 caffeine + Redis 的二级缓存是如何设计的?如何保证数据一致性?
- 本地缓存的 TTL 一般设置为多少?
- 秒杀场景中使用 Redis Lua 预扣库存 + 消息队列,为什么要用 Lua 脚本?
- 项目中的滑动窗口限流,Redis 是如何设计的?
- 滑动窗口限流中 Redis 存的内容是什么?
- 是否是根据时间戳去清理过期数据?
- 讲讲之前实习中具体做了什么工作?
- 项目中团队智能问答知识库是如何实现的?
《参考解析》
- Redis 快的原因:数据全部存于内存,避免磁盘 I/O;采用高效的数据结构(如跳表、压缩列表);单线程模型避免了线程上下文切换和锁竞争开销;I/O 多路复用(如 epoll)使单线程也能高并发处理网络请求。
- B+树索引 vs 哈希索引:B+树索引支持范围查询、排序、模糊匹配(前缀)等场景,叶子节点间通过链表连接便于顺序遍历;哈希索引只支持等值查询(=),不支持范围查询和排序,但等值查询的时间复杂度是 O(1),优于 B+树的 O(logN)。InnoDB 默认使用 B+树索引。
- 可重复读隔离级别下的幻读:标准定义中可重复读(REPEATABLE READ)理论上会出现幻读,但 InnoDB 通过 MVCC(快照读)+ 间隙锁(Gap Lock,当前读场景)的组合,在实际实现上很大程度避免了幻读,这也是面试中常被追问”MySQL 的可重复读和标准定义有何不同”的考点。
- Lua 脚本预扣库存的意义:秒杀场景中”判断库存是否充足”和”扣减库存”必须是原子操作,否则高并发下会出现超卖;Redis 单条命令是原子的,但多条命令组合执行时中间可能被其他请求插入。用 Lua 脚本把判断+扣减逻辑封装为一个脚本,利用 Redis 单线程执行脚本的特性保证整体操作的原子性,避免超卖。
- 滑动窗口限流的 Redis 设计:通常用 ZSet 存储请求记录,score 设为请求时间戳;每次请求先根据当前时间减去窗口大小,用
ZREMRANGEBYSCORE清理窗口外的旧记录,再用ZCARD统计窗口内剩余请求数判断是否超过阈值,通过后用ZADD写入当前请求的时间戳作为 member。