想象一下,你正在开发一个日活用户10万+的电商平台,随着用户量增长,每次商品详情页加载都要从MySQL数据库查询,高峰期数据库CPU直接飙到90%,页面打开速度从1秒变成了5秒,客服电话被打爆,技术部连夜开会——这就是我们团队上个月的真实遭遇。
解决方案很简单:引入Redis缓存高频访问的商品数据,但具体如何在SSM(Spring+SpringMVC+MyBatis)项目中实现呢?下面我就把实战经验分享给你。
首先在pom.xml中添加Redis相关依赖(我们使用Jedis客户端):
<!-- Redis核心依赖 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.3.1</version> </dependency> <!-- Spring Data Redis --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.7.5</version> </dependency>
在applicationContext.xml中添加Redis配置:
<!-- Redis连接池配置 --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="100"/> <!-- 最大连接数 --> <property name="maxIdle" value="20"/> <!-- 最大空闲连接 --> <property name="minIdle" value="5"/> <!-- 最小空闲连接 --> </bean> <!-- Redis连接工厂 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="127.0.0.1"/> <property name="port" value="6379"/> <property name="password" value="yourpassword"/> <property name="poolConfig" ref="jedisPoolConfig"/> <property name="usePool" value="true"/> </bean> <!-- Redis模板 --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory"/> <!-- 序列化方式配置 --> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/> </property> </bean>
@Component public class RedisUtil { @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 普通缓存放入 * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 普通缓存获取 * @param key 键 * @return 值 */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); } // 其他常用方法:expire、delete、increment等... }
@Service public class ProductServiceImpl implements ProductService { @Autowired private ProductMapper productMapper; @Autowired private RedisUtil redisUtil; private static final String PRODUCT_CACHE_PREFIX = "product:"; @Override public Product getProductById(Long id) { String cacheKey = PRODUCT_CACHE_PREFIX + id; // 1. 先查缓存 Product product = (Product) redisUtil.get(cacheKey); if (product != null) { System.out.println("从Redis获取商品数据:" + id); return product; } // 2. 缓存没有则查数据库 System.out.println("从MySQL查询商品数据:" + id); product = productMapper.selectByPrimaryKey(id); if (product == null) { return null; } // 3. 数据库查询结果写入缓存(设置30分钟过期) redisUtil.set(cacheKey, product, 30 * 60); return product; } @Override public boolean updateProduct(Product product) { // 先更新数据库 boolean success = productMapper.updateByPrimaryKey(product) > 0; if (success) { // 数据库更新成功后,删除缓存(下次查询自动重建) String cacheKey = PRODUCT_CACHE_PREFIX + product.getId(); redisUtil.delete(cacheKey); } return success; } }
public Product getProductByIdWithNullCache(Long id) { String cacheKey = PRODUCT_CACHE_PREFIX + id; Product product = (Product) redisUtil.get(cacheKey); if (product != null) { // 特殊标记的空对象 if (product.getId() == -1) { return null; } return product; } product = productMapper.selectByPrimaryKey(id); if (product == null) { // 缓存空对象,设置较短过期时间 Product nullProduct = new Product(); nullProduct.setId(-1L); // 特殊标记 redisUtil.set(cacheKey, nullProduct, 5 * 60); return null; } redisUtil.set(cacheKey, product, 30 * 60); return product; }
// 在设置缓存时,基础时间加上随机值 Random random = new Random(); int expireTime = 30 * 60 + random.nextInt(600); // 30-40分钟随机 redisUtil.set(cacheKey, product, expireTime);
序列化问题:早期直接使用JdkSerializationRedisSerializer,导致Redis中存储的数据可读性差且占用空间大,后来改用Jackson2JsonRedisSerializer解决了这个问题。
连接泄漏:没有正确配置连接池时,高并发下会出现连接不够用的情况,通过合理设置maxTotal和maxIdle参数解决。
缓存一致性:最初采用先更新数据库再更新缓存的策略,但在高并发下会出现短暂的数据不一致,后来改为先更新数据库再删除缓存,虽然可能产生一次缓存未命中,但保证了数据最终一致。
在我们的电商项目中,引入Redis前后关键接口的响应时间对比:
场景 | 平均响应时间(ms) | QPS |
---|---|---|
直接查询MySQL | 120 | 450 |
使用Redis缓存 | 15 | 3200 |
缓存穿透防护方案 | 18 | 3000 |
可以看到,性能提升非常明显,特别是QPS(每秒查询率)提升了7倍多。
Key设计规范:采用"业务名:表名:id"的格式,如"shop:product:1",清晰且避免冲突
过期时间设置:热点数据30-60分钟,配置类数据可以更长,及时性要求高的数据设置短一些
写策略选择:
监控指标:
通过这次Redis集成,我们的电商平台扛住了618大促的流量洪峰,希望这篇实战指南也能帮助你解决SSM项目中的性能瓶颈问题,技术选型要结合实际业务场景,没有银弹,只有最适合的解决方案。
本文由 次雪松 于2025-08-02发表在【云服务器提供商】,文中图片由(次雪松)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/511074.html
发表评论