今日八股:说反的生命周期补回来了,还有我面试栽过的缓存击穿

今日八股:说反的生命周期补回来了,还有我面试栽过的缓存击穿

题目一:Stream,Optional 到底该放哪

手上有 List<List<Integer>> nested。想把每个小 List 里的数字拍平倒进一个 List<Integer>,中间操作加哪个方法?接着去重、再从小到大排,分别用哪两个方法?一批 User 有 dept 和 name,想按部门分组,collect(...) 里传什么、返回的 Map 是什么类型?换个需求只数每个部门几个人,第二层统计怎么写,value 又是什么类型?

我答的是 flatMap 拍平,distinct 去重,sorted 排序。分组传 Collectors.groupingBy(User::getDept),返回 Map<String, List<User>>;只数人数就在第二层加一个 Collectors.counting(),返回 Map<String, Long>。

助手补了两处。map 和 flatMap 的区别是一对一和一对多再拍平,所以 List<List<T>> 要用 flatMap(List::stream)。groupingBy 的第二个参数叫下游收集器,counting 只是其中一个,常用的还有 mapping 和 summingInt;另外它默认返回 HashMap,想按插入顺序保留要传 LinkedHashMap::new,想按 key 排序传 TreeMap::new。

同一天还问了 Optional 的两个方法,有值就处理用 ifPresent,空了直接抛用 orElseThrow,这两个我都答住了。第二问是为什么不推荐把 Optional 当类的字段或者方法参数。我答的是它的设计初衷就是当方法返回类型、调用方再判断会多包一层。助手指了两条更硬的:Optional 没实现 Serializable,当 DTO 或者实体字段的时候序列化会直接报错;当参数又起不到任何约束作用,调用方照样能传 null 或者 empty,签名只会更啰嗦。

题目二:昨天说反的那组,今天补回来了

父组件里套了个子组件,两个都写了 onMounted。挂载的时候父和子的 onMounted 谁先跑?把从 beforeMount 到 mounted 的完整顺序说一遍。父的 onMounted 里能用 ref 直接拿到子组件实例吗,为什么?切路由把这俩一起卸载时,onUnmounted 的顺序跟挂载是同一套规则,还是整个反过来?

昨天这组我说反了,今天四项顺序全说对:父 beforeMount、子 beforeMount、子 onMounted、父 onMounted。父的 onMounted 里能拿到子组件实例,因为这时候子已经挂载好了。卸载是父 beforeUnmount、子 beforeUnmount、子 unmounted、父 unmounted。

助手补的是规则本身:从头到尾只有一条,开始类的钩子父先、完成类的钩子子先。不是"卸载整个反过来",我昨天那么记迟早还会错。

同一天另外两题也都过了。盒模型那题,100 加上左右各 10 的 padding、左右各 1 的 border,总宽 122px,改成 border-box 之后总宽就是 100px。助手补了个追问点,border-box 下内容区其实只剩 78px,问"设完之后内容还能放多宽",很容易顺口答 100。拷贝那题,const b = a 复制的是引用,所以 a.n 是 2;想互不影响用 structuredClone。助手顺手补了它的边界:函数和 DOM 节点不行,原型链也不保留,所以不能当万能替身。

题目三:一条慢 SQL,怎么事后揪出来

线上某个接口偶尔卡一下,等你连上数据库它又好了,EXPLAIN 也没法重放。MySQL 有个开关会把执行超过阈值的 SQL 记进一个文件,事后能翻,这个功能叫什么?那个阈值参数叫什么、默认多少秒?找到了那条慢 SQL,EXPLAIN 一看 rows 很大、Extra 里写着 Using filesort,说明什么、怎么优化?

我答了慢查询日志,默认是关的,阈值 10 秒。两个参数的名字没答上来,助手补的:开关叫 slow_query_log,阈值叫 long_query_time。

后半题答得顺:rows 很大说明要扫的行数多,Using filesort 说明没法利用索引的有序性直接拿到排序结果,得额外排一次。优化方向是加联合索引,让过滤和排序都走同一个索引。助手补了覆盖索引和调整 ORDER BY 方向这两条。

题目四:InnoDB 的三种日志

一条 UPDATE 改完数据先落在内存里,怕还没刷进磁盘就断电,所以会先写一份日志留底,这份叫什么、作用一句话。事务中途 ROLLBACK 能撤回去靠的是哪种日志,它记的是这行原来长什么样还是改成了什么样?还剩一个 binlog,它和上面两种最大的区别是什么?

三问我答的是:redo log,保证事务持久性,宕机后能把已提交还没刷进磁盘的数据恢复出来;undo log,记的是这行原来长什么样,也就是旧版本数据;binlog 是 Server 层的逻辑日志,主要给复制和恢复用,redo 和 undo 是 InnoDB 引擎自己的日志,一个管持久性,一个管回滚和 MVCC。

助手补了一个加分点:redo 和 binlog 的一致性靠两阶段提交保证,这是 crash-safe 的根本原因,也是面试官在"三种日志"上最爱追的一层。

题目五:缓存的三个挨打场景

有人拿一个数据库里根本不存在的 key 疯狂请求,每次都绕过 Redis 直接压到 MySQL,这叫什么、常见怎么防?某个热点 key 到期的瞬间,成百上千个请求一起发现缓存没了、一起冲去查 MySQL,这叫什么、思路怎么解?

穿透那题我答的是缓存空值,查不到也往 Redis 里写一个空结果、设个短过期;再加上参数校验把非法参数直接拦掉,限流、黑名单、风控。助手补了最常被追问的一个答案:布隆过滤器。

击穿那题我答的是加互斥锁或者分布式锁,只让一个请求去查数据库重建缓存,其他等着;热点 key 也可以逻辑过期,不设物理过期,后台异步更新。

第三块题里没问,我顺手答了缓存雪崩,大量 key 一起过期,或者 Redis 整体挂掉,请求全打到 MySQL,解法是过期时间加随机值、Redis 高可用、多级缓存、限流降级。

这道题对我有点特殊,击穿我以前面试被问过,当时没答上来,今天算是补上了。

← 返回文章列表