redis的整合
引入依赖
1 <dependency> 2 <groupId>redis.clients</groupId> 3 <artifactId>jedis</artifactId> 4 </dependency>
redis的工具类,将redis的池初始化到spring容器中
1 public class RedisUtil { 2 3 private JedisPool jedisPool; 4 5 public void initPool(String host,int port ,int database){ 6 JedisPoolConfig poolConfig = new JedisPoolConfig(); 7 poolConfig.setMaxTotal(200); 8 poolConfig.setMaxIdle(30); 9 poolConfig.setBlockWhenExhausted(true); 10 poolConfig.setMaxWaitMillis(10*1000); 11 poolConfig.setTestOnBorrow(true); 12 jedisPool=new JedisPool(poolConfig,host,port,20*1000); 13 } 14 15 public Jedis getJedis(){ 16 Jedis jedis = jedisPool.getResource(); 17 return jedis; 18 } 19 20 }
spring整合redis的配置类
1 @Configuration 2 public class RedisConfig { 3 //读取配置文件中的redis的ip地址 4 @Value("${spring.redis.host:disabled}") 5 private String host; 6 @Value("${spring.redis.port:0}") 7 private int port ; 8 @Value("${spring.redis.database:0}") 9 private int database; 10 @Bean 11 public RedisUtil getRedisUtil(){ 12 if(host.equals("disabled")){ 13 return null; 14 } 15 RedisUtil redisUtil=new RedisUtil(); 16 redisUtil.initPool(host,port,database); 17 return redisUtil; 18 } 19 }
application.properties
spring.redis.host=192.168.239.139 spring.redis.port=6379 spring.redis.database=0
缓存击穿
是某一个热点key在高并发访问的情况下,突然失效,导致大量的并发,引起mysql数据库压力瞬间增大,造成过大压力
击穿:在正常的访问情况下,如果缓存失效,如果保护mysql,重启缓存的过程,使用redis数据库的分布式锁,解决mysql的访问压力问题。
第一种分布式锁:redis自带一个分布式锁,set px nx
set sku:108:lock 1 px 600000 nx
px 600000 过期时间
nx 分布式锁参数,只有不存在才能返回成功
第二种分布式锁:redisson框架,一个redis的带有juc的lock功能的客户端的实现(既有jedis的功能,又有juc的锁功能)
例:通过skuId查询skuInfo
1 @Override 2 public PmsSkuInfo getSkuById(String skuId) { 3 4 PmsSkuInfo pmsSkuInfo = new PmsSkuInfo(); 5 6 7 //连接缓存,查询缓存 8 Jedis jedis = redisUtil.getJedis(); 9 10 11 //没有缓存,查询mysql 12 String skuKey = "sku:"+skuId+":info"; 13 String skuJson = jedis.get(skuKey); 14 15 if (StringUtils.isNotBlank(skuJson)){ 16 pmsSkuInfo = JSON.parseObject(skuJson, PmsSkuInfo.class); 17 }else { 18 19 //设置分布式锁 20 String token = UUID.randomUUID().toString(); 21 String OK = jedis.set("sku:" + skuId + ":lock", token, "nx", "px", 10*1000); 22 if (StringUtils.isNotBlank(OK)&&OK.equals("OK")){ 23 //设置成功,有权在10s内访问数据库 24 pmsSkuInfo = getSkuByIdFromDb(skuId); 25 26 try { 27 Thread.sleep(2000); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 32 if (pmsSkuInfo != null){ 33 //mysql存入redis 34 jedis.set("sku:"+skuId+":info",JSON.toJSONString(pmsSkuInfo)); 35 }else {
//数据库不存在该sku 36 //防止缓存穿透,值设为空字符串或null 37 jedis.setex("sku:"+skuId+":info",60*3,JSON.toJSONString("")); 38 } 39 40 String lockToken = jedis.get("sku:" + skuId + ":lock"); 41 if(StringUtils.isNotBlank(lockToken)&&lockToken.equals(token)){ //用token确认删除的是自己的锁 42 43 //在访问mysql后,将mysql的分布式锁删除 44 jedis.del("sku:" + skuId + ":lock"); 45 46 } 47 48 }else { 49 //设置失败,自旋(该线程在睡眠几秒后,重新尝试本方法) 50 return getSkuById(skuId); 51 } 52 53 } 54 jedis.close(); 55 56 return pmsSkuInfo; 57 58 }
49行中,如改为,则是错误的自旋
1 //设置失败,自旋(该线程在睡眠几秒后,重新尝试本方法) 2 try { 3 Thread.sleep(3000); 4 } catch (InterruptedException e) { 5 e.printStackTrace(); 6 } 7 8 getSkuById(skuId);
getSkuId(skuId) 表示又开新辟的线程重新进行访问,与原线程形成父子线程,
加入 return 后 ,return getSkuById(skuId) 表原线程重新进行访问