news 2026/5/12 23:06:36

黑马点评毕业设计技术解析:从单体架构到高并发点评系统的演进之路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
黑马点评毕业设计技术解析:从单体架构到高并发点评系统的演进之路


黑马点评毕业设计技术解析:从单体架构到高并发点评系统的演进之路

摘要:很多学生在完成“黑马点评”毕业设计时,常陷入数据库瓶颈、缓存穿透、接口幂等性缺失等典型问题。本文基于真实教学项目,系统讲解如何通过 Redis 缓存预热、分布式锁控制热点评论、异步消息队列解耦写操作等关键技术,构建一个具备基础高并发能力的点评系统。读者将掌握可落地的性能优化策略与工程化思维,显著提升系统吞吐量与稳定性。


1. 项目背景与常见痛点

“黑马点评”脱胎于教学场景,功能看似简单:用户注册登录、浏览商户、发评论、点赞/收藏。一旦把并发量从“一个人点一下”提升到“一千个人同时点”,单体架构的短板立刻暴露:

  1. 缓存击穿:热点商户信息过期瞬间,大量请求直穿 DB,CPU 飙高。
  2. 超卖:优惠券或秒杀库存未做原子扣减,出现“-1 件”。
  3. 冷启动慢:服务重启后缓存为空,接口 RT 从 50 ms 涨到 2 s。
  4. 幂等缺失:用户疯狂点击“点赞”,Redis 计数飙涨,DB 却重复插入。
  5. 事务边界混乱:Service 层一个方法里既调 Redis 又调 DB,回滚时缓存与数据库不一致。

这些问题在毕业答辩的 PPT 里往往被一句“后续优化”带过,真到演示时却频频 5xx。下面把踩过的坑逐一展开,给出可直接复制的工程级方案。


2. 技术选型对比:教学版 vs 生产版

维度教学默认生产推荐一句话理由
ORMMyBatisMyBatis-Plus内置分页、乐观锁、LambdaWrapper,代码量↓ 40%
Redis单机 6379三主三从 + Sentinel毕设答辩时老师随手关虚拟机,主从切换秒级完成
MQRocketMQ/RabbitMQ点赞、写评论异步落库,DB 压力降 70%
限流Bucket4j + Redis低成本令牌桶,突发流量 QPS 可控
序列化JDKProtostuff/KryoRedis 序列化体积减半,网络 IO↓ 30%

注意:教学版到生产版不是“一步替换”,而是“逐步灰度”。先让系统能跑通,再让系统跑得稳。


3. 核心模块实现细节

以下代码均基于 Spring Boot 2.7 + MyBatis-Plus + Redis 6.2,遵守 Clean Code 原则:方法名自解释、魔法值用常量、分支深度≤2。

3.1 用户登录:基于 Redis 的分布式会话

需求:支持多端登录、token 自动续期、踢人下线。

  1. 登录成功后生成 UUID 作为 token,以user:token:{userId}:{uuid}写入 Redis,TTL=30 min。
  2. 每次请求带 token,网关层通过UserContextHolder填充 ThreadLocal。
  3. 续期策略:接口返回前异步刷新 TTL,避免“半小时必掉线”尴尬。
@RestController @RequestMapping("/api/user") public class UserController { @Resource private StringRedisTemplate redisTpl; private static final String TOKEN_PREFIX = "user:token:"; private static final Duration TOKEN_TTL = Duration.ofMinutes(30); @PostMapping("/login") public ApiResult<String> login(@RequestBody LoginForm form) { // 1. 参数校验省略 User user = userService.verify(form.getPhone(), form.getPassword()); String uuid = IdUtil.fastUUID(); String key = TOKEN_PREFIX + user.getId() + ":" + uuid; redisTpl.opsForValue().set(key, String.valueOf(user.getId()), TOKEN_TTL); return ApiResult.ok(uuid); // 返回 token } }

关键点:token 里带 userId,可防止恶意遍历;UUID 段保证同一用户多端登录互不影响。

3.2 商户缓存:双重判定锁解决击穿

需求:商户信息读多写少,并发查询 5k+ QPS。

  1. 缓存空对象:DB 查不到也写 Redis,防止重复穿透。
  2. 双重判定:第 1 次判空→加锁→第 2 次判空,减少锁竞争。
  3. 热点自动续期:Worker 每 10 min 扫描访问量 Top 100 的 key,异步续期。
public Shop getShop(Long id) { String key = CACHE_SHOP_KEY + id; Shop shop = redisTpl.opsForValue().get(key); if (shop != null) { return shop; } // 第一层判定 String lockKey = LOCK_SHOP_KEY + id; Boolean locked = redisTpl.opsForValue().setIfAbsent(lockKey, "1", Duration.ofSeconds(10)); if (Boolean.TRUE.equals(locked)) { try { // 第二层判定 shop = redisTpl.opsForValue().get(key); if (shop != null) { return shop; } shop = shopMapper.selectById(id); if (shop == null) { // 缓存空对象 redisTpl.opsForValue().set(key, new Shop(), Duration.ofMinutes(2)); } else { redisTpl.opsForValue().set(key, shop, Duration.ofMinutes(30)); } } finally { redisTpl.delete(lockKey); } } else { // 50ms 后重试,避免大量线程阻塞 ThreadUtil.sleep(50); return getShop(id); } return shop; }

3.3 点赞/收藏:基于 Redis 的幂等计数

需求:用户点赞后 1 s 内连续点击只算一次;计数实时展示。

  1. 使用SETNX做幂等标记,key 带业务前缀like:uid:{userId}:bid:{blogId},TTL=2 s。
  2. 计数用HINCRBY,字段blog:{blogId}:like
  3. 定时任务每 5 s 把 Redis 计数批量写回 DB,减小写压力。
public void like(Long userId, Long blogId) { String idempotentKey = "like:uid:" + userId + ":bid:" + blogId; Boolean absent = redisTpl.opsForValue().setIfAbsent(idempotentKey, "1", Duration.ofSeconds(2)); if (Boolean.FALSE.equals(absent)) { throw new BizException("操作太快,请稍后再试"); } redisTpl.opsForHash().increment("blog:" + blogId + ":like", "cnt", 1); }

说明:SETNX 原子性保证并发点击只通过一次;HINCRBY 原子自增,无需事务。


4. 性能与安全考量

  1. 防刷限流

    • 网关层整合 Bucket4j,令牌桶容量 200,填充速率 100/s,超出直接返回 429。
    • 针对短信验证码接口再叠加手机号维度桶,1 分钟 3 次,防止短信轰炸。
  2. SQL 注入防护

    • MyBatis-Plus 内置#{}预编译,额外开启全局关键字过滤,拦截sleepbenchmark等危险函数。
    • 模糊查询使用QueryWrapperlike()方法,禁止拼接%${}%
  3. 缓存一致性

    • 写操作先删缓存再改 DB,延迟双删策略:第二次延迟 500 ms 再次删除,兜底并发脏读。
    • 使用 Canal 监听 binlog,异步修正 Redis,做到“最终一致”。
  4. 接口幂等

    • 除点赞外,订单型写操作统一带Idempotency-Token头,网关层校验 Redis 是否已存在,保证重复提交只处理一次。

5. 生产环境避坑指南

  1. Redis Key 设计规范

    • 格式:业务:子业务:唯一标识:维度,全部小写,用冒号分隔。
    • 禁止keys *,统一用scan + TYPE扫描;长度≤ 44 字节,减少内存占用。
  2. 事务边界划分

    • 只把“必须原子”的 DB 操作包进@Transactional,缓存操作放外层,防止回滚后缓存已写。
    • 跨 Redis 与 DB 的“混合事务”用最终一致思路,不要企图用 Redis 事务(Lua)包裹 DB。
  3. 序列化与版本兼容

    • 对象缓存放 Protostuff,新增字段必须serialVersionUID自增,防止升级后反序列化失败。
    • 开启spring.redis.serializer=GenericJackson2JsonRedisSerializer时,记得把类名写入 JSON,避免包路径调整导致找不到类。
  4. 慢 SQL 治理

    • 打开 MyBatis-Plus 的performanceInterceptor,阈值 100 ms,超阈值自动打印完整 SQL 与参数。
    • 商户模糊查询走 ES,只把 ID 回表,避免%xx%导致全表扫描。
  5. 容器线程数

    • Undertow 默认 IO 线程数 = CPU*2,业务线程池另配core=CPU*4,防止 IO 线程被阻塞。

6. 高并发验证与扩展思考

资源受限条件下,可用以下低成本方案模拟 1 k 并发:

  1. 本地起 Docker 版 Redis、MySQL,关闭持久化,纯内存跑。
  2. 用 Gatling/JMeter 起 2 个线程组,每个 500 并发,循环 30 s,观察 95 线 RT 与错误率。
  3. 重点指标:缓存命中率 ≥ 90%、QPS ≥ 1500、DB 连接 ≤ 20、CPU ≤ 70%。

扩展练习:地理位置附近商户推荐
把商户坐标导入 RedisGEO结构,用户上传经纬度后使用GEORADIUS命令 5 km 内搜索,再结合评分排序,即可实现“附近好评最多”功能。记得给坐标 key 设置过期时间,防止冷数据常驻。



写完这篇笔记,最大的感受是:毕业设计不是“跑通”就行,而是要把“跑通”到“跑稳”的每一步都留下可回滚的 Git 记录。把缓存、幂等、限流这些看似“高级”的词拆成一行行代码,才发现高并发系统其实是由无数个小细节累加而成。希望这些可直接粘贴的片段,能帮你在答辩现场少踩几个 5xx,把更多时间留给老师提问——“如果 Redis 挂了,你的系统还能降级吗?” 不妨现在就动手,把答案写进代码里。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 7:34:38

GmSSL国密开发实战指南:从环境搭建到应用部署

GmSSL国密开发实战指南&#xff1a;从环境搭建到应用部署 【免费下载链接】GmSSL 支持国密SM2/SM3/SM4/SM9/SSL的密码工具箱 项目地址: https://gitcode.com/gh_mirrors/gm/GmSSL 一、为什么国密开发选择GmSSL&#xff1f; 在当今数字化时代&#xff0c;信息安全已成为…

作者头像 李华
网站建设 2026/5/10 22:19:45

告别Excel公式:AI表格处理的极简革命

告别Excel公式&#xff1a;AI表格处理的极简革命 【免费下载链接】TabPFN Official implementation of the TabPFN paper (https://arxiv.org/abs/2207.01848) and the tabpfn package. 项目地址: https://gitcode.com/gh_mirrors/ta/TabPFN 表格AI正在重新定义数据处理…

作者头像 李华
网站建设 2026/5/9 11:02:41

3步掌握网络性能测试:从入门到精通iperf3工具

3步掌握网络性能测试&#xff1a;从入门到精通iperf3工具 【免费下载链接】iperf3-win-builds iperf3 binaries for Windows. Benchmark your network limits. 项目地址: https://gitcode.com/gh_mirrors/ip/iperf3-win-builds iperf3是一款轻量级网络性能测试工具&…

作者头像 李华
网站建设 2026/5/9 5:20:22

OLLAMA部署本地大模型轻量高效:LFM2.5-1.2B-Thinking在Jetson Orin实测

OLLAMA部署本地大模型轻量高效&#xff1a;LFM2.5-1.2B-Thinking在Jetson Orin实测 1. 为什么LFM2.5-1.2B-Thinking值得在边缘设备上跑 你有没有试过在Jetson Orin上跑一个真正能思考、能推理的本地大模型&#xff1f;不是那种只能接个简单问答的玩具&#xff0c;而是能理解上…

作者头像 李华