当前位置: 首页 > article >正文

黑马点评毕业设计效率提升实战:从单体到高并发架构的演进路径

最近在帮学弟学妹们review“黑马点评”这个经典的毕业设计项目时发现一个普遍现象大家都能把功能跑起来但一提到性能优化、高并发就有点无从下手。很多同学直接沿用课程里的单体架构模板结果在模拟答辩或者自己压测时系统响应慢、数据错乱甚至直接宕机。这其实很可惜因为毕业设计不仅是功能的堆砌更是展示你工程化思维和解决问题能力的绝佳机会。今天我就结合自己之前折腾这个项目的经验聊聊如何通过一系列“组合拳”把一个基础的单体点评系统改造成一个能扛住一定并发、数据更可靠、响应更快的“准生产”系统。核心思路就是引入缓存、异步和解锁。1. 痛点分析单体架构在高并发下的“原罪”我们先看看最原始的单体架构在高并发请求下会暴露哪些问题数据库成为绝对瓶颈所有读写请求无论是用户查询店铺详情、提交订单还是点赞评论都直接落到MySQL上。在并发查询时尤其是“热门店铺”这类热点数据数据库连接池迅速耗尽导致大量请求排队或超时这就是典型的“慢查询”引发的线程阻塞。“超卖”问题这是电商/秒杀类场景的经典难题。在原始代码中扣减库存的逻辑通常是“查询库存 - 判断是否大于0 - 执行更新”。在并发场景下多个线程可能同时查询到库存为1都判断通过然后都执行了更新最终导致库存变为负数商品超卖。用户体验卡顿像“点赞”、“收藏”这类操作如果采用同步方式直接写库用户会明显感觉到点击后页面“卡”一下。在高频交互场景下这种体验非常不友好。服务雪崩风险一旦某个耗时操作比如一个复杂的联表查询拖慢了数据库可能会导致依赖该数据库的所有服务线程都被阻塞进而引发整个系统不可用。2. 技术选型为什么是它们面对这些问题我们需要引入新的技术组件。选型的核心原则是简单、高效、与Spring生态整合度高。缓存Redis vs 本地缓存 (Caffeine)本地缓存如Caffeine访问速度极快纳秒级但数据只在单个应用实例内有效无法在集群环境下共享且应用重启后数据丢失。适合缓存完全静态、或与用户会话强绑定的数据。Redis虽然网络IO会带来微秒级的延迟但提供了丰富的数据结构、持久化、集群支持以及最重要的——数据共享。对于“黑马点评”中的店铺信息、热门商品列表、用户会话分布式Session等需要多实例共享或持久化的热点数据Redis是不二之选。异步消息队列 (RabbitMQ) vs 线程池线程池异步在应用内实现简单适合处理耗时短、与主业务逻辑解耦不强的任务如记录操作日志。但任务无法持久化应用重启会丢失且难以跨服务通信。消息队列如RabbitMQ/Kafka提供了可靠的消息传递、持久化、削峰填谷和能力。对于“点赞”、“发送通知”、“下单后更新排行榜”这类允许短暂延迟、但必须确保最终执行成功的旁路业务将其异步化到消息队列中能极大提升主流程的响应速度并保护数据库。RabbitMQ在消息可靠性、路由灵活性上更胜一筹Kafka则擅长超高吞吐的日志流处理。对于毕业设计RabbitMQ的易用性和与Spring的完美整合是更优选择。并发控制数据库乐观锁 vs Redis分布式锁数据库乐观锁通过版本号或时间戳实现在更新时校验。适合冲突频率不高的场景但失败率较高时重试逻辑复杂。Redis分布式锁利用SETNX命令或Redisson客户端实现是解决分布式环境下“超卖”、“防重提交”等互斥问题的标准方案。它能保证在集群环境下同一时刻只有一个线程能执行关键代码段。3. 核心实现细节与代码实战接下来我们分模块看看具体怎么实现。3.1 缓存与数据库的双写一致性策略缓存策略我们选择经典的Cache-Aside Pattern (旁路缓存)。这是最常用、可控性最高的模式。读流程先读缓存命中则返回未命中则读数据库写入缓存后返回。写流程先更新数据库再删除缓存而非更新缓存。为什么是“删缓存”而不是“更新缓存”主要是为了避免复杂的并发更新导致缓存脏数据。我们采用“先更新数据库再删除缓存”的策略在极端情况下可能有短暂脏读但概率很低且通过设置合理的缓存过期时间可以容忍。为了确保删除成功可以引入重试机制如通过消息队列。Service Slf4j public class ShopServiceImpl implements ShopService { Autowired private StringRedisTemplate redisTemplate; Autowired private ShopMapper shopMapper; private static final String CACHE_SHOP_KEY cache:shop:; Override public Shop queryById(Long id) { // 1. 从Redis查询商铺缓存 String key CACHE_SHOP_KEY id; String shopJson redisTemplate.opsForValue().get(key); // 2. 判断缓存是否存在 if (StrUtil.isNotBlank(shopJson)) { // 3. 存在直接返回 return JSONUtil.toBean(shopJson, Shop.class); } // 判断命中的是否是空值用于解决缓存穿透 if (shopJson ! null) { // 明确不等于null说明是空字符串 return null; } // 4. 不存在根据id查询数据库 Shop shop shopMapper.selectById(id); // 5. 数据库中不存在返回错误并将空值写入Redis解决缓存穿透 if (shop null) { redisTemplate.opsForValue().set(key, , 2L, TimeUnit.MINUTES); // 设置短TTL return null; } // 6. 数据库中存在写入Redis设置随机过期时间解决缓存雪崩 int ttl 30 new Random().nextInt(31); // 30-60分钟随机过期 redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), ttl, TimeUnit.MINUTES); return shop; } Override Transactional public Result update(Shop shop) { Long id shop.getId(); if (id null) { return Result.fail(店铺id不能为空); } // 1. 更新数据库 shopMapper.updateById(shop); // 2. 删除缓存 String key CACHE_SHOP_KEY id; redisTemplate.delete(key); return Result.ok(); } }3.2 基于Redis的分布式锁实现秒杀防超卖我们用Redis的SET key value NX EX seconds命令来实现一个简单的分布式锁确保扣减库存的原子性。Component Slf4j public class SeckillService { Autowired private StringRedisTemplate redisTemplate; Autowired private SeckillVoucherMapper seckillVoucherMapper; private static final String LOCK_KEY_PREFIX lock:seckill:; private static final String STOCK_KEY_PREFIX seckill:stock:; public Result seckill(Long voucherId) { // 1. 查询优惠券信息这里可从缓存或DB查略 // 2. 判断秒杀是否开始/结束 // ... (省略时间判断逻辑) // 3. 使用分布式锁确保一人一单 库存扣减原子性 Long userId UserHolder.getUser().getId(); // 假设从线程上下文获取用户ID String lockKey LOCK_KEY_PREFIX voucherId : userId; String clientId UUID.randomUUID().toString(); // 锁标识用于防误删 try { // 尝试获取锁设置10秒自动过期防止死锁 Boolean success redisTemplate.opsForValue() .setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS); if (Boolean.FALSE.equals(success)) { // 获取锁失败返回错误或重试 return Result.fail(不允许重复下单); } // 4. 扣减库存使用Redis的decrement操作保证原子性 String stockKey STOCK_KEY_PREFIX voucherId; Long stock redisTemplate.opsForValue().decrement(stockKey); if (stock null || stock 0) { // 库存不足需要将刚才减去的库存加回来或提前校验 redisTemplate.opsForValue().increment(stockKey); return Result.fail(库存不足); } // 5. 创建订单写数据库 VoucherOrder order new VoucherOrder(); // ... 设置订单信息 // voucherOrderService.save(order); // 实际保存操作 return Result.ok(order.getId()); } finally { // 6. 释放锁使用Lua脚本保证原子性判断锁标识是否为自己持有 String luaScript if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end; redisTemplate.execute( new DefaultRedisScript(luaScript, Long.class), Collections.singletonList(lockKey), clientId ); } } }3.3 异步化点赞/评论流程我们将点赞这个动作异步化。用户点击点赞后立即返回成功然后将点赞消息发送到RabbitMQ由消费者异步执行数据库写入。// 1. 控制器层接收点赞请求 PostMapping(/like) public Result likeBlog(RequestParam Long blogId) { // 同步操作更新Redis中的点赞状态和计数快速 blogService.likeBlog(blogId); // 异步操作发送消息到MQ持久化到数据库 amqpTemplate.convertAndSend(blog.exchange, blog.like, blogId); return Result.ok(); } // 2. 服务层处理Redis点赞 Service public class BlogServiceImpl { public void likeBlog(Long blogId) { Long userId UserHolder.getUser().getId(); String key blog:liked: blogId; Double score redisTemplate.opsForZSet().score(key, userId.toString()); if (score null) { // 未点赞执行点赞 redisTemplate.opsForZSet().add(key, userId.toString(), System.currentTimeMillis()); redisTemplate.opsForHash().increment(blog:stats: blogId, liked, 1); } else { // 已点赞取消点赞 redisTemplate.opsForZSet().remove(key, userId.toString()); redisTemplate.opsForHash().increment(blog:stats: blogId, liked, -1); } } } // 3. MQ消费者异步处理数据库持久化 Component Slf4j public class BlogLikeConsumer { Autowired private BlogLikeRecordMapper likeRecordMapper; RabbitListener(queues blog.like.queue) public void handleLikeMessage(Long blogId) { log.info(收到博客点赞异步消息blogId: {}, blogId); // 这里可以从Redis读取最新的点赞用户集合与数据库对比进行增量更新 // 为了简化我们假设每次都是新增一条记录实际需去重逻辑 // likeRecordMapper.insert(new BlogLikeRecord(blogId, ...)); // 也可以批量更新博客表的点赞计数字段 log.info(博客{}点赞数据已异步持久化, blogId); } }4. 效果验证与防护措施改造完成后使用JMeter进行压测对比。模拟1000个并发用户持续请求“查询热门店铺”接口。优化前直连数据库QPS大约在150左右平均响应时间超过500ms数据库CPU持续高位。优化后Redis缓存异步化QPS稳定在800平均响应时间降至50ms以内数据库负载下降超过70%。缓存穿透/雪崩/击穿防护穿透上文代码已展示对查询不到的数据也缓存一个空值如空字符串并设置较短的过期时间。雪崩给缓存数据设置随机过期时间避免大量key在同一时刻失效导致请求全部打到DB。击穿对于热点key如爆款商品使用互斥锁Mutex Lock。当缓存失效时不立即去数据库加载而是先获取一个分布式锁只有拿到锁的线程去数据库加载并回填缓存其他线程等待。这可以用上面分布式锁的代码变体实现。5. 生产环境避坑指南这些经验来自真实项目的教训热点Key处理像“秒杀库存”这样的Key在瞬间会有海量读写。解决方案本地缓存Redis在应用层做一层短期本地缓存减少对Redis的请求。Key分片将一个热点Key拆分成多个子Key如stock:商品ID:1stock:商品ID:2将压力分散。使用Redis集群将热点Key通过哈希标签强制分配到同一个slot避免集群下的跨节点访问。锁粒度控制锁的粒度要尽可能细。例如秒杀锁不应该锁整个商品而应该锁“用户商品”这样不同用户之间不会互相阻塞极大提升并发度如上文代码所示。幂等性设计对于消息队列消费、接口重试等场景必须保证操作幂等。可以通过业务唯一标识如订单号 状态机或者在Redis中设置已处理标记来实现。例如消费者处理点赞消息前先查一下Redis中该用户对该博客的点赞状态是否已同步避免重复插入。监控与告警一定要对Redis的内存使用率、连接数、慢查询以及MQ的堆积情况进行监控。设置合理的阈值告警早发现早处理。写在最后做完这一套优化你的“黑马点评”项目在技术深度和工程完成度上已经远超一个简单的CRUD毕业设计了。它展示了你对高并发典型问题的认知以及运用主流中间件解决实际问题的能力。最后留一个思考题也是分布式系统设计的经典权衡在有限的服务器资源下我们如何权衡数据的一致性与系统可用性在我们的优化里缓存带来了可用性和性能的巨大提升但也引入了数据短暂不一致的可能性如先更新数据库后删除缓存失败。对于“点赞数”这种对实时性要求不高的数据我们完全可以接受最终一致性。但对于“支付状态”、“库存”这类强一致性要求的数据方案就要复杂得多如使用分布式事务、更严谨的锁和重试机制。没有银弹一切取决于你的业务场景。在你的毕业设计中想清楚每个模块的数据一致性要求并做出合适的技术选择这本身就是一个非常棒的亮点。希望这篇笔记能给你带来启发祝你毕业设计顺利拿到心仪的Offer

相关文章:

黑马点评毕业设计效率提升实战:从单体到高并发架构的演进路径

最近在帮学弟学妹们review“黑马点评”这个经典的毕业设计项目时,发现一个普遍现象:大家都能把功能跑起来,但一提到性能优化、高并发,就有点无从下手。很多同学直接沿用课程里的单体架构模板,结果在模拟答辩或者自己压…...

经典游戏无法运行?DDrawCompat让老游戏在新系统重生

经典游戏无法运行?DDrawCompat让老游戏在新系统重生 【免费下载链接】DDrawCompat DirectDraw and Direct3D 1-7 compatibility, performance and visual enhancements for Windows Vista, 7, 8, 10 and 11 项目地址: https://gitcode.com/gh_mirrors/dd/DDrawCom…...

基于深度学习的桥梁健康状态监测与预警系统设计与实现

基于深度学习的桥梁健康状态监测与预警系统设计与实现 1. 系统总体架构 本系统采用 B/S 架构,由数据采集层、数据处理层、深度学习模型层、Web后端层及前端可视化层组成。 后端框架:Django (负责ORM、API、用户认证) 深度学习:TensorFlow 2.x / Keras (构建LSTM-Autoencod…...

Arduino轻量级软件消抖库FTDebouncer原理与应用

1. 项目概述Future Tailors’ Debouncer(简称 FTDebouncer)是一个专为 Arduino 平台设计的轻量级、高效率、低资源占用的软件消抖库。其核心目标是解决嵌入式开发中一个看似简单却极易出错的基础问题:机械按键或开关引脚的硬件抖动&#xff0…...

基于动态三维环境下的Q-Learning算法无人机自主避障路径规划研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

新手必看,在快马平台上手mcp,从零理解模型上下文协议的核心机制

今天想和大家分享一个特别适合新手理解MCP(模型上下文协议)的小项目。作为一个刚接触AI开发不久的人,我发现在InsCode(快马)平台上学习这些概念特别方便,尤其是它能把复杂的协议用实际代码展示出来。 MCP简单来说就是AI模型和外部…...

Beyond Compare 5 终极激活指南:本地密钥生成工具完整教程

Beyond Compare 5 终极激活指南:本地密钥生成工具完整教程 【免费下载链接】BCompare_Keygen Keygen for BCompare 5 项目地址: https://gitcode.com/gh_mirrors/bc/BCompare_Keygen Beyond Compare 5 是一款专业的文件对比与合并工具,广泛应用于…...

Harmonyos应用实例193:圆与方程探索

5. 圆与方程探索 功能简介:输入圆心坐标和半径,绘制圆并显示标准方程,探索圆与直线的位置关系。这是一个功能强大的圆方程计算器,支持通过滑块交互式调整圆心坐标和半径,实时绘制圆形并显示标准方程。用户可选择显示直线,通过调整斜率和截距探索圆与直线的位置关系,系统…...

零基础学习数据库:用快马AI生成你的第一个可操作图书管理系统

作为一个刚接触数据库的小白,最近在InsCode(快马)平台上尝试做了一个图书管理系统项目,整个过程意外地顺利。这里记录下我的学习心得,希望能帮到同样零基础的朋友们。 为什么选择图书管理系统作为入门项目 图书管理系统包含了数据库最基础的…...

Logisim-evolution完全指南:跨平台安装与配置实战

Logisim-evolution完全指南:跨平台安装与配置实战 【免费下载链接】logisim-evolution Digital logic design tool and simulator 项目地址: https://gitcode.com/gh_mirrors/lo/logisim-evolution 准备阶段:从零开始的环境搭建 1.1 认识Logisim…...

实战详解:从零构建 LangChain 智能 Agent,让大模型真正“动起来”!

文章目录📖 一、为什么我们需要 Agent?🔄 Agent 核心工作流图解🛠️ 二、环境准备与核心组件核心组件介绍💻 三、实战代码:构建“全能数据分析师”Agent1. 定义工具 (Tools)2. 构建 Agent 逻辑 (ReAct 模式…...

毕业设计题目100个:面向工程实践的技术选型与实现指南

最近在帮学弟学妹们看毕业设计,发现一个挺普遍的现象:很多同学想法天马行空,但一到动手实现就卡壳,要么技术栈选得五花八门拼不起来,要么代码写得像一锅粥,后期根本没法维护。选题“假大空”、实现“散乱差…...

开源项目版本冲突解决指南:从现象到实践的深度解析

开源项目版本冲突解决指南:从现象到实践的深度解析 【免费下载链接】ComfyUI-Impact-Pack 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-Impact-Pack 问题现象:版本不匹配的警告信号 在开源项目开发中,你是否遇到过这样的情…...

智能写作工坊:OpenClaw+Qwen3.5-9B辅助小说创作

智能写作工坊:OpenClawQwen3.5-9B辅助小说创作 1. 为什么需要AI辅助写作? 作为一个业余小说创作者,我长期面临三个核心痛点:世界观设定碎片化、人物关系维护困难和情节发展缺乏新意。传统写作软件如Scrivener虽然提供了素材管理…...

如何免费阅读付费文章?终极智能内容解锁工具完全指南

如何免费阅读付费文章?终极智能内容解锁工具完全指南 【免费下载链接】bypass-paywalls-chrome-clean 项目地址: https://gitcode.com/GitHub_Trending/by/bypass-paywalls-chrome-clean 在这个信息爆炸的时代,你是否经常遇到这样的情况&#xf…...

清华大学学位论文LaTeX模板:thuthesis完整使用指南

清华大学学位论文LaTeX模板:thuthesis完整使用指南 【免费下载链接】thuthesis LaTeX Thesis Template for Tsinghua University 项目地址: https://gitcode.com/gh_mirrors/th/thuthesis 清华大学thuthesis LaTeX模板是专为清华学子设计的学位论文写作工具&…...

多维尺度变换(MDS)实战指南:从原理到Python实现

1. 多维尺度变换(MDS)是什么? 多维尺度变换(Multidimensional Scaling,简称MDS)是一种经典的降维算法,它的核心思想是通过保持数据点之间的距离关系,将高维数据映射到低维空间。想象…...

解决CLI工具接入DeepSeek API流式传输失败的实战指南

最近在对接DeepSeek的API时,遇到了一个挺典型的问题:用CLI工具调用API进行流式传输时,经常中途失败,返回的错误信息又比较模糊,调试起来很头疼。特别是在处理长文本生成或者需要连续对话的场景下,这个问题直…...

智慧农业篇(一):一套大棚监控系统的架构与实战

2018年一个朋友找到我,想开发 一套完整的农业种植的智能控制监测系统,主要针对的是蔬菜大棚的智能控制;基本思路就是:给出一套让农民“坐在家里种地”的物联网方案。我们当时涉足智慧农业的初心就是:让数据替人跑腿&am…...

# React 发散创新:从状态管理到组件化架构的极致实践在前端开发领域,React

React 发散创新:从状态管理到组件化架构的极致实践 在前端开发领域,React 已经成为构建现代 Web 应用的事实标准。但你是否曾思考过——如何让 React 不只是“写页面”,而是真正成为驱动业务逻辑的核心引擎? 本文将带你突破常规思…...

PyLink 实战技巧:从基础连接到高级调试

1. PyLink入门:从零搭建调试环境 第一次接触PyLink时,我也被各种专业术语弄得晕头转向。后来才发现,只要掌握几个关键步骤,就能快速搭建起嵌入式调试环境。PyLink本质上是个Python库,它像翻译官一样,把我们…...

XUnity.AutoTranslator:Unity游戏自动翻译解决方案

XUnity.AutoTranslator:Unity游戏自动翻译解决方案 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity.AutoTranslator是一款专业的Unity游戏自动翻译插件,能够实时将游戏文本转…...

大数据核心知识全解(零基础到Hadoop专家路线)【20260324】001篇

文章目录 大数据核心知识全解(零基础到Hadoop专家路线) 一、为什么会出现大数据?(本质原因) 1. 数据来源爆炸 2. 传统技术扛不住 3. 需求倒逼 二、CNCF 是什么?(云原生核心组织) 它和大数据的关系 三、为什么 Hadoop 会流行?(3个核心原因) 1. 它解决了当时最痛的问题…...

Windows 10/11 上 Docker 部署 Milvus 与 Attu 图形化界面全攻略

1. Windows 系统准备与 Docker 安装 在 Windows 10/11 上部署 Milvus 之前,需要确保系统环境满足基本要求。我实测发现,Windows 家庭版默认不支持 Hyper-V,需要先升级到专业版或企业版。检查系统版本的方法很简单:右键点击"此…...

专科ENSP毕设实战:基于eNSP的校园网高可用架构设计与配置避坑指南

最近在帮几个专科的学弟学妹看他们的eNSP毕业设计,发现大家普遍卡在几个地方:拓扑画得挺漂亮,但一配置就各种不通;协议背得滚瓜烂熟,但实际命令敲下去就报错;最后答辩演示时,一拔线整个网络就瘫…...

颈肩痛分急性和慢性,对症缓解才有效

颈肩痛并非单一症状,根据发病时间和诱因,可分为急性颈肩痛和慢性颈肩痛,两者的缓解和治疗方式差异显著,找对方法才能快速摆脱疼痛困扰。急性颈肩痛多由外伤、运动不当、落枕等引起,疼痛剧烈且突然发作,常伴…...

从原理到实践:Matlab相机标定参数详解与坐标变换全流程

1. 相机标定基础概念与Matlab工具箱实战 刚接触相机标定的朋友可能觉得那些参数看着就头疼,其实拆解开来并不复杂。我最早做机器人视觉项目时,也是被各种矩阵绕得晕头转向,直到自己动手标定了十几台工业相机才摸清门道。相机标定的本质就是建…...

一键解决中文文献管理痛点:茉莉花插件让Zotero效率提升90%的完整指南

一键解决中文文献管理痛点:茉莉花插件让Zotero效率提升90%的完整指南 【免费下载链接】jasminum A Zotero add-on to retrive CNKI meta data. 一个简单的Zotero 插件,用于识别中文元数据 项目地址: https://gitcode.com/gh_mirrors/ja/jasminum …...

3分钟搞定Windows音频捕获:win-capture-audio让你的录音效率翻倍

3分钟搞定Windows音频捕获:win-capture-audio让你的录音效率翻倍 【免费下载链接】win-capture-audio An OBS plugin that allows capture of independant application audio streams on Windows, in a similar fashion to OBSs game capture and Discords applicat…...

Linux原生B站客户端:突破平台限制的深度体验指南

Linux原生B站客户端:突破平台限制的深度体验指南 【免费下载链接】bilibili-linux 基于哔哩哔哩官方客户端移植的Linux版本 支持漫游 项目地址: https://gitcode.com/gh_mirrors/bi/bilibili-linux 对于Linux用户来说,在开源生态中寻找优质的视频…...