目录
1.为什么要使用缓存?
在 淘淘商城46-商品详情页面代码编写 中,我们根据商品id从数据库中查询tb_item表与tb_itemDesc表,其实我们会发现,商品的信息与商品描述,基本上十天半个月都不会发生改变,所以我们可以考虑使用redis缓存一些不经常更换但是访问量很大的数据。
2.缓存数据库中表的分析
2.1分析
当我们准备使用redis缓存表中的数据时,会发现一些难题。
1.我们是否要缓存所有商品数据
由于商品量大而且访问频繁,所有我们肯定不会存储所有商品数据,我们要设置热点商品缓存,对于冷门的商品,我们不进行缓存。如何区分热门的商品与冷门的商品?我们可以对缓存的商品设置超时时间,如果一个商品长时间没有被用户访问,就会超时,从redis缓存中删除。
2.选择redis哪种数据结构来缓存商品数据
3.如何缓存一张表中的数据
下面是tb_item表、tb_itemDesc表的结构:
由于我们要根据商品id,来获取tb_item表与tb_itemDesc表的某一个字段。在redis中,最能模拟数据库中两张以上的不同表的类型就是hash类型。
hash类型:使用key:表名、filed:主键id、value:整个字段的数据
比如我们要缓存商品id为536563:
key:tb_item、filed:536563、value:整个字段的值
key:tb_itemDesc、filed:536563、value:整个字段的值
但是我们要设置超时时间,在redis中hash类型并不能设置超时时间,所以我们并不能使用hash类型来缓存商品数据。
对于list、set、sortset在这里并不合适,我们只能使用String类型。
String类型是key:value的形式,如果以id作为key,tb_item与tb_itemDesc放入缓存时会发生冲突,所以需要构造一个key。
构造一个key的思路:可以加前缀方法对redis中的key进行归类。
比如要缓存id:123456的商品信息:
key:ITEM_INFO:123456:BASE
要缓存id:123456的商品描述:
key:ITEM_INFO:123456:DESC
这里的ITEM_INFO只是一个用于分类的前缀(或者说是表名),可以任意修改。12346是商品id。BASE与DESC是用于区分是商品信息还是商品描述的后缀。中间的冒号用来分割的。
2.2总结
使用redis中的String类型来缓存tb_item与tb_itmeDesc表中的字段信息,
构造"ITEM_INFO:123456:BASE"与"ITEM_INFO:123456:DESC"这样的key,通过前缀来模拟redis中的表。商品id标识不同商品,后缀BASE与DESC区分是商品信息还是商品描述。
3.后端代码实现
3.1引入redis客户端
redis客户端的代码与applicationContext-redis.xml配置在 淘淘商城28-使用spring管理通用连接redis的接口 中
同时需要在taotao-manager-service的pom.xml中加入依赖
<!-- Redis客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
3.2配置常量配置文件
由于商品缓存入redis需要一个前缀与过期时间,所以我们将这种常量存放在properties文件中。
在src/main/resources的resources下新建一个resource.properties
#redis缓存时分类的前缀
ITEM_INFO_KEY=ITEM_INFO
#缓存商品超时时间
ITEM_INFO_KEY_EXPIRE=86400
需要在spring的配置文件中配置加载properties文件
<context:property-placeholder location="classpath:resource/*.properties" />
3.3向业务逻辑中添加缓存代码
在ItemServiceImpl中添加缓存
添加思路:根据构造的key从缓存中查询是否有商品信息或者商品描述对应的值,如果有直接返回,并重新设置过期时间,如果没有则从数据库中查询,并添加到缓存中,同时添加过期时间,然后返回。
3.3.1注入常量与对象
将商品缓存的前缀、过期时间 、redis客户端对象依赖注入
/** 商品缓存的前缀与过期时间 */
@Value("${ITEM_INFO_KEY}")
private String ITEM_INFO_KEY;
@Value("${ITEM_INFO_KEY_EXPIRE}")
private Integer ITEM_INFO_KEY_EXPIRE;
/** redis单机连接 */
@Autowired
private JedisClient client;
3.3.2添加缓存
在ItemServiceImpl的getItemById与getItemDescById方法中添加缓存
具体可以看我代码上的注释:
/**
* 根据商品id获取商品详细信息
*/
@Override
public TbItem getItemById(Long itemId) {
// 1.封装成redis中的key
String key = ITEM_INFO_KEY + ":" + itemId + "" + ":BASE";
// 2.根据key从缓存中查询是否存在该商品信息,如果存在,直接返回。
try {
String tbItemJson = client.get(key);
if (StringUtils.isNotBlank(tbItemJson)) {
// 2.1设置过期时间
System.out.println("缓存中有商品信息,直接返回...");
client.expire(key, ITEM_INFO_KEY_EXPIRE);
return JsonUtils.jsonToPojo(tbItemJson, TbItem.class);
}
} catch (Exception e) {
e.printStackTrace();
}
// 3.否则,将商品信息放入缓存,并设置过期时间
TbItem tbItem = itemMapper.selectByPrimaryKey(itemId);
try {
if (tbItem != null) {
System.out.println("缓存中没有商品信息,设置缓存...");
client.set(key, JsonUtils.objectToJson(tbItem));
client.expire(key, ITEM_INFO_KEY_EXPIRE);
}
} catch (Exception e) {
e.printStackTrace();
}
return tbItem;
}
/**
* 根据商品id获取商品描述
*/
@Override
public TbItemDesc getItemDescById(Long itemId) {
// 1.封装成redis中的key
String key = ITEM_INFO_KEY + ":" + itemId + "" + ":DESC";
// 2.根据key从缓存中查询是否存在该商品描述,如果存在,直接返回。
try {
String tbItemDescJson = client.get(key);
if (StringUtils.isNotBlank(tbItemDescJson)) {
// 2.1设置过期时间
System.out.println("缓存中有商品描述,直接返回...");
client.expire(key, ITEM_INFO_KEY_EXPIRE);
return JsonUtils.jsonToPojo(tbItemDescJson, TbItemDesc.class);
}
} catch (Exception e) {
e.printStackTrace();
}
// 3.否则,将商品描述放入缓存,并设置过期时间
TbItemDesc tbItemDesc = itemDescMapper.selectByPrimaryKey(itemId);
try {
if (tbItemDesc != null) {
System.out.println("缓存中没有商品描述,设置缓存...");
client.set(key, JsonUtils.objectToJson(tbItemDesc));
client.expire(key, ITEM_INFO_KEY_EXPIRE);
}
} catch (Exception e) {
e.printStackTrace();
}
return tbItemDesc;
}
4.运行测试
在首页http://localhost:8082/ 搜索商品
进入商品搜索页面
点击其中一件商品详情查看
查看eclipse的console,缓存生效,
如果点击没有在缓存中的商品则会