项目概述
本项目基于 SpringBoot 开发,采用前后端分离架构。通过 Redis 集群、Tomcat 集群及 MySQL 集群提升服务性能,实现了短信登录、商户查询缓存、优惠券秒杀、附近商户、UV 统计、用户签到、好友关注、达人探店等核心功能模块。
技术栈
SpringBoot + Nginx + MySQL + Lombok + MyBatis-Plus + Hutool + Redis
核心难点与解决方案
1. 集群模式下的 Session 共享
在分布式环境中,多台 Tomcat 实例并不共享 Session 存储空间。当请求切换到不同服务节点时,会导致用户状态丢失。我们采用 Redis 替代本地 Session 存储用户信息,注册用户时生成随机 Token 作为 Key 存入 Redis。
拦截器分层设计: 系统设置了两层拦截器以平衡职责与性能。
- 第一层(全局处理): 获取 Token,查询 Redis 中的用户信息,刷新 Token 有效期。将用户信息存入 ThreadLocal,供后续业务复用。
- 第二层(权限校验): 专注于验证特定路径的登录逻辑。若路径需要登录但用户未登录,直接拦截。
这种设计避免了在每个请求中都进行重复的 Redis 查询,同时确保了所有请求都能正确刷新 Token 时间。
2. 缓存一致性策略
采用 Cache Aside 模式解决数据库与缓存的一致性问题。读写操作使用 Redisson 实现的读写锁:读时加共享锁(读读不互斥),写时加排他锁(读写互斥),确保更新数据时不会读取到脏数据。
3. 高并发缓存防护
缓存穿透
恶意用户伪造不存在的 ID 发起请求,导致大量请求直达数据库。
- 校验机制: 对请求参数进行类型校验。
- 布隆过滤器: 提前将有效 ID 存入过滤器,不存在则直接返回。
- 缓存空值: 查询数据库无结果时,仍缓存一个空值或特殊标记,并设置较短过期时间。
缓存雪崩
大量 Key 在同一时刻失效,导致请求瞬间涌向数据库。
- 同步锁: 控制查询线程,同一时间仅允许一个线程回源。
- 随机过期时间: 在基础过期时间上增加随机偏移,避免集体失效。
- 缓存预热: 启动时主动将热点数据加载至缓存。
缓存击穿
热点 Key 失效后,大量并发请求同时访问数据库。
- 互斥锁: 利用 Redis
setnx实现,只有获取锁的线程负责重建缓存,其他线程等待。注意锁的有效期需大于业务处理时长。 - 逻辑过期: 适用于读多写少场景。不设置物理过期时间,而是判断逻辑过期时间。若过期,开启子线程异步重建缓存,主线程返回旧数据。此方案牺牲内存换取性能,但需注意脏读风险。
4. 秒杀业务优化
资格预检与超卖控制
使用 Redis + Lua 脚本实现原子性的资格预检(库存判断、用户去重、扣减库存)。Lua 脚本保证了多条指令的原子性,避免了传统多命令方式的竞态条件。
针对超卖问题,采用乐观锁 CAS 算法。在 SQL 更新时增加 stock > 0 的条件,利用数据库行锁保证并发安全。
// 示例:乐观锁更新库存
seckillVoucherService.update()
.setSql("stock = stock - 1")
.eq("voucher_id", voucherId)
.gt("stock", )
.update();


