第一步,安装Redis,启动后,通过如下代码测试Redis
Redis建立连接
Jedis jedis = new Jedis("127.0.0.1");
jedis.auth("youxin11");
//使用jedis操作redis
jedis.set("test123", "my first jedis test");
String string = jedis.get("test123");
System.out.println(string);
//关闭连接,每次使用完毕后关闭连接。连接池回收资源。
jedis.close();
Redis连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(redisConfig.getPoolMaxIdle());
poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal());
poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxWait() * 1000);
JedisPool pool = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(),
redisConfig.getTimeout()*1000, redisConfig.getPassword(), 0);
Jedis jedis = pool.getResource();
//使用jedis操作redis
jedis.set("test123", "my first jedis test");
String string = jedis.get("test123");
System.out.println(string);
//关闭连接,每次使用完毕后关闭连接。连接池回收资源。
jedis.close();
//关闭连接池。
pool.close();
分布式项目中,通过引入redis,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时也带来了一些问题。最要害的问题,就是数据的一致性问题。严格意义上来讲,这个问题是无解的,如果对数据一致性要求非常高,就不要使用缓存。另外的一些典型问题,就是缓存穿透、缓存雪崩和缓存击穿。
缓存穿透,是指查询一个数据库一定不存在的数据。
正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。如果用户发起id为“-1”或id特别大不存在的数据。这时很可能是攻击者,攻击会导致数据库压力过大。
1,接口层增加校验。对id做基础校验,id<=0的直接拦截;
2,从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value键值对写为key-null,缓存有效时间可以设置短点,如60秒(设置太长会导致正常情况也没法使用),这样可以防止攻击用户反复用同一个id暴力攻击。
@Reference(version = "1.0.0")
private GoodsService goodsService;
@Autowired
private CacheService cacheService;
@RequestMapping(value = "detail", method = RequestMethod.POST)
public Object detail(HttpServletRequest request) throws Exception{
String requestStr = RequestStr.geRequestStr(request);
JSONObject jsonObject = JSONObject.parseObject(RequestStr);
if(StringUtils.isEmpty(jsonObject.getString("goodsId"))){
throw new DescribeExceeption(ExcetpionEnum.PARAM_ERROR);
}
int goodsId = jsonObject.getInteger("goodsId");
if(goodsId <= 0){
//当id小于等于0时,我们认为是一场操作
throw new DescribeExceeption(ExcetpionEnum.OPERATE_ERROR);
}
GoodsInfo goods = (GoodsInfo) cacheService.getCacheByKey("lyn_goods:" + goodsId);
if(null == goods){
goods = goodsService.findGoodsInfoByPrimary(goodsId);
//非法数据也存入缓存,有效时间较短,防止同一ID,短时间,重复查询
if(null != goods){
cacheService.setCacheToRedis("lyn_goods:" + goodsId, goods, 18000);
}else{
cacheService.setCacheToRedis("lyn_goods:" + goodsId, goods, 60);
}
}
return ResultUtils.success(goods);
}
缓存雪崩,是指在某一个时间段,缓存集中过期失效。产生雪崩的原因之一,比如马上就到618了,很快就会迎来一波抢购,这些要抢购的商品在同一时间点(17号23点放入)比较集中的放入了缓存,假设缓存两个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而此时对这批商品的访问查询,都落到了数据库身上,这时对于数据库而言,就会带来极大的压力。
1,在设置数据缓存有效期时,在时间后加上一个随机因子。
2,分散缓存过期时间,将热门类数据缓存时间长一点,冷门类的短一点。
3,设置热点数据永不过期。
GoodsInfo goods = goodsService.findGoodsInfoByPrimary(goodsId);
if(null != goods){
Random random = new Random();
//根据是否为热点数据,设置数据有效时间(有效时间,增加随机银子判断)
long expire = goods.getIsHotSell() ? 3600 + random.nextInt() * 3600 : 600 + random.nextInt() * 600;
cacheService.setCacheToRedis("lyn_goods:" + goodsId, goods, expire);
}
缓存击穿,是指一个key非常热点,高并发集中对这个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像春运期间火车站售票大厅,本来那些设在门口和广场的自助机可以办理售票,结果自助机瞬间全部瘫痪,造成大量买票的人涌进售票大厅人工窗口。
1,设置热点数据永远不过期;
2,加互斥锁,互斥锁参考代码如下:
GoodsInfo goods = (GoodsInfo) cacheService.getCacheByKey("lyn_goods:" + goodsId);
if(null != goods){
String key = "lyn_goods_detail_lock";
if(cacheService.lock(key, "0")){
goods = this.getGoodsInfo(goodsId);
cacheService.unLock(key);
}else{
Thread.sleep(100);
goods = this.getGoodsInfo(goodsId);
}
}
Redis提供了将数据定期自动持久化至硬盘的能力,包括RDB和AOF两种方案,两种方案分别有其长处和短板,可以配合起来同时运行,确保数据的稳定性。
RDB方式是一种快照式的持久化方法,将某一时刻的数据持久化到磁盘中。并在启动时自动加载rdb文件,恢复之前保存的数据。可以在配置文件中配置Redis进行快照保存的时机:
save [60] [100]
//会让Redis每60秒检查一次数据变更情况,如果发生了100次或以上的数据变更,则进行RDB快照保存。可以配置多条save指令,让Redis执行多级的快照保存策略。Redis默认开启RDB快照。
采用AOF持久方式时,Redis会把每一个写请求都记录在一个日志文件里。在Redis重启时,会把AOF文件中记录的所有写操作顺序执行一遍,确保数据恢复到最新。AOF默认是关闭的,如要开启,进行如下配置:
appendonly yes
AOF提供了三种fsync配置,always/everysec/no,通过[appendfsync]指定:appendfsync no:不进行fsync,将flush文件的时机交给OS决定,速度最快;
appendfsync always:每写入一条日志就进行一次fsync操作,数据安全性最高,但速度最慢;
appendfsync everysec:折中的做法,交由后台线程每秒fsync一次;
作者:Java开发者记录站
链接:https://www.jianshu.com/p/ebca78731137
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。