zoukankan      html  css  js  c++  java
  • Redis--狂神说Redis基础汇总(完结)

    Redis--狂神说Redis基础汇总(完结)

    2021.6.12-2021.6.14:端午学学玩玩弄完了Redis基础的汇总,越学越觉得自己知识量的匮乏。

    参考链接:狂神说Java--Redis汇总:https://www.bilibili.com/video/BV1S54y1R7SB?p=1



    Redis五种基本数据类型:

    • 1.String字符串

      OK
      127.0.0.1:6380> set k1 1
      OK
      127.0.0.1:6380> INCR k1
      2
      127.0.0.1:6380> DECR k1
      1
      127.0.0.1:6380> keys *
      k1
      127.0.0.1:6380> set k2 huyuqiao
      OK
      127.0.0.1:6380> GETRANGE K2 0 3
      
      127.0.0.1:6380> GETRANGE k2 0 3
      huyu
      127.0.0.1:6380> GETRANGE k2 0 -1
      huyuqiao
      127.0.0.1:6380> SETRANGE k2 1 XX
      8
      127.0.0.1:6380> get k2
      hXXuqiao
      127.0.0.1:6380> SETEX k3 30 "hello, world"				#set exist:存在或不存在都替换
      OK
      127.0.0.1:6380> ttl k3
      28
      127.0.0.1:6380> ttl k2
      -1
      127.0.0.1:6380> keys *
      k1
      k2
      127.0.0.1:6380> ttl k3
      -2	
      127.0.0.1:6380> setnx mykey "redis"						#set if not exist:不存在就set,存在就set失败,还是原来值
      1
      127.0.0.1:6380> ttl mykey
      -1
      127.0.0.1:6380> setnx mykey "mongodb"
      0
      127.0.0.1:6380> get mykey
      redis
      127.0.0.1:6380> ttl k2									#永久返回-1
      -1
      127.0.0.1:6380> ttl k3									#过期返回-2
      -2
      127.0.0.1:6380> 
      127.0.0.1:6380> mset user:1:name huyuqiao user:1:age 22
      OK
      127.0.0.1:6380> mget user:1:name user:1:age
      huyuqiao
      22
      127.0.0.1:6380> 
      
      
      
    • List

      127.0.0.1:6380> FLUSHALL
      OK
      127.0.0.1:6380> clear
      127.0.0.1:6380> LPUSH list 1
      1
      127.0.0.1:6380> LPUSH list 2
      2
      127.0.0.1:6380> LPUSH list 3
      3
      127.0.0.1:6380> LRANGE list 0 -1
      3
      2
      1
      127.0.0.1:6380> RPUSH list a
      4
      127.0.0.1:6380> LPOP list
      3
      127.0.0.1:6380> LRANGE 0 -1
      ERR wrong number of arguments for 'lrange' command
      
      127.0.0.1:6380> LRANGE list 0 -1
      2
      1
      a
      127.0.0.1:6380> LINDEX list 0
      2
      127.0.0.1:6380> LLEN list
      3
      127.0.0.1:6380> FLUSHALL
      OK
      127.0.0.1:6380> clear
      127.0.0.1:6380> LPUSH list one 
      1
      127.0.0.1:6380> LPUSH list two
      2
      127.0.0.1:6380> LPUSH list two
      3
      127.0.0.1:6380> LREM list 1 one					#移除list中等于one的一个元素(最后加入的移除掉)
      1
      127.0.0.1:6380> LRANGE list 0 -1
      two
      two
      127.0.0.1:6380> LREM list 2 one
      0
      127.0.0.1:6380> LREM list 2 two
      2
      127.0.0.1:6380> LRANGE list 0 -1
      
      127.0.0.1:6380> FLUSHALL
      
      127.0.0.1:6380> LPUSH list one 
      1
      127.0.0.1:6380> LPUSH list two
      2
      127.0.0.1:6380> LPUSH list three
      3
      127.0.0.1:6380> LPUSH list four
      4
      127.0.0.1:6380> LTRIM list 1 2
      OK
      127.0.0.1:6380> LRANGE list 0 -1
      three
      two
      127.0.0.1:6380> 
      
      
      
    • Set

      127.0.0.1:6380> FLUSHALL
      OK
      127.0.0.1:6380> SADD myset "hello"
      1
      127.0.0.1:6380> sadd myset "world"
      1
      127.0.0.1:6380> sadd myset "huyuqiao"
      1
      127.0.0.1:6380> smembers myset 
      hello
      huyuqiao
      world
      127.0.0.1:6380> SISMEMBER myset huyuqiao
      1
      127.0.0.1:6380> sadd myset "HYQ"
      1
      127.0.0.1:6380> SMEMBERS myset
      hello
      huyuqiao
      HYQ
      world
      127.0.0.1:6380> SREM myset hello
      1
      127.0.0.1:6380> scard myset 
      3
      127.0.0.1:6380> SMEMBERS myset 
      huyuqiao
      HYQ
      world
      127.0.0.1:6380> SRANDMEMBER myset 
      world
      127.0.0.1:6380> SRANDMEMBER myset 
      huyuqiao
      127.0.0.1:6380> 
      
    • Hash

      huyuqiao
      127.0.0.1:6380> FLUSHALL
      OK
      127.0.0.1:6380> clear
      127.0.0.1:6380> hset myhash field1 huyuqiao
      1
      127.0.0.1:6380> hmset myhash field1 hello field2 world
      OK
      127.0.0.1:6380> hmget myhash field1 field2
      hello
      world
      127.0.0.1:6380> hgetall myhash
      field1
      hello
      field2
      world
      127.0.0.1:6380> hlen myhash
      2
      127.0.0.1:6380> HEXISTS myhash field1
      1
      
      127.0.0.1:6380> HKEYS myhash
      field1
      field2
      127.0.0.1:6380> HVALS myhash
      hello
      world
      127.0.0.1:6380> HSETNX myhash field4 hello			#hash设置
      1
      127.0.0.1:6380> HGETALL myhash
      field1
      hello
      field2
      world
      field4
      hello
      127.0.0.1:6380> 
      
    • Zset

      127.0.0.1:6380> zadd salary 100 huyuqiao
      1
      127.0.0.1:6380> zadd salary 200 HUYUQIAO
      1
      127.0.0.1:6380> zadd salary 300 HYQ
      1
      127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf
      huyuqiao
      HUYUQIAO
      HYQ
      127.0.0.1:6380> zrange salary 0 -1
      huyuqiao
      HUYUQIAO
      HYQ
      127.0.0.1:6380> zrem salary huyuqiao
      1
      127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf
      HUYUQIAO
      HYQ
      
      



    Redis三种特殊数据类型:

    1.GeoSpatical

    适用场景:获取附近人,好友距离,网约车定位功能。

    # 添加位置经纬度
    127.0.0.1:6379> GEOADD china:city 116.41667 39.91667 北京
    (integer) 1
    127.0.0.1:6379> GEOADD china:city 121.43333 31.23000 上海
    (integer) 1
    127.0.0.1:6379> GEOADD china:city 106.45000 29.56667 重庆
    (integer) 1
    127.0.0.1:6379> GEOADD china:city 114.06667 22.61667 深圳
    (integer) 1
    127.0.0.1:6379> GEOADD china:city 120.20000 30.26667 杭州
    (integer) 1
    127.0.0.1:6379> GEOADD china:city 108.95000 34.26667 西安
    (integer) 1
    
    # 查看不同位置经纬度
    127.0.0.1:6379> GEOPOS china:city 北京 西安
    
    #查看不同位置之间距离(默认是m)
    127.0.0.1:6379> GEODIST china:city 北京 上海
    "1066981.1340"
    127.0.0.1:6379> GEODIST china:city 北京 上海 km
    "1066.9811"
    127.0.0.1:6379> GEODIST china:city 北京 重庆 km
    "1465.8918"
    127.0.0.1:6379> 
    
    # 查看指定经纬度周边范围内的地方(可以以此扩散到周围指定范围内的指定人数的好友)
    127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
    重庆
    西安
    深圳
    杭州
    127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
    重庆
    西安
    127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist	#查看某经纬度周围500km的所有城市和距离/km
    重庆
    346.0548
    西安
    484.7511
    127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord #查看某经纬度周围500km的所有城市和经纬度
    重庆
    106.4500012993812561
    29.56666939001875249
    西安
    108.95000249147415161
    34.2666710302806834
    127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 1 #查看某经纬度周围500km的三个城市
    重庆
    127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 3 #查看某经纬度周围500km的三个城市
    重庆
    西安
    127.0.0.1:6379> 
    
    
    #查看地点周边的所有城市(类似于定位)
    127.0.0.1:6379> GEORADIUSBYMEMBER china:city 北京 1000 km
    北京
    西安
    127.0.0.1:6379> GEORADIUSBYMEMBER china:city 上海  1000 km
    杭州
    上海
    127.0.0.1:6379> 
    
    
    #将城市的二维经纬度转成一位hash字符串
    127.0.0.1:6379> GEOHASH china:city 北京 重庆
    wx4g14s53n0
    wm78nq6w2f0
    127.0.0.1:6379> 
    
    #GEO底层是zset,所有可以用zset命令来操作
    127.0.0.1:6379> zrange china:city 0 -1		#查看所有城市
    重庆
    西安
    深圳
    杭州
    上海
    北京
    127.0.0.1:6379> zrem china:city 北京			#删除北京这个城市
    
    
    2.Hyperloglog

    适用场景: 网站UV量。传统用set统计,但若存在大量用户id,则太消耗内容且麻烦,若只为计数且允许有错误率(0.81%),则可行,否则还是用set统计

    基数:集合中不重复元素个数。如{1, 3, 5, 5 ,7}则为{1,3,5,7},基数为4

    image-20210612145140896

    127.0.0.1:6379> clear
    127.0.0.1:6379> PFADD mykey a b c d e f g h i j				#设置mykey 集合
    (integer) 1
    127.0.0.1:6379> PFCOUNT mykey								#统计mykey 集合基数数量
    (integer) 10
    127.0.0.1:6379> PFADD mykey2 i j z x c v b n m
    (integer) 1
    127.0.0.1:6379> PFCOUNT mykey2
    (integer) 9
    127.0.0.1:6379> PFMERGE mykey3 mykey mykey2					#2个集合取并集
    OK
    127.0.0.1:6379> PFCOUNT mykey3
    (integer) 15
    127.0.0.1:6379>
    
    3.Bitmaps

    适用场景:判断、统计活跃、不活跃,登录、未登录这些非1即0的场景

    image-20210612144352965

    [root@VM-8-11-centos ~]# redis-cli -a root --raw
    127.0.0.1:6379> setbit sign 0 1			#设置sign用户某天是否打卡
    (integer) 0
    127.0.0.1:6379> SETBIT sign 1 0
    (integer) 0
    127.0.0.1:6379> SETBIT sign 2 0
    (integer) 0
    127.0.0.1:6379> SETBIT sign 3 1
    (integer) 0
    127.0.0.1:6379> SETBIT sign 4 1
    (integer) 0
    127.0.0.1:6379> SETBIT sign 5 0
    (integer) 0
    127.0.0.1:6379> SETBIT sign 6 0
    (integer) 0
    127.0.0.1:6379> GETBIT sign 3			#得到sign用户某天打卡情况
    (integer) 1
    127.0.0.1:6379> GETBIT sign 6
    



    Redis事务:

    Reidsi事务:没有隔离级别概念,即事务不保证原子性(事务内部有多条命令,不一定,可以实现部分成功、部分失败),但是单条命令保证原子性

    1.Redis事务流程:
    • 开启事务(multi)
    • 命令入队
    • 执行事务(exec)
    #开启-结束事务
    127.0.0.1:6379> multi 
    OK
    127.0.0.1:6379> set k1 v1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> get k2
    QUEUED
    127.0.0.1:6379> set k3 v3
    QUEUED
    127.0.0.1:6379> exec
    OK
    OK
    v2
    OK
    127.0.0.1:6379>
    
    #放弃事务:之前队列代码全部rollback
    127.0.0.1:6379> set k1 v1
    QUEUED
    127.0.0.1:6379> set k2 asdfasdf
    QUEUED
    127.0.0.1:6379> DISCARD
    OK
    127.0.0.1:6379> EXEC
    ERR EXEC without MULTI
    
    127.0.0.1:6379> get k2
    v2
    
    
    2.Redis事务非原子性

    之所以事务非原子性,是因为有两种异常:

    • 编译性异常(检查性异常):代码错了,根本就没某个语句、函数、配置文件等

      127.0.0.1:6379> MULTI
      OK
      127.0.0.1:6379> set k1 v1
      QUEUED
      127.0.0.1:6379> set k2 v2
      QUEUED
      127.0.0.1:6379> gasdfa k3				#语句错误,相当于没执行,在编译时候就出错了
      ERR unknown command 'gasdfa'
      
      127.0.0.1:6379> set k4 v4
      QUEUED
      127.0.0.1:6379> EXEC
      EXECABORT Transaction discarded because of previous errors.
      
      127.0.0.1:6379> get k4
      
      127.0.0.1:6379> get k1
      
      
    • 运行时异常(非检查性异常):没有某个变量、某个对象没有new等(不会全部rollback)

      127.0.0.1:6379> set k1 "v1"
      OK
      127.0.0.1:6379> multi
      OK
      127.0.0.1:6379> incr k1
      QUEUED
      127.0.0.1:6379> set k2 v2
      QUEUED
      127.0.0.1:6379> get k2
      QUEUED
      127.0.0.1:6379> EXEC
      ERR value is not an integer or out of range
      
      OK
      v2									#上面出错了,但是依然不影响下面语句执行,所以证明了redis事务是不保证原子性的
      127.0.0.1:6379> 
      



    Redis乐观锁:

    乐观锁:不会上锁,更新数据时才会比较version是否被人修改过(redis事务中如果被修改过,则事务不会执行成功)

    悲观锁:无论做什么,都会上锁,效率低下但是安全

    ​ Redis事务中用<font color=red size=3>Watch</font>实现乐观锁,中途如果被修改,导致version变更,则事务全部都不会成功

    127.0.0.1:6379> set money 100
    OK
    127.0.0.1:6379> set out 0
    OK
    127.0.0.1:6379> watch money				#Redis乐观锁:watch监视money
    OK
    127.0.0.1:6379> MULTI 					#在另一个窗口将money改成了101
    OK
    127.0.0.1:6379> DECRBY money 10
    QUEUED
    127.0.0.1:6379> incrby out 10
    QUEUED
    127.0.0.1:6379> EXEC
    127.0.0.1:6379> get money
    101
    127.0.0.1:6379> 
    
    



    SpringBooot--配置Redis源代码

    Jedis:直连,多线程下不安全,类似于BIO模式(Springboot2.X后被淘汰)

    Lettuce:采用Netty,多线程下共享,类似于NIO模式

    autoconfig->srping.factories中找到redis相关配置

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(RedisOperations.class)
    @EnableConfigurationProperties(RedisProperties.class)
    @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
    public class RedisAutoConfiguration {
    
    	@Bean
    	@ConditionalOnMissingBean(name = "redisTemplate")
    	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
    			throws UnknownHostException {
    		RedisTemplate<Object, Object> template = new RedisTemplate<>();
    		template.setConnectionFactory(redisConnectionFactory);
    		return template;
    	}
    
    	@Bean
    	@ConditionalOnMissingBean
    	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
    			throws UnknownHostException {
    		StringRedisTemplate template = new StringRedisTemplate();
    		template.setConnectionFactory(redisConnectionFactory);
    		return template;
    	}
    
    }
    



    SpringBooot--自定义RedisTemplate、RedisUtil

    1.RedisTemplate序列化配置
    package com.empirefree.springboot.config;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.JsonTypeInfo;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    /**
     * @program: springboot
     * @description: RedisTemplate配置
     * @author: huyuqiao
     * @create: 2021/06/13 16:05
     */
    
    @Configuration
    public class RedisConfig {
        @Bean
        @SuppressWarnings("all")
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
            RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
            template.setConnectionFactory(factory);
            // Json序列化配置
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,
                    JsonTypeInfo.As.WRAPPER_ARRAY);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            // String 的序列化
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            // key采用String的序列化方式
            template.setKeySerializer(stringRedisSerializer);
            // hash的key也采用String的序列化方式
            template.setHashKeySerializer(stringRedisSerializer);
            // value序列化方式采用jackson
            template.setValueSerializer(jackson2JsonRedisSerializer);
            // hash的value序列化方式采用jackson
            template.setHashValueSerializer(jackson2JsonRedisSerializer);
            template.afterPropertiesSet();
            return template;
        }
    }
    
    
    2.RedisUtil配置(CRUD操作string,map,list,set)
    package com.empirefree.springboot.utils;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    import org.springframework.util.CollectionUtils;
    
    import java.util.Collection;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @program: springboot
     * @description: Redis工具类
     * @author: huyuqiao
     * @create: 2021/06/13 16:14
     */
    
    @Component
    public final class RedisUtil {
    
        @Autowired
        private RedisTemplate<String, Object> redisTemplate;
    
        // =============================common============================
        /**
         * 指定缓存失效时间
         * @param key  键
         * @param time 时间(秒)
         */
        public boolean expire(String key, long time) {
            try {
                if (time > 0) {
                    redisTemplate.expire(key, time, TimeUnit.SECONDS);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 根据key 获取过期时间
         * @param key 键 不能为null
         * @return 时间(秒) 返回0代表为永久有效
         */
        public long getExpire(String key) {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        }
    
    
        /**
         * 判断key是否存在
         * @param key 键
         * @return true 存在 false不存在
         */
        public boolean hasKey(String key) {
            try {
                return redisTemplate.hasKey(key);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * 删除缓存
         * @param key 可以传一个值 或多个
         */
        @SuppressWarnings("unchecked")
        public void del(String... key) {
            if (key != null && key.length > 0) {
                if (key.length == 1) {
                    redisTemplate.delete(key[0]);
                } else {
                    redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
                }
            }
        }
    
    
        // ============================String=============================
    
        /**
         * 普通缓存获取
         * @param key 键
         * @return 值
         */
        public Object get(String key) {
            return key == null ? null : redisTemplate.opsForValue().get(key);
        }
    
        /**
         * 普通缓存放入
         * @param key   键
         * @param value 值
         * @return true成功 false失败
         */
    
        public boolean set(String key, Object value) {
            try {
                redisTemplate.opsForValue().set(key, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * 普通缓存放入并设置时间
         * @param key   键
         * @param value 值
         * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
         * @return true成功 false 失败
         */
    
        public boolean set(String key, Object value, long time) {
            try {
                if (time > 0) {
                    redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
                } else {
                    set(key, value);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * 递增
         * @param key   键
         * @param delta 要增加几(大于0)
         */
        public long incr(String key, long delta) {
            if (delta < 0) {
                throw new RuntimeException("递增因子必须大于0");
            }
            return redisTemplate.opsForValue().increment(key, delta);
        }
    
    
        /**
         * 递减
         * @param key   键
         * @param delta 要减少几(小于0)
         */
        public long decr(String key, long delta) {
            if (delta < 0) {
                throw new RuntimeException("递减因子必须大于0");
            }
            return redisTemplate.opsForValue().increment(key, -delta);
        }
    
    
        // ================================Map=================================
    
        /**
         * HashGet
         * @param key  键 不能为null
         * @param item 项 不能为null
         */
        public Object hget(String key, String item) {
            return redisTemplate.opsForHash().get(key, item);
        }
    
        /**
         * 获取hashKey对应的所有键值
         * @param key 键
         * @return 对应的多个键值
         */
        public Map<Object, Object> hmget(String key) {
            return redisTemplate.opsForHash().entries(key);
        }
    
        /**
         * HashSet
         * @param key 键
         * @param map 对应多个键值
         */
        public boolean hmset(String key, Map<String, Object> map) {
            try {
                redisTemplate.opsForHash().putAll(key, map);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * HashSet 并设置时间
         * @param key  键
         * @param map  对应多个键值
         * @param time 时间(秒)
         * @return true成功 false失败
         */
        public boolean hmset(String key, Map<String, Object> map, long time) {
            try {
                redisTemplate.opsForHash().putAll(key, map);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * 向一张hash表中放入数据,如果不存在将创建
         *
         * @param key   键
         * @param item  项
         * @param value 值
         * @return true 成功 false失败
         */
        public boolean hset(String key, String item, Object value) {
            try {
                redisTemplate.opsForHash().put(key, item, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 向一张hash表中放入数据,如果不存在将创建
         *
         * @param key   键
         * @param item  项
         * @param value 值
         * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
         * @return true 成功 false失败
         */
        public boolean hset(String key, String item, Object value, long time) {
            try {
                redisTemplate.opsForHash().put(key, item, value);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * 删除hash表中的值
         *
         * @param key  键 不能为null
         * @param item 项 可以使多个 不能为null
         */
        public void hdel(String key, Object... item) {
            redisTemplate.opsForHash().delete(key, item);
        }
    
    
        /**
         * 判断hash表中是否有该项的值
         *
         * @param key  键 不能为null
         * @param item 项 不能为null
         * @return true 存在 false不存在
         */
        public boolean hHasKey(String key, String item) {
            return redisTemplate.opsForHash().hasKey(key, item);
        }
    
    
        /**
         * hash递增 如果不存在,就会创建一个 并把新增后的值返回
         *
         * @param key  键
         * @param item 项
         * @param by   要增加几(大于0)
         */
        public double hincr(String key, String item, double by) {
            return redisTemplate.opsForHash().increment(key, item, by);
        }
    
    
        /**
         * hash递减
         *
         * @param key  键
         * @param item 项
         * @param by   要减少记(小于0)
         */
        public double hdecr(String key, String item, double by) {
            return redisTemplate.opsForHash().increment(key, item, -by);
        }
    
    
        // ============================set=============================
    
        /**
         * 根据key获取Set中的所有值
         * @param key 键
         */
        public Set<Object> sGet(String key) {
            try {
                return redisTemplate.opsForSet().members(key);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
    
        /**
         * 根据value从一个set中查询,是否存在
         *
         * @param key   键
         * @param value 值
         * @return true 存在 false不存在
         */
        public boolean sHasKey(String key, Object value) {
            try {
                return redisTemplate.opsForSet().isMember(key, value);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * 将数据放入set缓存
         *
         * @param key    键
         * @param values 值 可以是多个
         * @return 成功个数
         */
        public long sSet(String key, Object... values) {
            try {
                return redisTemplate.opsForSet().add(key, values);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
    
        /**
         * 将set数据放入缓存
         *
         * @param key    键
         * @param time   时间(秒)
         * @param values 值 可以是多个
         * @return 成功个数
         */
        public long sSetAndTime(String key, long time, Object... values) {
            try {
                Long count = redisTemplate.opsForSet().add(key, values);
                if (time > 0)
                    expire(key, time);
                return count;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
    
        /**
         * 获取set缓存的长度
         *
         * @param key 键
         */
        public long sGetSetSize(String key) {
            try {
                return redisTemplate.opsForSet().size(key);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
    
        /**
         * 移除值为value的
         *
         * @param key    键
         * @param values 值 可以是多个
         * @return 移除的个数
         */
    
        public long setRemove(String key, Object... values) {
            try {
                Long count = redisTemplate.opsForSet().remove(key, values);
                return count;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
        // ===============================list=================================
    
        /**
         * 获取list缓存的内容
         *
         * @param key   键
         * @param start 开始
         * @param end   结束 0 到 -1代表所有值
         */
        public List<Object> lGet(String key, long start, long end) {
            try {
                return redisTemplate.opsForList().range(key, start, end);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
    
        /**
         * 获取list缓存的长度
         *
         * @param key 键
         */
        public long lGetListSize(String key) {
            try {
                return redisTemplate.opsForList().size(key);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    
    
        /**
         * 通过索引 获取list中的值
         *
         * @param key   键
         * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
         */
        public Object lGetIndex(String key, long index) {
            try {
                return redisTemplate.opsForList().index(key, index);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
    
        /**
         * 将list放入缓存
         *
         * @param key   键
         * @param value 值
         */
        public boolean lSet(String key, Object value) {
            try {
                redisTemplate.opsForList().rightPush(key, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * 将list放入缓存
         * @param key   键
         * @param value 值
         * @param time  时间(秒)
         */
        public boolean lSet(String key, Object value, long time) {
            try {
                redisTemplate.opsForList().rightPush(key, value);
                if (time > 0)
                    expire(key, time);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
    
        }
    
    
        /**
         * 将list放入缓存
         *
         * @param key   键
         * @param value 值
         * @return
         */
        public boolean lSet(String key, List<Object> value) {
            try {
                redisTemplate.opsForList().rightPushAll(key, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
    
        }
    
    
        /**
         * 将list放入缓存
         *
         * @param key   键
         * @param value 值
         * @param time  时间(秒)
         * @return
         */
        public boolean lSet(String key, List<Object> value, long time) {
            try {
                redisTemplate.opsForList().rightPushAll(key, value);
                if (time > 0)
                    expire(key, time);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * 根据索引修改list中的某条数据
         *
         * @param key   键
         * @param index 索引
         * @param value 值
         * @return
         */
    
        public boolean lUpdateIndex(String key, long index, Object value) {
            try {
                redisTemplate.opsForList().set(key, index, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
    
        /**
         * 移除N个值为value
         *
         * @param key   键
         * @param count 移除多少个
         * @param value 值
         * @return 移除的个数
         */
    
        public long lRemove(String key, long count, Object value) {
            try {
                Long remove = redisTemplate.opsForList().remove(key, count, value);
                return remove;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
    
        }
    
    }
    



    Redis--持久化

    持久化:在指定时间间隔内将内存数据存入磁盘中,断电也能恢复数据,使用快照文件读到内存中。

    1.Redis--RDB(默认推荐)
    • 保存过程:父进程fork一个子进程,将数据持久化到临时文件中,持久化结束,再替换上次的RDB正式文件。

    • 触发条件:

      • save满足:命令save 900 1 即是在15分钟内修改了1次 即会触发RDB。
      • 执行FlushAll命令
      • 退出Redis,会产生RDB文件
    • 适用场景:适合大规模数据恢复且数据完整性不敏感的情况。

    2.Redis--AOF(重启后是默认先载入AOF,因为数据更完整)
    • 保存过程:父进程fork一个子进程,以日志形式将所有指令记录下来(读操作不记录),然后将数据只追加不改写到AOF文件,然后替换上次的AOF文件

    • 触发条件:appendfsync always/everysec/no 命令

    • 适用场景:对恢复数据完整性要求严格

    • 重写场景:不断追加文件到一个阈值,则会重写aof文件



    Redis--发布订阅

    发布订阅:可以做消息推送、聊天室等等

    #发布者:往Redis某渠道中发消息,所有订阅者都可以接收到
    127.0.0.1:6379> PUBLISH huyuqiao "hello,world"
    1
    
    
    #订阅者:订阅Redis中某个渠道channel
    127.0.0.1:6379> SUBSCRIBE huyuqiao
    subscribe
    huyuqiao
    1
    message
    huyuqiao
    hello,world
    
    



    Redis--主从复制(主写从读)

    1.Linux配置文件
    • daemonize的no改成yes
    • port、pidfile、logfile、dbfilename改成对于新redis名
    • 如果redis有密码,需要在slave中配置或者在master中去掉密码
    #master断了,slaver依然是原来master的slaver。但是slaver断了,master就没有slaver了
    >>slaveof no one #关闭slave状态,变成master
    >>shutdown       #停止redis,其slave状态停止,下次启动变成master,且原来master下的该redis就没有了
    
    
    redis-server redis80.conf #启动80redis窗口。
    kill -s 9 pid  #关闭某个进程
    
    2. 复制原理
    • 全量复制:slave启动后,发送sync同步命令给master,然后同步master中所有数据
    • 增量复制:master写数据后,slave实时得到。



    Redis--哨兵模式

    流程:如下,在启动哨兵后,关闭79主服务,就从80,81中投票选举了80为主服务,然后把79,80都当成了slave,所以下次79启动的时候会默认是slave。另外,由于数据是增量复制,所以数据在80中保存完好,且79启动后就全量复制到79中了

    #哨兵模式下的日志
    
    8044:X 14 Jun 16:35:06.885 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
    8044:X 14 Jun 16:35:06.885 # Sentinel ID is c0ce22fc8365ff48663b7db710ce8c359529c3d9
    8044:X 14 Jun 16:35:06.885 # +monitor master mymaster 127.0.0.1 6379 quorum 1
    8044:X 14 Jun 16:35:51.556 # +sdown master mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:51.556 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1
    8044:X 14 Jun 16:35:51.556 # +new-epoch 1
    8044:X 14 Jun 16:35:51.556 # +try-failover master mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:51.572 # +vote-for-leader c0ce22fc8365ff48663b7db710ce8c359529c3d9 1
    8044:X 14 Jun 16:35:51.572 # +elected-leader master mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:51.572 # +failover-state-select-slave master mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:51.624 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:51.624 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:51.684 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:51.826 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:51.826 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:51.914 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:52.831 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:52.831 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:52.912 # +failover-end master mymaster 127.0.0.1 6379
    8044:X 14 Jun 16:35:52.912 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
    8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
    8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
    
    



    Redis--缓存穿透、击穿、雪崩

    • 缓存穿透:缓存和数据库中都没有数据
      • 解决方案:1.布隆过滤器 2.存储空对象:数据库没找到后,redis中临时存一个空对象
    • 缓存击穿:某个key值过期,来了大量访问
      • 解决方案:1.永不过期 2.分布式锁:一个线程获取,其他线程等待
    • 缓存雪崩:海量key值过期,来了大量访问
      • 解决方案:1.redis高可用:多设置几台redis 2.限流降级:缓存失效后,通过加锁或者队列来控制读数据库写缓存的数量 3.数据预热:大量数据加载到缓存中,根据不同访问量来设置不同过期时间

    我曾七次鄙视自己的灵魂:
    第一次,当它本可进取时,却故作谦卑;
    第二次,当它在空虚时,用爱欲来填充;
    第三次,在困难和容易之间,它选择了容易;
    第四次,它犯了错,却借由别人也会犯错来宽慰自己;
    第五次,它自由软弱,却把它认为是生命的坚韧;
    第六次,当它鄙夷一张丑恶的嘴脸时,却不知那正是自己面具中的一副;
    第七次,它侧身于生活的污泥中,虽不甘心,却又畏首畏尾。
  • 相关阅读:
    锋利的jQuery复制粘贴(一)
    使用photoshop以及markman进行快速重构页面的几个步骤
    线程间同步之 semaphore(信号量)
    关于C#中Thread.Join()的一点理解
    无废话WCF入门教程一[什么是WCF]
    Oracle function注释
    throw new DataException("检查服务器是否存在失败:" + ex);
    C# 将数据集以excel的形式输出
    .net 安装remoting服务
    任务计划 每日删除设定目录内的文件(包括文件夹)
  • 原文地址:https://www.cnblogs.com/meditation5201314/p/14882992.html
Copyright © 2011-2022 走看看