
个人主页代码不加冰欢迎来访作者简介java后端学习者❄️个人专栏LeetCode刷题日记 苍穹外卖日记SSM框架深入JavaWeb✨命运的结局尽可永在不屈的挑战却不可须臾或缺前言今天由于没有很多的时间然后又看到群友发的奥特曼的额度多的用不完所以跑了好几个项目玩了一玩搓写了个个人日记当然只是自己使用很简单然后还在claude code中接入了一个skill感觉还是挺实用的superpowers感兴趣的可以去搜一下可以提高分析项目或者处理项目的效率。如图这是一份面向“新手入门 老手复盘”的项目分析文档。新手可以跟着它理解项目怎么拆、代码怎么读有经验的同学可以用它复盘 Redis、缓存、秒杀、MQ、Feed 流、GEO、限流等面试高频点。1. 项目速览黑马点评是一个简化版的大众点评类项目核心业务围绕“本地生活服务”展开用户可以登录、浏览商铺、查看附近店铺、发布探店博客、点赞关注、查看关注博主动态也可以参与优惠券秒杀。项目项内容项目名称黑马点评 / hm-dianping项目类型简化版大众点评本地生活点评类应用后端框架Spring Boot 2.3.12.RELEASEJava 版本Java 8ORMMyBatis-Plus 3.4.3数据库MySQL缓存/中间件Redis、RabbitMQ网关/前端代理Nginx核心亮点登录鉴权、商铺缓存、GEO 附近商铺、博客点赞、关注 Feed、签到、优惠券秒杀、Lua 原子校验、MQ 异步下单1.1 这个项目适合学什么学习方向项目中的落地点适合人群Spring Boot Web 开发Controller、Service、Mapper 分层Java Web 新手MyBatis-Plus 实战实体、Mapper、ServiceImpl、分页插件想熟悉 ORM 的同学Redis 实战缓存、登录态、点赞、Feed、签到、GEO、限流准备 Redis 项目的同学高并发秒杀Lua、Redis 库存、RabbitMQ 异步下单、令牌桶限流面试复盘 / 项目亮点包装社交业务建模关注、共同关注、点赞排行榜、关注推送想理解内容社区模型的同学项目排错能力配置、端口、缓存一致性、MQ 消费有一定基础的后端开发2. 技术栈职责表这个项目不是简单地把技术堆在一起而是把 Redis 的多种数据结构放进了真实业务场景中这是它最值得复盘的地方。技术在项目中的作用适合复盘的问题Spring BootWeb 容器、接口开发、依赖注入一个后端项目如何分层MyBatis-Plus数据库 CRUD、分页、Service 基类如何快速完成表操作MySQL保存用户、商铺、博客、关注、优惠券、订单等核心数据哪些数据必须落库Redis String验证码、商铺缓存、秒杀库存、缓存空值如何解决缓存穿透Redis Hash登录 token 对应的用户信息token 登录态如何保存Redis List商铺类型缓存有序列表如何缓存Redis Set关注关系、一人一单记录如何做去重和交集Redis ZSET点赞排行榜、Feed 收件箱、验证码限流Feed 流和排行榜如何实现Redis Bitmap用户签到连续签到如何高效统计Redis GEO附近商铺查询附近的人/店怎么查Lua秒杀库存和一人一单原子校验如何防止超卖RabbitMQ秒杀订单异步落库为什么要削峰填谷Redisson分布式锁配置能力分布式锁如何接入Guava RateLimiter秒杀入口限流如何保护系统入口Nginx静态资源服务、接口代理前后端如何联调3. 项目目录与阅读顺序项目是单模块 Maven Spring Boot 应用主启动类位于src/main/java/com/hmdp/HmDianPingApplication.java通过MapperScan(com.hmdp.mapper)扫描 MyBatis Mapper。目录/文件职责新手阅读建议pom.xmlMaven 依赖和 Spring Boot 版本先看项目用了哪些技术src/main/java/com/hmdp/HmDianPingApplication.javaSpring Boot 启动类确认启动入口和 Mapper 扫描src/main/java/com/hmdp/controllerHTTP 接口入口先看接口知道每个业务从哪里进来src/main/java/com/hmdp/service业务接口快速了解有哪些业务能力src/main/java/com/hmdp/service/impl核心业务实现重点阅读Redis、MQ、缓存、秒杀都在这里src/main/java/com/hmdp/mapperMyBatis-Plus 数据访问层对照数据库表理解 CRUDsrc/main/java/com/hmdp/entity数据库实体对照hmdp.sql理解表结构src/main/java/com/hmdp/dto接口传输对象理解前后端交互数据结构src/main/java/com/hmdp/utilsRedis key、拦截器、ID 生成器、缓存工具等复盘通用技术点src/main/java/com/hmdp/configSpring MVC、MyBatis、Redisson、RabbitMQ 配置跑项目和排错时重点看src/main/resources/application.yaml后端运行配置检查 MySQL、Redis、RabbitMQ 连接src/main/resources/db/hmdp.sql数据库建表和初始化数据跑项目前先导入src/main/resources/seckill.lua秒杀 Lua 脚本理解 Redis 原子扣库存nginx-1.18.0本地 Nginx 代理与静态资源目录前端联调时查看3.1 推荐阅读顺序顺序阅读对象目标1README.md、pom.xml、application.yaml知道项目依赖和运行环境2Controller 包知道项目有哪些接口3Entity SQL知道业务表怎么设计4ServiceImpl 包理解每个业务怎么实现5Utils 包复盘 Redis key、拦截器、ID 生成器6seckill.lua RabbitMQ 相关类深挖秒杀链路4. 运行环境与基础配置配置项当前项目表现说明后端端口8081application.yaml中server.port8081MySQL127.0.0.1:3306/hmdp需要先导入src/main/resources/db/hmdp.sqlRedislocalhost:6379database6Redis 版本建议不低于 6.2因为 GEOSEARCH 需要 Redis 6.2RabbitMQhost 为192.168.88.128port 为15672这里需要注意15672通常是 RabbitMQ 管理后台端口Nginx监听8080代理/api/**到后端8081用于本地前后端联调如果你只是学习源码可以先读代码如果要完整跑通需要检查 MySQL、Redis、RabbitMQ、Nginx 的本地环境是否和配置一致。5. 核心业务模块拆解5.1 模块用户登录与鉴权解决的问题用户如何获取验证码。用户如何登录。后端如何识别当前请求是谁。登录态如何续期。用户签到如何记录和统计。核心文件文件作用UserController.java暴露验证码、登录、当前用户、用户信息、签到等接口UserServiceImpl.java实现验证码发送、登录、签到统计等逻辑RefreshTokenInterceptor.java根据 token 从 Redis 读取用户并刷新 TTLLoginInterceptor.java判断当前请求是否已登录UserHolder.java使用 ThreadLocal 保存当前用户RedisConstants.java维护登录相关 Redis key 前缀和 TTL核心流程用户请求验证码。服务端生成验证码。当前项目通过邮箱发送验证码。验证码写入 Redislogin:code:{email}。用户提交验证码登录。服务端校验 Redis 中的验证码。登录成功后生成 token。用户信息写入 Redis Hashlogin:token:{token}。前端后续请求携带 token。RefreshTokenInterceptor从 Redis 读取用户信息并刷新 TTL。LoginInterceptor判断当前用户是否存在不存在则拦截。技术复盘问题项目方案复盘关键词为什么不用 Session使用 token Redis更适合前后端分离和分布式部署无状态、共享登录态token 存在哪里Redis HashStringRedisTemplate.opsForHash()如何续期拦截器每次请求刷新 Redis TTL滑动过期如何避免未登录访问登录拦截器检查UserHolder拦截器链、ThreadLocal验证码如何限流使用 Redis ZSET 记录发送时间窗口并配合限制 key时间窗口、ZSET、TTL新手注意当前代码里很多命名仍然偏向phone但实际验证码发送使用的是邮箱能力。这一点适合在复盘中说明项目从短信登录语义改成了邮箱验证码但字段命名没有完全同步。5.2 模块商铺查询与缓存解决的问题用户如何查询商铺详情。商铺数据如何缓存。查询不存在的商铺时如何避免缓存穿透。修改商铺后如何保持缓存和数据库一致。核心文件文件作用ShopController.java商铺查询、新增、更新、按类型查询、按名称查询接口ShopServiceImpl.java商铺缓存、数据库查询、GEO 查询核心实现CacheClient.java通用缓存工具包含缓存穿透、互斥锁、逻辑过期等思路RedisConstants.java商铺缓存 key 和 TTL 常量核心流程查询商铺详情接收/shop/{id}请求。先查 Rediscache:shop:{id}。如果 Redis 有正常数据直接返回。如果 Redis 命中空字符串说明之前查过数据库且不存在直接返回空结果。如果 Redis 没有数据查询 MySQL。MySQL 查不到时向 Redis 写入空字符串设置较短 TTL防止缓存穿透。MySQL 查到时将商铺 JSON 写入 Redis设置正常 TTL。返回商铺信息。缓存策略复盘场景项目方案说明正常热点商铺Redis String 缓存 JSON降低数据库压力查询不存在商铺缓存空字符串解决缓存穿透商铺更新先更新数据库再删除缓存常见 Cache Aside 策略缓存重建代码中保留互斥锁、逻辑过期思路可作为扩展复盘点面试表达这个模块可以这样讲商铺详情是典型读多写少场景所以使用 Redis 缓存。查询时先查缓存缓存未命中再查数据库。为了防止恶意请求不存在 ID 导致缓存穿透数据库查不到时会缓存空值并设置较短过期时间。更新商铺时采用先更新数据库、再删除缓存的 Cache Aside 策略保证最终一致性。5.3 模块附近商铺 GEO 查询解决的问题用户如何查询附近商铺。如何按照距离排序。如何把 Redis GEO 查询结果和数据库详情结合起来。核心文件文件作用ShopController.java/shop/of/type接口入口ShopServiceImpl.java根据类型和坐标查询附近商铺HmDianPingApplicationTests.java测试/工具代码中包含商铺 GEO 数据预热逻辑RedisConstants.javashop:geo:key 前缀核心流程用户传入商铺类型、当前页、经纬度。如果没有经纬度走普通数据库分页查询。如果有经纬度使用 Redis GEO 查询shop:geo:{typeId}。Redis 返回指定范围内的商铺 ID 和距离。后端根据 ID 列表查询 MySQL 商铺详情。使用FIELD(id, ...)保持 Redis 返回的距离排序。将距离字段补充到商铺对象中返回。技术复盘问题项目方案复盘关键词GEO 数据怎么存按商铺类型分组存入shop:geo:{typeId}分组索引如何查附近商铺Redis GEOSEARCH地理位置搜索为什么还要查 MySQLRedis GEO 只适合存坐标和 member不适合存完整商铺详情Redis DB 组合查询如何保持距离排序SQL 中使用FIELD(id, ...)顺序保持5.4 模块商铺类型缓存解决的问题首页商铺分类如何加载。排序后的类型列表如何缓存。简单列表数据如何用 Redis 缓存。核心文件文件作用ShopTypeController.java/shop-type/list接口入口ShopTypeServiceImpl.java查询商铺类型并缓存RedisConstants.java商铺类型缓存 key核心流程查询 Redis Listcache:shoptype:。如果 Redis 中有数据将 JSON 反序列化为类型列表。如果 Redis 没数据查询数据库。数据库按sort字段排序。将结果逐个写入 Redis List。返回商铺类型列表。技术复盘问题项目方案适合复盘点为什么用 List商铺类型是有序列表保序缓存为什么不是 Hash查询通常一次性返回全部类型简化读取如何避免每次查 DBRedis 缓存分类列表热点基础数据缓存5.5 模块博客、点赞与 Feed 流解决的问题用户如何发布探店博客。热门博客如何查询。用户如何点赞/取消点赞。点赞用户排行榜如何展示。关注博主后如何收到新博客推送。核心文件文件作用BlogController.java博客发布、点赞、查询、Feed 接口入口BlogServiceImpl.java博客发布、点赞、排行榜、Feed 流核心逻辑FollowServiceImpl.java关注关系维护ScrollResult.javaFeed 滚动分页返回对象RedisConstants.java点赞和 Feed 相关 key点赞流程用户点击点赞。查询 Redis ZSETblog:liked:{blogId}。如果当前用户不在 ZSET 中说明未点赞。数据库博客点赞数加一。用户 ID 写入 ZSETscore 使用当前时间戳。如果当前用户已在 ZSET 中说明已点赞。数据库博客点赞数减一。从 ZSET 移除用户 ID。Feed 推送流程用户发布博客。博客先保存到 MySQL。查询当前作者的所有粉丝。将新博客 ID 推送到每个粉丝的 Feed ZSETfeed:{fanUserId}。粉丝查看关注动态时从自己的 Feed ZSET 按时间倒序滚动分页。技术复盘问题项目方案复盘关键词点赞为什么用 ZSET既能判断是否点赞也能按时间排序去重 排序点赞排行榜怎么做ZSET score 存点赞时间戳TopN、时间顺序Feed 流采用什么模式推模式发布时写入粉丝收件箱写扩散Feed 为什么用滚动分页按时间戳滚动查询避免传统分页重复/遗漏Scroll Pagination推模式和拉模式对比模式做法优点缺点适用场景推模式发布时推给粉丝收件箱读很快大 V 写入压力大普通用户社交动态拉模式读取时查询关注列表再聚合写很轻读很重关注量少或实时性要求低推拉结合普通用户推大 V 拉平衡读写实现复杂大型内容社区当前项目使用的是推模式逻辑更直观也适合教学项目。5.6 模块关注与共同关注解决的问题用户如何关注/取关其他用户。如何判断是否已关注。如何查询共同关注。核心文件文件作用FollowController.java关注、取关、共同关注接口FollowServiceImpl.java关注关系数据库写入和 Redis Set 维护Follow.java关注关系实体核心流程关注用户用户点击关注。后端向tb_follow写入关注关系。同时将被关注用户 ID 写入 Redis Setfollows:{userId}。判断共同关注时对两个用户的 Set 做交集。根据交集中的用户 ID 查询用户信息。技术复盘问题项目方案复盘关键词关注关系为什么要落库关系数据需要持久化MySQL 主存储为什么还要写 Redis Set共同关注需要高效交集计算Set Intersection取关怎么处理删除数据库记录同时从 Redis Set 移除双写一致性5.7 模块优惠券与秒杀下单解决的问题商家如何发布优惠券。秒杀库存如何控制。高并发下如何防止超卖。如何保证一人只能下一单。如何降低数据库写入压力。核心文件文件作用VoucherController.java普通券、秒杀券发布和查询接口VoucherServiceImpl.java新增秒杀券并初始化 Redis 库存VoucherOrderController.java秒杀下单接口入口VoucherOrderServiceImpl.java秒杀入口限流、Lua 调用、订单消息发送seckill.luaRedis 原子判断库存、一人一单、扣库存RabbitMQTopicConfig.java秒杀队列、交换机、绑定关系配置MQSender.java发送秒杀订单消息MQReceiver.java消费秒杀订单消息并写入数据库RedisIdWorker.java生成全局唯一订单 ID秒杀链路总表阶段做了什么使用技术请求入口接收秒杀请求Controller入口限流控制瞬时请求量Guava RateLimiter原子校验判断库存、一人一单、扣 Redis 库存Lua记录用户将用户加入已下单集合Redis Set生成订单 ID生成全局唯一 IDRedis 自增 时间戳异步下单发送订单消息RabbitMQ数据落库扣 DB 库存、保存订单MySQL 事务Lua 脚本做了什么seckill.lua的核心职责是把多个 Redis 操作合并成一个原子操作。判断Redis 数据失败返回目的库存是否充足seckill:stock:{voucherId}1防止库存不足还继续下单用户是否下过单seckill:order:{voucherId}2防止一人多单扣减库存incrby stockKey -10Redis 侧预扣库存记录用户sadd orderKey userId0标记用户已参与为什么要用 Lua不用 Lua 的问题Lua 的价值判断库存、判断是否下单、扣库存、记录用户是多个命令Lua 脚本在 Redis 中原子执行多线程并发下可能出现竞态Redis 单线程执行脚本天然串行Java 端加锁性能较差分布式部署复杂把并发控制前移到 Redis为什么要用 RabbitMQ没有 MQ使用 MQ 后秒杀请求直接打到数据库请求先在 Redis 完成资格判断数据库承受瞬时高并发写入订单写入异步消费削峰填谷用户等待完整下单事务结束用户更快拿到“排队成功/下单中”结果系统峰值能力受 DB 限制明显Redis MQ 承担入口压力面试表达这个模块可以这样讲秒杀接口入口先通过令牌桶做限流避免瞬时流量直接打满系统。通过 Lua 脚本在 Redis 中原子完成库存判断、一人一单判断、库存扣减和用户记录保证不会在 Redis 层超卖。校验成功后生成订单 ID并将订单消息发送到 RabbitMQ由消费者异步扣减数据库库存并保存订单从而实现削峰填谷保护数据库。5.8 模块签到统计解决的问题如何记录用户每天是否签到。如何统计用户连续签到天数。如何避免一人一天一条签到记录导致数据量过大。核心文件文件作用UserController.java签到和连续签到统计接口UserServiceImpl.javaBitmap 签到逻辑RedisConstants.java签到 key 前缀核心流程用户请求签到。根据用户 ID 和当前年月构造 Redis keysign:{userId}:{yyyyMM}。以当月第几天作为 bit 位偏移。使用 Bitmap 将当天标记为已签到。统计连续签到时读取从月初到今天的 bitmap。从低位开始连续判断 1 的个数。技术复盘问题项目方案复盘关键词为什么用 Bitmap一个月最多 31 位即可表示每天是否签到空间极小如何按月隔离key 中拼接年月yyyyMM如何统计连续签到位运算判断连续的 1Bit Operation5.9 模块文件上传与 Nginx 代理解决的问题博客图片如何上传。前端静态页面如何通过 Nginx 访问后端接口。核心文件文件作用UploadController.java博客图片上传、删除接口SystemConstants.java上传路径等系统常量nginx-1.18.0/conf/nginx.confNginx 静态资源和接口代理配置Nginx 联调思路请求处理方式静态页面请求Nginx 从本地 html 目录读取/api/**请求Nginx 代理到后端127.0.0.1:8081后端接口Spring Boot 应用处理新手注意当前仓库中包含 Nginx 目录和配置但完整前端页面目录可能并不完整。如果你要跑前端需要确认nginx-1.18.0/html/hmdp这类静态资源目录是否存在。6. Redis 在项目中的使用总表这个项目最适合用来复盘 Redis因为它几乎覆盖了常见数据结构在业务中的落地方式。Redis 数据结构项目场景Key 示例价值String验证码、商铺缓存、秒杀库存、缓存空值login:code:{email}、cache:shop:{id}、seckill:stock:{voucherId}简单 KV、TTL、缓存穿透处理Hashtoken 登录态login:token:{token}保存用户字段便于续期和读取List商铺类型缓存cache:shoptype:保存有序分类列表Set关注关系、一人一单记录follows:{userId}、seckill:order:{voucherId}去重、交集计算ZSET点赞排行榜、Feed、验证码限流blog:liked:{blogId}、feed:{userId}、sms:sendtime:{email}排序、滚动分页、时间窗口Bitmap用户签到sign:{userId}:{yyyyMM}高效记录连续签到GEO附近商铺shop:geo:{typeId}地理位置搜索6.1 Redis key 设计复盘Key 前缀业务含义数据结构login:code:登录验证码Stringlogin:token:登录用户信息Hashcache:shop:商铺详情缓存Stringcache:shoptype:商铺类型列表Listlock:shop:商铺缓存重建锁Stringseckill:stock:秒杀券 Redis 库存Stringseckill:order:秒杀券已下单用户集合Setblog:liked:博客点赞用户排行ZSETfeed:用户关注 Feed 收件箱ZSETshop:geo:商铺地理位置索引GEOsign:用户签到记录Bitmapsms:sendtime:验证码发送时间窗口ZSETlimit:onelevel:/limit:twolevel:验证码登录限制String / Set 语义限制 key7. 重点技术方案深挖7.1 缓存穿透问题说明什么是缓存穿透请求的数据在 Redis 和数据库都不存在导致每次请求都会打到数据库项目怎么做数据库查不到商铺时向 Redis 写入空字符串为什么设置短 TTL防止空值长期占用同时允许后续真实数据创建后恢复还能怎么优化增加布隆过滤器、参数校验、热点保护7.2 缓存更新策略策略项目使用情况说明先更新数据库再删除缓存商铺更新使用该思路常见 Cache Aside 模式互斥锁重建缓存代码中有相关实现思路适合热点 key 失效重建逻辑过期代码中有相关实现思路适合高可用场景牺牲短暂一致性换可用性7.3 Redis ID 生成器组成作用时间戳部分保证 ID 趋势递增Redis 自增序列保证同一时间内不重复业务前缀区分不同业务 ID这种方式适合分布式场景下生成全局唯一 ID比单机自增更适合多实例部署。7.4 限流设计限流位置项目方案目的验证码发送Redis ZSET 时间窗口 限制 key防止频繁发送验证码秒杀入口Guava RateLimiter防止高并发瞬时打爆服务限流的核心不是让所有请求都成功而是让系统在高压下仍然可控。8. 项目坑点与优化建议这部分很适合老手复盘也适合面试时主动补充“我不仅会用还知道它的问题在哪里”。问题当前表现影响优化建议RabbitMQ 端口疑似配置错误application.yaml中配置为1567215672通常是管理后台端口AMQP 常用5672本地启动时确认 RabbitMQ AMQP 端口rebbitmq包名拼写异常RabbitMQ 包路径命名为rebbitmq不影响运行但影响可读性统一重命名为rabbitmq登录字段命名不一致接口/字段偏phone实际使用邮箱验证码新手阅读时容易误解统一改为 email或恢复短信登录语义登出接口未实现/user/logout返回失败/占位登录态无法主动删除删除 Redis token 并返回成功秒杀时间未校验Lua/Java 秒杀路径未判断开始、结束时间非活动时间也可能进入秒杀逻辑在入口查询活动时间或在 Redis 中预热时间并校验一人一单缺少唯一索引依赖 Redis Set 和 DB 查询判断极端情况下 DB 层没有最后防线给tb_voucher_order(user_id, voucher_id)加唯一索引Redis 与 DB 一致性边界Redis 预扣成功后MQ/DB 失败可能不一致库存和订单状态可能偏差加消息确认、失败补偿、对账任务或库存回滚GEO 数据需要预热GEO 数据主要依赖测试/工具逻辑加载新环境可能查不到附近商铺增加启动预热任务或后台管理同步能力Nginx 前端资源可能不完整仓库有 Nginx但完整html/hmdp目录可能缺失前端页面可能无法直接访问补齐前端资源或在 README 中说明来源上传路径硬编码图片上传目录是本机绝对路径换机器后容易失败改为配置项并区分本地/生产环境配置存在明文账号密码application.yaml写有本地账号密码公开部署存在安全风险用环境变量、私密配置或配置中心9. 新手学习路线阶段学习目标建议阅读内容第 1 阶段跑通项目README.md、application.yaml、hmdp.sql第 2 阶段看懂接口入口所有 Controller第 3 阶段理解基础 CRUD商铺、用户、博客基础查询第 4 阶段学 Redis 缓存ShopServiceImpl、ShopTypeServiceImpl、CacheClient第 5 阶段学登录鉴权UserServiceImpl、两个 Interceptor、UserHolder第 6 阶段学社交业务关注、点赞、Feed 流第 7 阶段学高并发秒杀VoucherOrderServiceImpl、seckill.lua、RabbitMQ 发送/消费第 8 阶段做项目复盘总结缓存、限流、Lua、MQ、一致性问题9.1 新手不要一上来就钻秒杀推荐先按这个顺序学先看普通 CRUD比如商铺查询、用户信息查询。再看 Redis 缓存比如商铺详情缓存。再看登录鉴权理解 token、Redis Hash、拦截器、ThreadLocal。然后看点赞、关注、Feed理解 Redis ZSET 和 Set。最后看秒杀因为秒杀同时涉及限流、Lua、Redis、MQ、事务和一致性。10. 老手面试复盘表高频问题回答关键词项目落地点这个项目的核心亮点是什么Redis 多数据结构实战、高并发秒杀、Feed 流、GEO商铺、博客、秒杀、签到模块如何解决缓存穿透缓存空值、短 TTL、布隆过滤器可扩展商铺详情缓存缓存和数据库如何保持一致先更新 DB再删除缓存最终一致性商铺更新如何防止秒杀超卖Lua 原子判断库存并扣减DB CAS 扣库存seckill.lua、订单消费如何防止一人多单Redis Set 记录下单用户DB 查询校验建议加唯一索引seckill:order:{voucherId}为什么使用 Lua多个 Redis 命令合并为原子操作秒杀资格校验为什么使用 RabbitMQ异步下单、削峰填谷、保护数据库秒杀订单消息Redis 扣库存成功但 MQ 失败怎么办需要消息确认、补偿、对账、回滚策略当前项目可优化点Feed 流怎么实现推模式Redis ZSET 做用户收件箱feed:{userId}点赞排行榜怎么实现ZSETscore 存时间戳blog:liked:{blogId}附近商铺怎么实现Redis GEO MySQL 详情查询shop:geo:{typeId}签到怎么实现Bitmap 按月记录sign:{userId}:{yyyyMM}验证码如何限流ZSET 时间窗口 限制 keysms:sendtime:{email}11. 一句话总结每个模块模块一句话总结用户登录token Redis Hash 拦截器实现分布式登录态商铺缓存Redis 缓存热点数据空值解决缓存穿透附近商铺Redis GEO 负责距离搜索MySQL 负责详情数据商铺类型Redis List 缓存有序分类数据博客点赞Redis ZSET 同时实现是否点赞和点赞排行榜关注 Feed发布博客时推送到粉丝 ZSET 收件箱共同关注Redis Set 交集快速计算共同关注用户秒杀下单RateLimiter Lua Redis RabbitMQ DB 事务组成高并发链路签到Bitmap 用极小空间记录月度签到Nginx 代理前端静态资源和后端 API 通过 Nginx 联调12. 项目价值与复盘建议这个项目的最大价值不在于业务复杂而在于它把 Redis 的常见能力串进了多个真实业务场景中Redis 能力业务场景缓存 TTL商铺详情、验证码空值缓存缓存穿透Hash登录态Set关注、一人一单ZSET点赞排行、Feed、时间窗口限流Bitmap签到GEO附近商铺Lua秒杀原子校验如果你是新手建议把它当成 Redis 实战项目来学如果你准备面试建议重点讲清楚三条线缓存线商铺缓存、缓存穿透、缓存更新。社交线点赞、关注、Feed 流。高并发线限流、Lua、Redis 预扣库存、RabbitMQ 异步下单、DB 最终落库。最后复盘项目时不要只讲“我用了 Redis 和 MQ”更好的表达是我在不同业务场景下选择了不同 Redis 数据结构String 做缓存和库存Hash 做登录态Set 做关注和一人一单ZSET 做点赞排行和 FeedBitmap 做签到GEO 做附近商铺。秒杀场景下为了避免超卖和一人多单使用 Lua 保证 Redis 校验和扣减的原子性再通过 RabbitMQ 异步落库达到削峰和保护数据库的目的。同时我也意识到项目还有一致性补偿、唯一索引、秒杀时间校验、配置安全等可以继续优化的点。