緩存設計原則
- 用快速存取設備,用內存
- 將緩存推到離用戶最近的地方
- 髒緩存處理(關鍵數據存儲到數據庫,數據庫數據與緩存中數據的一致性問題)
多級緩存
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中緩存的數據,可以做到實時的更新