PmsSkuInfo pmsSkuInfo = null;
Jedis jedis = redisUtil.getJedis();
String skuKey = "sku:" + skuId + ":info";
String skuJson = jedis.get(skuKey);
if(StringUtils.isNoneBlank(skuJson)){
pmsSkuInfo = JSON.parseObject(skuJson, PmsSkuInfo.class);
}else{
//nx redis自带分布式锁,只有为空时才可以set成功
String token = UUID.randomUUID().toString();
String OK = jedis.set("sku:" + skuId + ":lock", token, "nx", "px", 10000);
if(StringUtils.isNotBlank(OK) && OK.equals("OK")){
pmsSkuInfo = pmsSkuInfoMapper.selectByPrimaryKey(skuId);
//mysql查询结果存入redis
if(null != pmsSkuInfo){
jedis.set(skuKey, JSON.toJSONString(pmsSkuInfo));
}else{
//数据库中不存在sku
//为了防止缓存穿透,将null值或者空字符串值设置给redis
jedis.setex(skuKey, 60*3, JSON.toJSONString(""));
}
//在访问mysql后,将mysql的分布式锁释放
String lockToken = jedis.get("sku:" + skuId + ":lock");
//说明:当刚好锁过期,其他线程获取到锁,本线程删除锁时,删除了其他线程获取的锁,所以需要判断删除的锁是不是自己的锁
if(StringUtils.isNotBlank(lockToken) && lockToken.equals(token)){
//说明:在判断locktoken时刚好锁过期了,其他线程获取到锁,然后本线程把其他线程的锁删除了,所以使用lua脚本,缩短时间
//jedis.eval("lua") 可以用lua脚本,在查询到key时同时删除该key,防止高并发下发生特殊情况
jedis.del("sku:" + skuId + ":lock");//用token确认锁是自己的
}
}else{
//设置失败,自旋(该线程在睡眠几秒后,重新访问)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getSkuById(skuId);
}
}
jedis.close();
//sku图片集合
List<PmsSkuImage> pmsSkuImageList = pmsSkuImageMapper.selectBySkuId(skuId);
pmsSkuInfo.setSkuImageList(pmsSkuImageList);
return pmsSkuInfo;