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");

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

    我这里截下来一部分

  • 相关阅读:
    必读:Spark与kafka010整合
    6 个开源的家庭自己主动化工具 | Linux 中国
    承上 DBlink 与 SCN | 新增视图找出外部 SCN 跳变
    NetBeans使用Consolas中文乱码的解决
    IDEA community + Gradle + Gretty 调试 servlet 应用 + war包部署到tomcat
    解决ubuntu下IntelliJ IDEA无法锁定到启动器的问题
    virtualbox安装android6.0并设置分辨率为1920x1080x32
    Ubuntu安装Sqlite报错:No module named 'ConfigParser'
    Linux常用指令笔记
    解决Warning Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock.
  • 原文地址:https://www.cnblogs.com/javawxid/p/12812030.html
Copyright © 2011-2022 走看看