zoukankan      html  css  js  c++  java
  • Redis的使用

    一、Redis的安装

    1、安装C语言编译环境:yum install -y gcc-c++

    2、解压reidstar -zxvf redis-4.0.2.tar.gz -C /opt

    3、修改redis编译后的路径(修改解压目录src/Makefile):
    PREFIX?=/usr/local/redis

    4、编译-进去redis的解压目录:make install

    5、定制配置项启动

    ​ 复制redis.confcp /opt/redis-4.0.2/redis.conf /usr/local/redis/;

    ​ 修改配置项:

    配置项名称 作用 取值
    daemonize 控制是否以守护进程形式运行redis服务器 yes
    logfile 指定日志文件位置 "/var/logs/redis.log"
    dir redis工作目录 /usr/local/redis
    protected-mode 关闭保护,这样才能远程访问 no

    注释掉#bind 127.0.0.1

    二、Redis的使用

    1、启动redis服务

    ./redis-server ../redis-conf

    2、引入reids客户端

    <dependency>
    	<groupId>redis.clients</groupId>
    	<artifactId>jedis</artifactId>
    </dependency>
    

    3、在application.properties配置redis

    spring.redis.host=192.168.163.211
    spring.redis.port=6379
    spring.redis.database=0
    

    3、编写ReidsUtil工具类

    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;
        }
    }
    

    4、编写RedisConfig配置类同时将RedisUtil注入到容器中

    @Configuration
    public class RedisConfig {
    
        //读取配置文件中的redis的ip地址
        @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
        public RedisUtil getRedisUtil(){
            if(host.equals("disabled")){
                return null;
            }
            RedisUtil redisUtil=new RedisUtil();
            redisUtil.initPool(host,port,database);
            return redisUtil;
        }
    }
    

    5、redis的使用

    @Override
    public SkuInfo item(String skuId) {
        SkuInfo skuInfo = null;
        //从缓存中取出sku的数据
        Jedis jedis = redisUtil.getJedis();
        String skuInfoStr = jedis.get("sku:" + skuId + ":info");
    
        skuInfo = JSON.parseObject(skuInfoStr, SkuInfo.class);
        //如果缓存中没有数据时,则去访问数据库
        if (skuInfo == null) {
    
            //设置分布式锁(在Redis缓存在高并发下宕机后,为了防止多请求访问数据库导致数据库宕机,则设置分布式锁)
            String OK = jedis.set("sku:" + skuId + ":lock", "1", "nx");
            //如果返回的OK为null的话则存在分布式锁,如果不为null,则设置分布式锁成功
            if (StringUtils.isBlank(OK)){
                //分布式锁被占用,设置一定时间线程睡眠时间,等时间过后再次请求
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //自旋(递归的访问过程,不会开启新的线程)
                return item(skuId);
            }else {
                //拿到分布式锁,可以访问数据库
                skuInfo = itemFromDB(skuId);
            }
            jedis.del("sku:" + skuId + ":lock");
            //同步缓存到Redis
            jedis.set("sku:" + skuId + ":info", JSON.toJSONString(skuInfo));
        }
        jedis.close();
        return skuInfo;
    }
    

    6、redisKey的命名规范

    由于redis不像数据库那样有结构,其所以的数据全靠key进行索引,所以redis数据的可读性全依靠key.
    企业中最常用的方式就是:object:id:field
    比如:sku:1314:info; user:1092:password;

    三、Redis做分布式锁

    ​ 通过UUID生成一个唯一值,然后通过setnx将该值存入到redis中(可以设置过期时间),这样就设置了分布式锁。如何解锁呢?通过UUID的值和redis中的值进行比较,如果相同,则删除redis中的该值,就释放了锁。

    public String func(Integer id,String str){
    	//px为毫秒 ex为秒
        String OK = jedis.set("item:" + id + ":lock", UUID.randomUUID(), "nx", "px", 10000);
        if(StringUtils.isBlank(OK)){
            //自旋
            TimeUnit.SECONDS.sleep(10);
            //执行一次
            return func(id,str);
        }else{
            //执行业务操作
            ......
        }
        jedis.del("item:" + id + ":lock")
    }
    

    四、Redis与数据库数据一致性问题

    redismysql数据同步是先删redis后写mysql还是先写mysql后删redis?

    ​ 这两种方式均会出现数据不一致问题。因为写和读是并发的,没法保证顺序,如果删了缓存,还没来得及写库,另一个线程就来读取,发现缓存为空,则去数据库汇总读取数据写入缓存,此时缓存中为脏数据;如果先写库后删缓存,如果删缓存的线程宕机没有删除掉缓存,此时也会出现数据不一致的问题。如果是redis集群,主从复制模式在复制的过程中存在一定的时间延迟,也会导致数据不一致问题。

    【解决方案】:优先考虑先写入数据库,在一定的情况下可以允许数据缓存有误差,但是比如在结算等重要步骤时不允许数据不一致,此时应该进行数据一致性校验,校验成功后才能进行操作。

  • 相关阅读:
    SpringMVC
    SpringMVC
    SpringMVC
    Spring
    Spring
    值类型和引用类型
    判断字符串的开头和结尾
    二分法(课后)
    验证码
    从1-36中随机出6个不相等的数
  • 原文地址:https://www.cnblogs.com/hucheng1997/p/11803859.html
Copyright © 2011-2022 走看看