最近,在做一个商城项目的过程中使用了redis,也碰到了一些问题,记录下。
1.使用redis的过程中抛出了Could not get a resource from the pool的异常。
此时我修改了redis.conf的内容,变为以下
daemonize no
protected-mode no
#bind 127.0.0.1
但是发现不起作用,原来是redis启动的时候如果不指定配置文件,则还会按照默认的启动方式,此时只需要启动的时候附带上redis.conf就可以了。
./redis-server redis.conf
2、redis用在项目的时候要考虑以下问题:
2.1、缓存雪崩
所有的缓存数据在同一时间过期,导致所有数据都访问数据库,使得数据库崩溃,系统崩溃。
解决:给所有的缓存设置一个随机的过期时间。
2.2、缓存穿透
攻击者请求的数据是数据库和redis中都没有的数据,使得每次都访问数据库,已达到攻击效果。
解决:null或者空字符串值设置给redis。
2.3、缓存击穿
某一个热度高的商品在某一个时间缓存过期了,导致都访问数据库。
解决:使用分布式锁,一次都只能有一个请求访问数据库。
以下是代码,以及一些详细过程。
public PmsSkuInfo getSkuById(String skuId,String ip) {
System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"进入的商品详情的请求");
PmsSkuInfo pmsSkuInfo = new PmsSkuInfo();
// 链接缓存
Jedis jedis = redisUtil.getJedis();
// 查询缓存
String skuKey = "sku:"+skuId+":info";
String skuJson = jedis.get(skuKey);
if(StringUtils.isNotBlank(skuJson)){//if(skuJson!=null&&!skuJson.equals(""))
System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"从缓存中获取商品详情");
pmsSkuInfo = JSON.parseObject(skuJson, PmsSkuInfo.class);
}else{
// 如果缓存中没有,查询mysql
System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"发现缓存中没有,申请缓存的分布式锁:"+"sku:" + skuId + ":lock");
// 设置分布式锁,解决缓存击穿问题。使用token防止错误删除别人的分布
//式锁,不过,即使用了token,也可能删除的时候刚好另外一个创建了,
//也可能导致删除错误,此时使用lua脚本,直接删除,没有空闲的时间。
String token = UUID.randomUUID().toString();
String OK = jedis.set("sku:" + skuId + ":lock", token, "nx", "px", 10*1000);// 拿到锁的线程有10秒的过期时间
if(StringUtils.isNotBlank(OK)&&OK.equals("OK")){
// 设置成功,有权在10秒的过期时间内访问数据库
System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"有权在10秒的过期时间内访问数据库:"+"sku:" + skuId + ":lock");
pmsSkuInfo = getSkuByIdFromDb(skuId);
if(pmsSkuInfo!=null){
// mysql查询结果存入redis
jedis.set("sku:"+skuId+":info",JSON.toJSONString(pmsSkuInfo));
}else{
// 数据库中不存在该sku
// 为了防止缓存穿透将,
jedis.setex("sku:"+skuId+":info",60*3,JSON.toJSONString(""));
}null或者空字符串值设置给redis
// 在访问mysql后,将mysql的分布锁释放
System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"使用完毕,将锁归还:"+"sku:" + skuId + ":lock");
String lockToken = jedis.get("sku:" + skuId + ":lock");
if(StringUtils.isNotBlank(lockToken)&&lockToken.equals(token)){
//jedis.eval("lua");可与用lua脚本,在查询到key的同时删除该key,防止高并发下的意外的发生
jedis.del("sku:" + skuId + ":lock");// 用token确认删除的是自己的sku的锁
}
}else{
// 设置失败,自旋(该线程在睡眠几秒后,重新尝试访问本方法)
System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"没有拿到锁,开始自旋");
return getSkuById(skuId,ip);
}
}
jedis.close();
return pmsSkuInfo;
}