黑马点评复盘

发布时间:2026/6/8 0:05:53

黑马点评复盘 文章中只是部分摘要建议大家去看原文档笔记文档地址https://wwzdcv5m5b4.feishu.cn/wiki/Djy3wb2XNiZiMRkKlA4cxYr9nkb?fromfrom_copylink一、登录状态管理从“单店会员卡”到“全国一卡通”在传统的单体应用中我们习惯把用户登录状态存在 Tomcat 的 Session 里。但这就像办了一张“单店会员卡”一旦你的服务扩展成集群用户被负载均衡路由到另一台机器状态就丢失了导致用户频繁掉线。解决方案与落地痛点为了解决集群下的状态失效我们把登录信息抽离出来存到独立的 Redis 空间中。这就像把单店系统升级成了云端的“全国一卡通”。第一步是发送验证码校验手机号后生成6位随机码以 phone 为 key 存入 Redis 并设置有效期。第二步是登录校验拿用户输入的 code 与 Redis 中的 cacheCode 比对一致则通过并将用户信息存入 Redis 辅助后续权限校验。工程实战中的巧思双层拦截器这里有个极易踩坑的点并非所有接口都需要登录拦截但未登录的访问比如逛首页也需要刷新 Session 存活时间。因此我们不能一刀切而是设计了双层拦截器TokenRefreshInterceptor拦截所有路径/专门负责给已经携带有效 Token 的用户续期。LoginInterceptor只拦截特定需要权限的路径如发帖、点赞检查是否真的登录了没登录则拦截。 最后通过这两层拦截器协同就能优雅地对不同请求进行精准处理。二、缓存设计与“三大皆空”的防线为了提高商户信息的查询速度我们会把数据存进 Redis。但数据库是活的随时会改这就带来了数据不一致的风险。实战经验更新数据库时一定要先更新数据库再删除 Redis 缓存。下次访问自然会去 DB 查最新数据并重建缓存。但引入缓存后我们还得面对面试官最爱问的“缓存三大坑”。我把它们总结在一张表里异常场景痛点描述 (工程体感)应对方案缓存穿透攻击者用大量数据库根本不存在的恶意 ID 狂刷接口请求直接“穿透”缓存打到 DB。方案 A缓存短期的空值拦截在 Redis 层但大量随机攻击会耗费 Redis 内存。方案 B布隆过滤器预计算合法 key 指纹但有误判率。缓存击穿某个超级爆款的“热点 Key”突然失效导致海量并发请求同时去查 DB 试图重建缓存。方案 A互斥锁只让第一个拿到锁的线程去查库其余等待适合一致性要求高的场景。方案 B逻辑过期数据永不主动过期发现逻辑时间超时后尝试获取分布式锁异步更新。缓存雪崩大量缓存 Key 同时过期或者 Redis 直接宕机请求如雪崩般压垮 DB。基础 TTL 上加上一个 random(0, 300) 秒的随机时间避免扎堆。如果是分布式架构还需配合 Sentinel 限流、Hystrix 熔断以及多级缓存。三、秒杀架构优化把“作坊手工”变成“流水线作业”爆款优惠券秒杀是检验架构功底的试金石。优化前的朴素方案作坊手工 一个简单的秒杀逻辑通常包含查库存 - 判重复 - 扣库存 - 插订单。每一次请求需要进行 3 次 DB 读和 2 次 DB 写总共 5 次 DB 操作。如果来了 10000 并发那就是 50000 次操作MySQL 会直接崩溃。此外异步并发读写还会导致库存出现负数超卖现象。优化后的四层异步架构流水线作业 怎么解咱们得把同步阻塞变成异步解耦。第一道防线Lua 脚本原子校验。把最关键的“判库存 判重复 扣库存 记用户”打包成一个原子操作在 Redis 内存里极速执行拦截无效请求。优化后响应时间从 ~200ms 降到了 ~0.5ms快了成百上千倍。第二道防线全局 ID 生成器。由于不能阻塞等待 MySQL 返回自增 ID我们用 Redis 的自增特性拼接秒级时间戳预先生成一个 64 位的高性能全局唯一订单 ID。第三层解耦立即返回响应。一旦 Lua 校验通过不管后台怎么处理接口层面立刻给用户返回“秒杀成功”绝不让用户干等 DB 写入。第四层削峰Kafka 队列与幂等校验。把订单发往 Kafka前端的海量请求化作队列里匀速滴下的水滴保护了 DB 永远不被冲垮。写 DB 时利用乐观锁WHERE stock 0做最终兜底防超卖。同时利用count(user_id, voucher_id)做幂等检查防止 Kafka 重复投递导致的一人多单。四、灵魂拷问你真的懂分布式锁吗在处理缓存击穿时我们引入了 Redis 分布式锁setIfAbsent。但在复杂的工程环境中这把锁其实危机四伏。危机 1锁被误删。你抢到锁但业务执行太慢锁到期自动释放了。别人拿到了新锁结果你业务执行完了随手一个del把别人的锁给删了。解法用 Lua 脚本把“判断是不是我的锁”和“删除”变成原子操作。危机 2不可重入死锁。同一个线程里的方法 A 加了锁调用方法 B 时又想加同一把锁结果自己把自己锁死了。解法利用 Redis 的 Hash 结构不仅存线程标识还存一个重入次数用 Lua 脚本增加锁的深度。危机 3业务没跑完锁没了。这就好比上厕所规定只能用 10 分钟但你拉肚子需要 15 分钟10 分钟一到门开了全乱套了。解法引入看门狗Watchdog机制。开启一个定时任务只要你的线程还活着它就每隔一段时间比如 TTL/3自动帮你把锁的有效期续满。如果你的服务器宕机挂了看门狗自然也就停止续期锁到期自动释放完美避免死锁。灵魂伪代码看看看门狗是如何运作的Java// 每 timeout/3 秒续期一次 renewalTask WATCHDOG.scheduleAtFixedRate(() - { if (unlocked) return; // 已经释放就停止 // 执行 Lua 脚本如果这把锁还是我的就重置过期时间 (EXPIRE) Long result stringRedisTemplate.execute(RENEWAL_SCRIPT, ...); if (执行失败) { renewalTask.cancel(false); } // 锁已掉取消续期任务 }, interval, interval, TimeUnit.SECONDS);架构设计从来都不是纸上谈兵每一个精妙的方案背后都是无数次系统崩溃带来的血泪教训。希望这份干货总结能帮你少走一些弯路。

相关新闻