共计 5728 个字符,预计需要花费 15 分钟才能阅读完成。
导读 | 本文通过场景分析给大家介绍了基于 Redis 的 List 实现特价商品列表, 本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下 |
1、场景分析
购物平台的特价商品列表,
商品特点:
商品有限,并发量非常的大。
考虑分页
传统解决方案:数据库 db,
但是在如此大的并发量的情况下,不可取。
一般会采用 redis 来处理。这些特价商品的数据不多,而且 redis 的 list 本身也支持分页。是天然处理这种列表的最佳选择解决方案。
2、分析
采用 list 数据,因为 list 数据结构有:lrange key 0 -1 可以进行数据的分页。
lpush products p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 | |
(integer) 10 | |
0 1 | lrange products|
1) "p10" | |
2) "p9" | |
2 3 | lrange products|
1) "p8" | |
2) "p7" | |
4 5 | lrange products|
1) "p6" | |
2) "p5" |
3、具体实现
购物平台的热门商品在双 11 的时候,可能有 100 多 w 需要搞活动:程序需要 5 分钟对特价商品进行刷新。
3.1 ProductListService 类
初始化的活动的商品信息 100 个(从数据库去查询)
@PostContrcut 使用
查询产品列表信息
换算的分页的起始位置和结束位置
package com.example.service; | |
import com.example.entity.Product; | |
import lombok.extern.slf4j.Slf4j; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.data.redis.core.RedisTemplate; | |
import org.springframework.stereotype.Service; | |
import javax.annotation.PostConstruct; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Random; | |
/** | |
* @Auther: 长颈鹿 | |
* @Date: 2021/08/29/18:00 | |
* @Description: | |
*/ | |
public class ProductListService { | |
private RedisTemplate redisTemplate; | |
// 数据热加载 | |
public void initData(){log.info("启动定时加载特价商品到 redis 的 list 中..."); | |
new Thread(() -> runCourse()).start();} | |
public void runCourse() {while (true) { | |
// 从数据库中查询出特价商品 | |
List<product> productList = this.findProductsDB(); | |
// 删除原来的特价商品 | |
this.redisTemplate.delete("product:hot:list"); | |
// 把特价商品添加到集合中 | |
this.redisTemplate.opsForList().leftPushAll("product:hot:list", productList); | |
try { | |
// 每隔一分钟执行一次 | |
Thread.sleep(1000 * 60); | |
log.info("定时刷新特价商品...."); | |
} catch (Exception ex) {ex.printStackTrace(); | |
} | |
} | |
} | |
/** | |
* 数据库中查询特价商品 | |
* | |
* @return | |
*/ | |
public List<product> findProductsDB() {//List<product> productList = productMapper.selectListHot(); | |
List<product> productList = new ArrayList(); | |
for (long i = 1; i | |
3.2 商品的数据接口的定义和展示及分页 | |
package com.example.controller; | |
import com.example.entity.Product; | |
import com.example.service.ProductListService; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.data.redis.core.RedisTemplate; | |
import org.springframework.util.CollectionUtils; | |
import org.springframework.web.bind.annotation.GetMapping; | |
import org.springframework.web.bind.annotation.RestController; | |
import java.util.List; | |
/** | |
* @Auther: 长颈鹿 | |
* @Date: 2021/08/29/18:04 | |
* @Description: | |
*/ | |
public class ProductListController { | |
private RedisTemplate redisTemplate; | |
private ProductListService productListService; | |
public List<product> findProducts(int pageNo, int pageSize) { | |
// 从那个集合去查询 | |
String key = "product:hot:list"; | |
// 分页的开始结束的换算 | |
if (pageNo | |
3.3 定时任务 | |
// 主要用于标记配置类,兼备 Component 的效果。@EnableScheduling // 开启定时任务 | |
public class SaticScheduleTask { | |
// 添加定时任务 | |
// 或直接指定时间间隔,例如:5 秒 | |
// @Scheduled(fixedRate=5000) | |
private void configureTasks() {System.err.println("执行静态定时任务时间:" + LocalDateTime.now()); | |
} | |
} | |
4、解决商品列表存在的缓存击穿问题 | |
4.1 如何引起的缓存击穿的情况 | |
public void runCourse() {while (true) { | |
// 从数据库中查询出特价商品 | |
List<product> productList = this.findProductsDB(); | |
// 删除原来的特价商品 | |
this.redisTemplate.delete("product:hot:list"); | |
// 把特价商品添加到集合中 需要时间 | |
this.redisTemplate.opsForList().leftPushAll("product:hot:list", productList); | |
try { | |
// 每隔一分钟执行一遍 | |
Thread.sleep(1000 * 60); | |
log.info("定时刷新特价商品...."); | |
} catch (Exception ex) {ex.printStackTrace(); | |
} | |
} | |
} | |
出现原因: | |
特价商品的数据更换需要时间,刚好特价商品还没有放入到 redis 缓存中。 | |
查询特价商品的并发量非常大,可能程序还正在写入特价商品到缓存中,这时查询缓存根本没有数据,就会直接冲入数据库中去查询特价商品。可能造成数据库冲垮。这个就叫做:缓存击穿 | |
4.2 解决方案 | |
主从轮询 | |
可以开辟两块 redis 的集合空间 A 和 B。定时器在更新缓存的时候,先更新 B 缓存,然后再更新 A 缓存。 | |
一定要按照特定顺序来处理。 | |
package com.example.service; | |
import com.example.entity.Product; | |
import lombok.extern.slf4j.Slf4j; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.data.redis.core.RedisTemplate; | |
import org.springframework.stereotype.Service; | |
import javax.annotation.PostConstruct; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Random; | |
/** | |
* @Auther: 长颈鹿 | |
* @Date: 2021/08/29/18:00 | |
* @Description: | |
*/ | |
public class ProductListService { | |
private RedisTemplate redisTemplate; | |
// 数据热加载 | |
public void initData(){log.info("启动定时加载特价商品到 redis 的 list 中..."); | |
new Thread(() -> runCourse()).start();} | |
public void runCourse() {while (true) { | |
// 从数据库中查询出特价商品 | |
List<product> productList = this.findProductsDB(); | |
// 删除原来的特价商品 | |
this.redisTemplate.delete("product:hot:slave:list"); | |
// 把特价商品添加到集合中 | |
this.redisTemplate.opsForList().leftPushAll("product:hot:slave:list", productList);// 删除原来的特价商品 | |
this.redisTemplate.delete("product:hot:master:list"); | |
// 把特价商品添加到集合中 | |
this.redisTemplate.opsForList().leftPushAll("product:hot:master:list", productList); | |
// // 删除原来的特价商品 | |
// this.redisTemplate.delete("product:hot:list"); | |
// // 把特价商品添加到集合中 | |
// this.redisTemplate.opsForList().leftPushAll("product:hot:list", productList); | |
try { | |
// 每隔一分钟执行一次 | |
Thread.sleep(1000 * 60); | |
log.info("定时刷新特价商品...."); | |
} catch (Exception ex) {ex.printStackTrace(); | |
} | |
} | |
} | |
/** | |
* 数据库中查询特价商品 | |
* | |
* @return | |
*/ | |
public List<product> findProductsDB() {//List<product> productList = productMapper.selectListHot(); | |
List<product> productList = new ArrayList(); | |
for (long i = 1; i | |
package com.example.controller; | |
import com.example.entity.Product; | |
import com.example.service.ProductListService; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.data.redis.core.RedisTemplate; | |
import org.springframework.util.CollectionUtils; | |
import org.springframework.web.bind.annotation.GetMapping; | |
import org.springframework.web.bind.annotation.RestController; | |
import java.util.List; | |
/** | |
* @Auther: 长颈鹿 | |
* @Date: 2021/08/29/18:04 | |
* @Description: | |
*/ | |
public class ProductListController { | |
private RedisTemplate redisTemplate; | |
private ProductListService productListService; | |
public List<product> findProducts(int pageNo, int pageSize) { | |
// 从那个集合去查询 | |
String master_key = "product:hot:master:list"; | |
String slave_key = "product:hot:slave:list"; | |
String key = "product:hot:list"; | |
// 分页的开始结束的换算 | |
if (pageNo | |
到此这篇关于基于 Redis 的 List 实现特价商品列表的文章就介绍到这了。 | |
阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配 | |
腾讯云新客低至 82 元 / 年,老客户 99 元 / 年 | |
代金券:在阿里云专用满减优惠券 | |
正文完
星哥玩云-微信公众号
