zoukankan      html  css  js  c++  java
  • Redis数据结构存储系统:第二章:如何使用

    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

    拿一个之前的例子:

    public SkuInfo getSkuInfo(String skuId){
    
            Jedis jedis = redisUtil.getJedis();
            String skuKey= RedisConst.sku_prefix+skuId+RedisConst.skuInfo_suffix;
            String skuInfoJson = jedis.get(skuKey);
            if(skuInfoJson!=null ){
                System.err.println( Thread.currentThread().getName()+":命中缓存"  );
                SkuInfo skuInfo = JSON.parseObject(skuInfoJson, SkuInfo.class);
                jedis.close();
                return skuInfo;
            }else{
                   System.err.println( Thread.currentThread().getName()+":未命中缓存"  );
    
                    System.err.println( Thread.currentThread().getName()+": 查询数据##################### ##" );
                    SkuInfo skuInfoDB = getSkuInfoDB(skuId);
                    String skuInfoJsonStr = JSON.toJSONString(skuInfoDB);
                    jedis.setex(skuKey,RedisConst.skuinfo_exp_sec,skuInfoJsonStr);
                    System.err.println( Thread.currentThread().getName()+":数据库更新完毕############### #####" );
                    jedis.close();
                    return skuInfoDB;
            }
    }

    以上基本实现使用缓存的方案。

    高并发时可能会出现的问题:

    但在高并发环境下还有如下三个问题。

    1. 如果redis宕机了,或者链接不上,怎么办?
    2. 如果redis缓存在高峰期到期失效,在这个时刻请求会向雪崩一样,直接访问数据库如何处理?
    3.  如果用户不停地查询一条不存在的数据,缓存没有,数据库也没有,那么会出现什么情况,如何处理?
     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");

    不太理解,大家可以看看前一章节的博客,或者去官网查看

    我这里截下来一部分

  • 相关阅读:
    简单的模板解析函数
    HTML通过事件传递参数到js 二 event
    HTML通过事件传递参数到js一
    通过this获取当前点击选项相关数据
    LeetCode 20. 有效的括号(Valid Parentheses)
    LeetCode 459. 重复的子字符串(Repeated Substring Pattern)
    LeetCode 14. 最长公共前缀(Longest Common Prefix)
    LeetCode 168. Excel表列名称(Excel Sheet Column Title)
    LeetCode 171. Excel表列序号(Excel Sheet Column Number) 22
    LeetCode 665. 非递减数列(Non-decreasing Array)
  • 原文地址:https://www.cnblogs.com/javawxid/p/12812030.html
Copyright © 2011-2022 走看看