快手Java后端一面面经
《面试题目》
- 算法二选一:最长无重复字符子串 / 删除链表倒数第 k 个节点(选择了后者,中间卡顿但最终 AC)
- MVCC 解决了什么问题
- 既然已经有锁机制,为什么还需要 MVCC
- 一共有哪几种隔离级别,各自解决了什么问题
- RR(可重复读)级别解决了幻读吗
- 讲解一下什么是索引
- 讲解一下 B+树
- 索引失效的情况有哪些
- 如果对性别字段建立索引,查找的时候会走这个索引吗
- 性别字段走索引和全表扫描哪个更优
- 慢 SQL 优化手段
- RabbitMQ 的结构
- 消息有哪些路由模式
- 消息确认机制
- RabbitMQ 持久化策略
- Spring 怎么解决循环依赖
- 有哪些依赖注入的方式,以及有什么区别,推荐用哪一个
《参考解析》
- MVCC 与锁的关系:锁通过阻塞实现读写互斥,会牺牲并发性能;MVCC(多版本并发控制)通过为每行数据维护多个历史版本(配合 undo log 和 Read View),让读操作不加锁也能读到某个时间点的一致性快照,从而实现读写不冲突,大幅提升并发读的性能,二者是互补而非替代关系(写操作依然需要加锁)。
- RR 隔离级别与幻读:InnoDB 的可重复读级别通过 MVCC(快照读场景)+ 间隙锁/临键锁(当前读场景,如
select ... for update)共同作用,基本解决了幻读问题,但并非在所有场景下都能 100% 杜绝(例如快照读之后紧接当前读仍可能读到新插入的行)。 - 索引失效常见场景:对索引列做函数运算/隐式类型转换、使用
!=/<>、like '%xx'前置模糊匹配、or连接非索引列、列的区分度过低(如性别字段只有男女两个值,全表扫描往往比走索引更快,因为回表成本更高)。 - Spring 循环依赖:通过三级缓存机制解决——一级缓存存完全初始化好的单例 Bean,二级缓存存提前暴露的半成品 Bean,三级缓存存 ObjectFactory 用于生成代理对象;Bean 实例化后立即暴露到三级缓存,属性注入阶段如果发现循环依赖,直接从三级缓存中取出(可能生成代理)放入二级缓存,从而打破循环。