Redis与SpringBoot整合:
第一步:在项目中引入
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
第二步:将连接池和配置类创建好
RedisUtil:
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class RedisUtil { private JedisPool jedisPool; public void initPool(String host,int port ,int database){ JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(200); poolConfig.setMaxIdle(30); poolConfig.setBlockWhenExhausted(true); poolConfig.setMaxWaitMillis(10*1000); poolConfig.setTestOnBorrow(true); jedisPool=new JedisPool(poolConfig,host,port,20*1000); } public Jedis getJedis(){ Jedis jedis = jedisPool.getResource(); return jedis; } }
RedisConfig:
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration//Spring容器中的注解 public class RedisConfig { //读取配置文件中的redis的ip地址,@Value注入赋值 @Value("${spring.redis.host:disabled}") private String host; @Value("${spring.redis.port:0}") private int port; @Value("${spring.redis.database:0}") private int database; @Bean//将返回值给Spring,Spring容器中就有了RedisUtil(连接池) public RedisUtil getRedisUtil(){ if(host.equals("disabled")){ return null; } RedisUtil redisUtil=new RedisUtil(); redisUtil.initPool(host,port,database); return redisUtil; } }
在哪个项目中使用Redis就在application.properties中配置以下:
客户端登录:
cd /usr/local/redis/bin
./redis-cli -h 192.168.0.100 -p 6379
192.168.0.100:6379> ping
PONG
测试一下:
@RunWith(SpringRunner.class) @SpringBootTest public class GmallManageServiceApplicationTests { @Autowired RedisUtil redisUtil; @Test public void contextLoads() { Jedis jedis = redisUtil.getJedis(); String ping = jedis.ping(); System.out.println(ping); } }
使用redis进行业务开发
开始开发先说明redis key的命名规范,由于Redis不像数据库表那样有结构,其所有的数据全靠key进行索引,所以redis数据的可读性,全依靠key。
企业中最常用的方式就是:object:id:field
比如:sku:1314:info
user:1092:password
拿一个之前的例子:
比如:sku:1314:info user:1092:password 拿一个之前的例子: public
以上基本实现使用缓存的方案。
高并发时可能会出现的问题:
但在高并发环境下还有如下三个问题。
- 如果redis宕机了,或者链接不上,怎么办?
- 如果redis缓存在高峰期到期失效,在这个时刻请求会向雪崩一样,直接访问数据库如何处理?
- 如果用户不停地查询一条不存在的数据,缓存没有,数据库也没有,那么会出现什么情况,如何处理?
public SkuInfo getSkuInfo(String skuId){ SkuInfo skuInfo = null; try { Jedis jedis = redisUtil.getJedis(); String skuInfoKey = ManageConst.SKUKEY_PREFIX + skuId + ManageConst.SKUKEY_SUFFIX; String skuInfoJson = jedis.get(skuInfoKey); if (skuInfoJson == null || skuInfoJson.length() == 0) { System.err.println(Thread.currentThread().getName()+"缓存未命中!"); String skuLockKey = ManageConst.SKUKEY_PREFIX + skuId + ManageConst.SKULOCK_SUFFIX; String lock = jedis.set(skuLockKey, "OK", "NX", "PX", ManageConst.SKULOCK_EXPIRE_PX); if ("OK".equals(lock) ){ System.err.println(Thread.currentThread().getName()+"获得分布式锁!"); skuInfo = getSkuInfoFromDB(skuId); if(skuInfo==null){ jedis.setex(skuInfoKey, ManageConst.SKUKEY_TIMEOUT, "empty"); return null; } String skuInfoJsonNew = JSON.toJSONString(skuInfo); jedis.setex(skuInfoKey, ManageConst.SKUKEY_TIMEOUT, skuInfoJsonNew); jedis.close(); return skuInfo; }else{ System.err.println(Thread.currentThread().getName()+"未获得分布式锁,开始自旋!"); Thread.sleep(1000); jedis.close(); return getSkuInfo( skuId); } } else if(skuInfoJson.equals("empty")){ return null; } else { System.err.println(Thread.currentThread().getName()+"缓存已命中!!!!!!!!!!!!!!!!!!!"); skuInfo = JSON.parseObject(skuInfoJson, SkuInfo.class); jedis.close(); return skuInfo; } }catch (JedisConnectionException e){ e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } return getSkuInfoFromDB(skuId); }
最近写的也一样:
controller:
@RequestMapping("{skuId}.html") public String item(@PathVariable("skuId") String skuId, ModelMap map, HttpServletRequest request){ SkuInfo skuInfo = skuService.item(skuId,request.getRemoteAddr()); }
service接口我就不写了
Serviceimpl:
@Override public SkuInfo item(String skuId,String ip) { System.out.println(ip+"访问"+skuId+"商品"); SkuInfo skuInfo = null; //从redis获取redis的客户端jedis Jedis jedis = redisUtil.getJedis(); // 从缓存中取出skuId的数据 String skuInfoStr = jedis.get("sku:"+skuId+":info"); //Json格式转成实体类类型 skuInfo = JSON.parseObject(skuInfoStr, SkuInfo.class); //从db中取出sku的数据 //缓存中没有 if(skuInfo == null){ System.out.println(ip+"发现缓存中没有"+skuId+"商品数据,申请分布式锁"); // 拿到分布式锁 String OK = jedis.set("sku:" + skuId + ":lock", "1", "nx", "px", 10000); if(StringUtils.isBlank(OK)){ System.out.println(ip+"分布式锁申请失败,三秒后开始自旋"); // 缓存锁被占用,等一会儿继续申请 try { Thread.sleep(3000);//让它等3秒 } catch (InterruptedException e) { e.printStackTrace(); } return item(skuId,ip);//自旋,这里没有启动新线程,item(skuId,ip);才会启动新线程 }else{ System.out.println(ip+"分布式锁申请成功,访问数据库"); // 拿到缓存锁,可以访问数据库 skuInfo = getSkuInfo(skuId); } System.out.println(ip+"成功访问数据库后,归还锁,将"+skuId+"商品放入缓存"); jedis.del("sku:"+skuId+":lock"); } //关闭redis客户端 jedis.close(); return skuInfo; }
getSkuInfo方法:
public SkuInfo getSkuInfo(String skuId) { SkuInfo skuInfo = new SkuInfo(); skuInfo.setId(skuId); SkuInfo skuInfos = skuInfoMapper.selectOne(skuInfo); SkuImage skuImage = new SkuImage(); skuImage.setSkuId(skuId); List<SkuImage> skuImages = skuImageMapper.select(skuImage); skuInfos.setSkuImageList(skuImages); return skuInfos; }
如果对于
String skuInfoStr = jedis.get("sku:"+skuId+":info");
String OK = jedis.set("sku:" + skuId + ":lock", "1", "nx", "px", 10000);
jedis.del("sku:"+skuId+":lock");
不太理解,大家可以看看前一章节的博客,或者去官网查看
我这里截下来一部分