缓存设计原则
- 用快速存取设备,用内存
- 将缓存推到离用户最近的地方
- 脏缓存处理(关键数据存储到数据库,数据库数据与缓存中数据的一致性问题)
多级缓存
redis缓存
redis
本质上是一个数据库,
Redis商品详情动态内容的实现
在商品详情页,当客户端频繁访问某个数据库的时候,可以将数据库中查询出来的内容缓存到redis中,下一次当客户端发出同样请求的时候不会访问数据库而访问redis缓存。
//商品详情页浏览
@RequestMapping(value = "/get",method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType getItem(@RequestParam(name = "id")Integer id){
//根据商品的id到redis内获取商品的详情信息
ItemModel itemModel = (ItemModel)redisTemplate.opsForValue().get("item_"+id);
//若在redis内不存在对应的itemModel,则访问下游的service
if(itemModel==null){
itemModel = itemService.getItemById(id);
//若redis内没有数据则将查询出来的数据存储到redis中
redisTemplate.opsForValue().set("item_"+id,itemModel);
//10分中之内不会反复访问数据库
redisTemplate.expire("item_"+id,10, TimeUnit.MINUTES);
}
ItemVO itemVO = convertVOFromModel(itemModel);
return CommonReturnType.create(itemVO);
}
对redis存储的数据格式进行配置
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//首先解决key的序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
//解决value的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(DateTime.class,new JodaDateTimeJsonSerializer());
simpleModule.addDeserializer(DateTime.class,new JodaDateTimeJsonDeserializer());
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.registerModule(simpleModule);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
//从rdis中获取数据
“[“com.imooc.miaoshaproject.service.model.ItemModel”,{“id”:1,“title”:“iphone12”,“price”:[“java.math.BigDecimal”,21123],“stock”:97,“description”:”\xe4\xb8\x8d\xe9\x94\x99",“sales”:147,“imgUrl”:“https://i1.mifile.cn/a1/pms_1550642182.7527088!220x220.jpg”,“promoModel”:[“com.imooc.miaoshaproject.service.model.PromoModel”,{“id”:1,“status”:2,“promoName”:“iphone12”,“startDate”:“2020-06-01 08:39:47”,“endDate”:“2020-06-30 10:00:00”,“itemId”:1,“promoItemPrice”:[“java.math.BigDecimal”,10000]}]}]"
本地热点缓存(在JVM中的缓存)—使用Guava cache
容量大小的限制,很难做到实时更新
- 存放热点数据
- 脏读不敏感
- 内存可控
生命存活时间比redis中数据存活时间非常的短
- HashMap
- ConcurrentHashMap
Guava cache
Guava cache
可控制的大小和超时时间
可配置的lru策略
- 线程安全
导入jar包
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
编写缓存的处理方法
package com.imooc.miaoshaproject.service;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/27 0027 23:36
*/
//封装本地哦缓存操作类
public interface CacheService {
//存数据的方法
void setCommonCache(String key,Object value);
//取数据的方法
Object getFromCommonCache(String key);
}
//实现类
package com.imooc.miaoshaproject.service.impl;
import com.imooc.miaoshaproject.service.CacheService;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/27 0027 23:37
*/
@Service
public class CacheServiceImpl implements CacheService {
private com.google.common.cache.Cache<String,Object> commonCache = null;
@PostConstruct
public void init(){
commonCache = com.google.common.cache.CacheBuilder.newBuilder()
//设置缓存容器锤石容量为10
.initialCapacity(10)
//设置缓存中最大可以存储100个key,超过100个之后会按照LRU的策略移除缓存向
.maximumSize(100)
//设置写缓存后多少秒过期
.expireAfterWrite(60, TimeUnit.SECONDS)
.build();
}
@Override
public void setCommonCache(String key, Object value) {
commonCache.put(key,value);
}
@Override
public Object getFromCommonCache(String key) {
return commonCache.getIfPresent(key);
}
}
在controller中实现
/商品详情页浏览
@RequestMapping(value = "/get",method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType getItem(@RequestParam(name = "id")Integer id){
ItemModel itemModel = null;
//1、先取本地更缓存
itemModel = (ItemModel)cacheService.getFromCommonCache("item_"+id);
if(itemModel==null){
//根据商品的id到redis内获取商品的详情信息
itemModel = (ItemModel)redisTemplate.opsForValue().get("item_"+id);
//若在redis内不存在对应的itemModel,则访问下游的service
if(itemModel==null){
itemModel = itemService.getItemById(id);
//若redis内没有数据则将查询出来的数据存储到redis中
redisTemplate.opsForValue().set("item_"+id,itemModel);
//10分中之内不会反复访问数据库
redisTemplate.expire("item_"+id,10, TimeUnit.MINUTES);
}
//存入本地缓存
cacheService.setCommonCache("item_"+id,itemModel);
}
ItemVO itemVO = convertVOFromModel(itemModel);
return CommonReturnType.create(itemVO);
}
nginx proxy cache缓存
- ngix反向代理时才可以使用
- 依靠文件系统存索引级的文件
- 依靠内存缓存文件地址
这种方式不推荐,读取的数据还是需要从磁盘中读取,
nginx lua缓存
nginx lua脚本
OpenResty
使用lua shared dict:共享字典
OPenresy对Redis的支持
nginx读取redis中缓存的数据,可以做到实时的更新