目录/blog/hot接口优化笔记一、优化背景与核心问题二、核心优化思路重中之重三、具体优化方案与思考按优先级排序优化1调整Redis数据结构最彻底、性价比最高1. 问题根源2. 优化思路3. 解决方案4. 思考为什么这样做优化2新增缓存缓存公共数据减轻数据库压力1. 问题根源2. 优化思路3. 解决方案4. 思考为什么这样做优化3解决N1查询问题减少数据库交互1. 问题根源2. 优化思路3. 解决方案4. 思考为什么这样做优化4适配单博客查询queryBlogById保证逻辑闭环1. 问题根源2. 优化思路3. 解决方案4. 思考为什么这样做四、关键误区与反思重点记五、优化效果与总结1. 优化效果2. 核心总结/blog/hot接口优化笔记今日核心学习内容针对/blog/hot热门博客列表接口的性能优化围绕“减少数据库/Redis压力、提升接口响应速度”展开重点掌握优化思路、问题根源及解决方案理解每一步优化的底层逻辑而非单纯记忆代码。一、优化背景与核心问题优化前的/blog/hot接口核心功能是查询热门博客列表按点赞数降序并返回每条博客的关联用户信息、当前用户的点赞状态。通过分析代码和实际场景发现3个核心性能瓶颈也是优化的核心靶心无缓存兜底每次请求都直接查询数据库/blog/hot作为热点接口高并发下会导致数据库连接耗尽接口响应时间达到秒级严重影响用户体验N1查询灾难查询博客列表后循环调用queryBlogUser方法逐个查询每条博客的关联用户1次查博客N次查用户N每页博客数数据库交互次数过多Redis交互效率低点赞状态查询isBlogLiked方法循环逐个查询Redis每次查询都触发一次网络请求虽Redis性能优异但高并发下网络开销累积会影响接口速度数据结构不合理初始采用“博客维度ZSet”存储点赞信息keyblog:liked:{blogId}memberuserId贴合“博客查用户”的逻辑但接口核心需求是“当前用户查多博客点赞状态”数据结构与查询需求不匹配。核心优化目标降低数据库/Redis压力将接口响应时间从秒级降至50ms以内保证高并发下的稳定性和数据准确性。二、核心优化思路重中之重优化的核心逻辑用缓存减轻数据库压力用批量操作减少网络交互用合理的数据结构匹配查询需求同时区分“公共数据”和“个性化数据”兼顾性能与数据准确性。核心思考接口优化不是“盲目加缓存”而是先定位瓶颈再针对性解决——数据库压力大就用缓存兜底网络交互多就用批量操作数据结构不合理就调整结构每一步优化都要明确“为什么做”而非“怎么做”。三、具体优化方案与思考按优先级排序优化1调整Redis数据结构最彻底、性价比最高1. 问题根源初始用“博客维度ZSet”存储点赞信息适合“查询某篇博客的所有点赞用户”但接口需求是“查询当前用户对当前页所有博客的点赞状态”属于“用户→博客”的查询数据结构与查询需求反向导致查询效率低。2. 优化思路将Redis数据结构从“博客维度ZSet”改为“用户维度Set”贴合接口核心查询需求用Set存储“用户点赞的所有博客ID”keyuser:liked:{userId}memberblogId这样查询当前用户对多博客的点赞状态时可通过一次Redis交集查询搞定无需循环。3. 解决方案1修改点赞/取消点赞逻辑updateLike方法点赞将博客ID加入当前用户的Set集合stringRedisTemplate.opsForSet().add(key, blogIdStr)取消点赞将博客ID从当前用户的Set集合中移除stringRedisTemplate.opsForSet().remove(key, blogIdStr)补充空指针防护判断当前用户是否登录避免未登录用户操作导致空指针。2优化点赞状态查询逻辑批量isBlogLiked方法收集当前页所有博客ID转为Set集合用Redis的intersect方法查询“用户点赞的博客ID”与“当前页博客ID”的交集一次Redis请求搞定批量判断遍历博客列表填充点赞状态交集包含博客ID则为已点赞否则未点赞。4. 思考为什么这样做① 数据结构贴合查询需求接口核心是“用户查多博客点赞状态”用户维度Set直接存储用户点赞的博客ID查询时无需遍历一次交集操作即可获取结果② 简化操作、减少内存占用Set无需存储scoreZSet的score用于记录时间戳此处无意义操作更轻量一个用户点赞几百条博客Set内存占用≤1KB完全可控③ 为后续批量查询奠定基础Set的intersect方法天然支持批量判断解决了原ZSet循环查询的网络开销问题。优化2新增缓存缓存公共数据减轻数据库压力1. 问题根源/blog/hot作为热点接口每次请求都查数据库高并发下数据库压力骤增响应时间长且博客列表、用户信息等“公共数据”所有用户看到的内容一致无需每次查询。2. 优化思路采用“缓存优先”策略先查询Redis缓存缓存命中则直接返回未命中则查询数据库查询后将公共数据写入缓存兜底数据库压力。同时明确“公共数据”和“个性化数据”的区别——公共数据可缓存个性化数据点赞状态实时查。3. 解决方案设计缓存Key按分页区分格式为“blog:hot:{current}”current为分页参数避免不同分页的缓存互相覆盖缓存逻辑接口执行时先查缓存命中则反序列化返回未命中则查数据库查询后写入缓存设置5分钟过期避免数据与数据库不一致缓存容错缓存操作加try-catch避免Redis故障导致接口不可用缓存是优化项不是依赖项数据区分缓存仅存储博客列表和用户信息公共数据点赞状态个性化数据不缓存缓存命中后实时填充。4. 思考为什么这样做① 缓存的核心价值Redis是内存数据库响应时间≤1ms远快于数据库毫秒/秒级99%的请求可命中缓存仅1%的请求穿透到数据库大幅降低数据库压力② 缓存Key设计分页参数必须加入Key否则不同分页的缓存会互相覆盖导致数据错乱③ 不缓存个性化数据点赞状态是用户维度的个性化数据不同用户结果不同若缓存会导致数据串用比如用户A的点赞状态被用户B看到因此必须实时查询。优化3解决N1查询问题减少数据库交互1. 问题根源原逻辑中查询博客列表后循环调用queryBlogUser方法逐个查询每条博客的关联用户1次查博客N次查用户数据库交互次数随博客数增长高并发下性能极差。2. 优化思路用“批量查询”替代“循环逐个查询”先收集所有博客的用户ID去重再通过userService.listByIds(userIds)批量查询用户将查询次数从1N次降为2次查博客批量查用户。3. 解决方案收集用户ID通过Stream流收集当前页所有博客的userId去重后得到用户ID集合批量查询用户调用listByIds方法批量查询用户将结果转为MapkeyuserIdvalueUser便于快速取值填充用户信息遍历博客列表从Map中获取用户信息替代循环调用queryBlogUser方法无额外数据库交互。4. 思考为什么这样做① 数据库交互的瓶颈数据库的性能瓶颈不在“查询计算”而在“网络交互”每次查询都要走TCP握手、传输等流程10条博客循环查询需10次网络交互批量查询仅需1次② 去重的意义避免重复查询同一用户比如同一用户发布多篇热门博客进一步减少数据库压力③ Map取值的优势批量查询后转Map后续填充用户信息时可直接通过userId取值时间复杂度为O(1)比循环查询高效得多。优化4适配单博客查询queryBlogById保证逻辑闭环1. 问题根源优化/blog/hot接口后单博客详情接口queryBlogById的点赞状态查询逻辑仍基于原ZSet结构与新的用户维度Set不匹配导致数据错乱。2. 优化思路复用批量点赞查询的逻辑新增单博客版本的isBlogLiked方法避免重复代码保证点赞查询逻辑统一均基于用户维度Set。3. 解决方案重载isBlogLiked方法新增单博客参数的方法内部将单博客包装成List调用已有的批量方法实现逻辑复用兜底处理未登录用户默认未点赞避免点赞状态为null提升代码健壮性。4. 思考为什么这样做① 代码复用避免重复编写Redis查询逻辑后续修改点赞查询逻辑时只需改一处降低维护成本② 逻辑统一单博客和批量博客的点赞查询共用一套逻辑与updateLike方法的写入逻辑一致避免数据错乱③ 最小改动无需重写queryBlogById主方法仅新增重载方法符合“开闭原则”对扩展开放对修改关闭。四、关键误区与反思重点记误区1认为“Stream流能减少Redis查询次数”——实际是“批量查询策略”减少查询次数Stream流只是简化“收集数据”的代码核心是“先收集所有查询条件再一次性请求”而非Stream流本身有优化效果误区2认为“能按用户精准查询点赞状态就可以将点赞状态缓存到公共列表”——缓存Key的粒度要匹配数据归属公共缓存分页维度不能存储个性化数据用户点赞状态否则会导致不同用户数据串用误区3认为“代码层面的循环整理就是批量查询”——真正的Redis批量查询需要用Pipeline将多个请求打包成一次网络请求单纯的循环收集Key后逐个查询依然是多次网络请求反思优化不是“堆砌技术”而是“解决问题”每一步优化都要先明确“问题是什么”再思考“为什么用这种方案”最后落地“怎么做”避免盲目加缓存、加批量兼顾性能、可读性和可维护性。五、优化效果与总结1. 优化效果数据库交互次数从1N次循环查用户降为2次查博客批量查用户Redis交互次数从N次循环查点赞降为1次批量交集查询接口响应时间从秒级降至50ms以内数据库压力99%的请求被Redis缓存拦截压力骤降高并发下稳定性提升数据准确性公共数据缓存个性化数据实时查避免数据错乱点赞状态查询准确。2. 核心总结本次/blog/hot接口优化的核心的是“找准瓶颈、匹配需求、简化交互”① 瓶颈定位先明确数据库、Redis的核心压力点N1查询、多次网络交互再针对性优化② 需求匹配Redis数据结构要贴合接口查询需求避免“数据结构与查询反向”③ 简化交互用批量操作减少网络交互用缓存减少数据库查询用复用减少代码冗余④ 兼顾细节区分公共/个性化数据、补充空指针防护、缓存容错保证代码健壮性和数据准确性。后续可补充的优化热点博客加本地缓存Caffeine进一步减少Redis压力缓存过期时间改为随机值避免缓存雪崩。