Redis常见类型操作及使用场景

一、配置redis,防止乱码

package com.duanmh.config.redis;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;

import java.nio.charset.Charset;

/**
 * Redis使用FastJson序列化
 * @Author dmh 2022/4/11
 */
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
    @SuppressWarnings("unused")
    private ObjectMapper objectMapper = new ObjectMapper();

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class<T> clazz;



    public FastJson2JsonRedisSerializer(Class<T> clazz)
    {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException
    {
        if (t == null)
        {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException
    {
        if (bytes == null || bytes.length <= 0)
        {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);

        return JSON.parseObject(str, clazz);
    }

    public void setObjectMapper(ObjectMapper objectMapper)
    {
        Assert.notNull(objectMapper, "'objectMapper' must not be null");
        this.objectMapper = objectMapper;
    }

    protected JavaType getJavaType(Class<?> clazz)
    {
        return TypeFactory.defaultInstance().constructType(clazz);
    }
}

package com.duanmh.config.redis;

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.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/** redis配置 @Author dmh 2022/4/11 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    @Value(value = "${spring.redis.host}")
    private String host;
    @Value(value = "${spring.redis.port}")
    private int port;
  /*  @Value(value = "${spring.redis.database}")
    private int database;
    @Value(value = "${spring.redis.password}")
    private String password;*/

    /**
     * 单Redis节点模式配置方法
     * 其他配置參數,看:
     * <a href = "https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#26-%E5%8D%95redis%E8%8A%82%E7%82%B9%E6%A8%A1%E5%BC%8F">
     * 单Redis节点模式配置方法
     * </a>
     *
     * @return {@link RedissonClient}
     */
    @Bean(destroyMethod = "shutdown")
    RedissonClient redisson() {
        Config config = new Config();
        //Redis多节点
        // config.useClusterServers()
        //     .addNodeAddress("redis://127.0.0.1:6379", "redis://127.0.0.1:7001");
        //Redis单节点
        SingleServerConfig singleServerConfig = config.useSingleServer();
        //可以用"rediss://"来启用SSL连接
        String address = "redis://" + host + ":" + port;
        singleServerConfig.setAddress(address);
        //设置 数据库编号
        /*singleServerConfig.setDatabase(database);
        singleServerConfig.setPassword(password);*/
        //连接池大小:默认值:64
        // singleServerConfig.setConnectionPoolSize()
        return Redisson.create(config);
    }


    /**
     * 设置redis编码信息,防止乱码
     * @param connectionFactory
     * @return
     */

    @Bean
    @SuppressWarnings(value = {"unchecked", "rawtypes"})
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(mapper);

        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);

        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public DefaultRedisScript<Long> limitScript() {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(limitScriptText());
        redisScript.setResultType(Long.class);
        return redisScript;
    }

    /** 限流脚本 */
    private String limitScriptText() {
        return "local key = KEYS[1]\n"
                + "local count = tonumber(ARGV[1])\n"
                + "local time = tonumber(ARGV[2])\n"
                + "local current = redis.call('get', key);\n"
                + "if current and tonumber(current) > count then\n"
                + "    return tonumber(current);\n"
                + "end\n"
                + "current = redis.call('incr', key)\n"
                + "if tonumber(current) == 1 then\n"
                + "    redis.call('expire', key, time)\n"
                + "end\n"
                + "return tonumber(current);";
    }
}

二、使用redisTemplate操作各种类型数据

import com.duanmh.PublisherApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

@RunWith(SpringRunner.class)
@SpringBootTest(classes= PublisherApplication.class)
public class RedisTemplateTest {

    @Resource
    RedisTemplate redisTemplate;

    /**
     * 使用RedisTemplate 操作 String
     *
     *  应用场景 :
     *      存储字符串,常用于缓存(常用不常改变的数据),计数器(如库存)和分布式锁
     */
    @Test
    public void StringTemplate() throws InterruptedException {

        //将字符串直接存入Redis
        redisTemplate.opsForValue().set("userName","张三");

        //将字符串读取出来
        String massage = (String) redisTemplate.opsForValue().get("userName");
        System.out.println("读取直接存入redisTemplate数据:{}"+massage);

        //同时添加多个Key和Val
        Map<String, Object> hashMap = new HashMap<>();
        hashMap.put("a", "123");
        hashMap.put("b","456");
        redisTemplate.opsForValue().multiSet(hashMap);

        //读取多个key
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        List<String> multiGet = redisTemplate.opsForValue().multiGet(list);
        for (String key : multiGet) {
            System.out.println("读取批量存入redisTemplate数据:{}"+key);
        }


        //将数据存入Redis,有效期3秒
        redisTemplate.opsForValue().set("name", "李四",3,TimeUnit.SECONDS);

        String name = (String)redisTemplate.opsForValue().get("name");
        System.out.println("读取在有效期的数据:{}"+name);
        Thread.sleep(4000);
        String name2 = (String)redisTemplate.opsForValue().get("name");
        System.out.println("读取未在有效期的数据:{}"+name2);

        //读取之后重新赋值
        String andSet = (String)redisTemplate.opsForValue().getAndSet("userName", "大春");
        System.out.println("修改前的userName:{}"+andSet);
        String userName = (String)redisTemplate.opsForValue().get("userName");
        System.out.println("修改后的userName:{}"+userName);

        //增减值
        Long age1 = redisTemplate.opsForValue().increment("age", 2);
        System.out.println("加上两年的年龄:{}"+ age1);
        Long age2 = redisTemplate.opsForValue().decrement("age", 1);
        System.out.println("减去一年的年龄:{}"+ age2);

        Long age = redisTemplate.opsForValue().increment("age", 10);
        System.out.println("加上十年的年龄:{}"+age);



    }

    /**
     * 使用RedisTemplate操作 List
     *
     *  使用场景:
     *      list的特点是可以从头或尾存入或读取数据,所以可以用于消息队列、最新消息、过段时间计算一次的排行等场景
     */
    @Test
    public void ListTempalate(){
        //直接从头加入条数据
        Long list = redisTemplate.opsForList().leftPush("list", 3);
        //批量从头加入一条数据
        List<Integer> ints = new ArrayList<>();
        ints.add(2);
        ints.add(1);
        Long list2 = redisTemplate.opsForList().leftPushAll("list", ints);

        List all1 = redisTemplate.opsForList().range("list", 0, -1);
        System.out.println("从头加入数据为:{}"+ all1);
        //直接从尾加入一条数据
        Long list3 = redisTemplate.opsForList().rightPush("list", "a");
        //批量从尾加入一条数据
        List<String> ints2 = new ArrayList<>();
        ints2.add("b");
        ints2.add("c");
        redisTemplate.opsForList().rightPushAll("list", ints2);

        List all2 = redisTemplate.opsForList().range("list", 0, -1);
        System.out.println("从尾部加入数据为:{}"+ all2);
        //从左侧读取一个并删除
        List list1 = redisTemplate.opsForList().leftPop("list", 1);

        List all3 = redisTemplate.opsForList().range("list", 0, -1);
        System.out.println("从左侧读取一个并删除数据为:{}"+ all3);
        //从右侧读取一个并删除
        List list5 = redisTemplate.opsForList().rightPop("list", 1);

        List all4 = redisTemplate.opsForList().range("list", 0, -1);
        System.out.println("从右侧读取一个并删除数据为:{}"+ all4);

        //读取全部list
        List all5 = redisTemplate.opsForList().range("list", 0, -1);

        System.out.println("最后的数据为 :{}"+all5);
    }


    /**
     * 使用RedisTemplate操作 set
     *
     *  使用场景:
     *      set的特点是无序不可重复, 可以用于去重、抽奖、共同好友等场景
     *
     */
    @Test
    public void setTemplate(){

        //添加三个参数
        String []arr={"a","1","2","3"};
        Long set = redisTemplate.opsForSet().add("set", arr);
        //取出数据
        Set set1 = redisTemplate.opsForSet().members("set");
        System.out.println("直接存入set的数据为:{}"+set1);

        //删除数据
        redisTemplate.opsForSet().remove("set","a");
        Set set2 = redisTemplate.opsForSet().members("set");
        System.out.println("删除a之后的数据:{}"+set2);

        //获取随机数据,但是不删除元素(可能会重复)
        List set3 = redisTemplate.opsForSet().randomMembers("set", 2);
        System.out.println("随机获取两个数据:{}"+set3);

        //在添加一条数据
        String []arr1={"3","4","5"};
        redisTemplate.opsForSet().add("set1",arr1);
        Set set11 = redisTemplate.opsForSet().members("set1");
        System.out.println("set1的数据为:{}"+set11);

        //用法5:计算出set和set1的差运算值
        Set difference = redisTemplate.opsForSet().difference("set", "set1");
        System.out.println("set和set1的差集:{}"+difference);
        //用法6:将test和test1的差值存入test2中
        redisTemplate.opsForSet().differenceAndStore("set","set1","difference");
        Set set21 = redisTemplate.opsForSet().members("difference");
        System.out.println("set2中存入的set和set1的差集:{}"+set21);

        //用法7:计算test和test1的交集
        Set intersect = redisTemplate.opsForSet().intersect("set", "set1");
        System.out.println("set和set1的交集:{}"+intersect);

        //用法8:将test和test1的交集存入test2中
        redisTemplate.opsForSet().intersectAndStore("set","set1","intersect");

        Set intersect1 = redisTemplate.opsForSet().members("intersect");
        System.out.println("set和set1存起来的交集:{}"+intersect1);
        //用法9:计算test和test1的并集
        Set union = redisTemplate.opsForSet().union("set", "set1");
        System.out.println("set和set1的并集:{}"+union);
        //用法10:将test和test1的并集存入test2中
        redisTemplate.opsForSet().unionAndStore("set","set1","union");
        Set union1 = redisTemplate.opsForSet().members("union");
        System.out.println("存入union的set和set1的并集:{}"+union1);
        //用法11:获取test中元素的个数
        Long size = redisTemplate.opsForSet().size("union");
        System.out.println("集合 union 的个数为:{}"+size);
        //用法12:从test中随机弹出一个数,且删除元数据
        redisTemplate.opsForSet().pop("set",1);
        Set set5 = redisTemplate.opsForSet().members("set");
        System.out.println("移除后的set数据:{}"+set5);

        //用法13:将test中的a值移入到test1中
        redisTemplate.opsForSet().move("set","1","set1");
        Set set4 = redisTemplate.opsForSet().members("set");
        Set set12 = redisTemplate.opsForSet().members("set1");

        System.out.println("移动数据后的 set:{}"+set4);
        System.out.println("移动数据后的 set1:{}"+set12);
    }

    /**
     * 使用RedisTemplate操作 Zset
     *
     *  使用场景:
     *      Zset的特点是有序不可重复,可以用于 排行榜、点击率、时间轴等操作
     */

    @Test
    public void ZsetTempalate(){
        //在zset集合中添加 java,并将分值设置为 1
        redisTemplate.opsForZSet().add("zset", "java",1);
        redisTemplate.opsForZSet().add("zset", "C",2);
        redisTemplate.opsForZSet().add("zset", "C++",3);
        redisTemplate.opsForZSet().add("zset", "C#",40);
        redisTemplate.opsForZSet().add("zset", "python", 50);
        redisTemplate.opsForZSet().add("zset", "PHP", 60);

        //取出Java 的分值
        Double score = redisTemplate.opsForZSet().score("zset", "java");
        System.out.println("java 的score 为:{}"+score);

        //用法3:获取zset中成员的数量
        long count=redisTemplate.opsForZSet().zCard("zset");
        System.out.println("zset的成员数量:{}"+count);


        //用法4:删除zset集合中的java
        String []arr={"java"};
        Long zset = redisTemplate.opsForZSet().remove("zset",arr);

        //根据score值取出zset的值,-1表示全部取出
        Set zset1 = redisTemplate.opsForZSet().range("zset", 0, -1);
        System.out.println("删除Java之后的集合:{}"+zset1);
        //按用法5:照范围删除,-1表示最后
        redisTemplate.opsForZSet().removeRange("zset",0,1);

        Set zset2 = redisTemplate.opsForZSet().range("zset", 0, -1);
        System.out.println("删除0-1之后的集合:{}"+zset2);
        //用法6:删除集合zset中score在20-30的所有数据
        redisTemplate.opsForZSet().removeRangeByScore("zset",40,40);

        Set zset3 = redisTemplate.opsForZSet().range("zset", 0, -1);
        System.out.println("按照分值删除后的数据为:{}"+zset3);
        //用法7:按照score从大到小返回数据
        Set<Object> sets=redisTemplate.opsForZSet().reverseRange("zset",0,-1);
        System.out.println("根据score值由大到小排序为:{}"+sets);
        //用法8:返回集合zset中20-30的数据,且按照从低到高排序(MAX值不能为-1)
        Set zset4 = redisTemplate.opsForZSet().rangeByScore("zset", 0, 60);
        System.out.println("根据score值由小到大排序:{}"+zset4);
        //用法9:为集合zset中java的score值增加5
        redisTemplate.opsForZSet().incrementScore("zset","python",5);

        Double score1 = redisTemplate.opsForZSet().score("zset", "python");
        System.out.println("增加分值后的python分值为:{}"+score1);


        //用法10:获取分数在20-30之间的成员个数
        Long zset5 = redisTemplate.opsForZSet().count("zset", 20, 55);
        System.out.println("获取分数为20-50之间的数据个数为:{}"+zset5);
        //用法11:按照从小到大规则,返回java在集合zset中的排名
        Long rank = redisTemplate.opsForZSet().rank("zset", "PHP");
        System.out.println("由大到小 PHP在排名中的位数为:{}"+rank);
        //用法12:按照从大到小规则,返回java在集合zset中的排名
        Long aLong = redisTemplate.opsForZSet().reverseRank("zset", "PHP");
        System.out.println("由小到大,PHP在排名中的位数为:{}"+aLong);

    }


    /**
     *使用redisTemplate操作 Hash
     *
     *  使用场景:
     *      Hash类型可以将多个键值对存入一个Redis的健当中,散列非常适用于将一些相关的数据存储在一起,所以可以用于将一个对象存储在Redis当中
     *
     */

    @Test
    public void HashTemplate() throws InterruptedException {
        //用法1:put->添加一条hash参数,key为id,value为1001
        redisTemplate.opsForHash().put("stu1","id","1001");
        Map<String,Object> allKeyAndValue=redisTemplate.opsForHash().entries("stu1");
        System.out.println("获取到的学生数据为:{}"+allKeyAndValue);
        Map<String,Object> map=new HashMap<String,Object>();
        map.put("name","晓春");
        map.put("sex","男");
        map.put("age",10);
        //用法2:putAll->:同时向stu1中添加多条数据
        redisTemplate.opsForHash().putAll("stu1",map);
        Map stu1 = redisTemplate.opsForHash().entries("stu1");
        System.out.println("添加多条数据后的学生数据为:{}"+stu1);
        //用法3:get->获取stu1中key为name的值
        String name = (String) redisTemplate.opsForHash().get("stu1", "name");
        System.out.println("获取stu1的学生姓名为:{}"+name);
        //用法4:increment->为stu1中sex的值增加2
        redisTemplate.opsForHash().increment("stu1","age", 1);
        Integer age = (Integer)redisTemplate.opsForHash().get("stu1", "age");
        System.out.println("修改后的学生年龄为:{}"+age);
        //用法5:delete->删除stu1中的name
        redisTemplate.opsForHash().delete("stu1","sex");
        Map stu11 = redisTemplate.opsForHash().entries("stu1");
        System.out.println("删除sex之后的数据:{}"+stu11);

        //用法7:multiGet->获取stu1中,id和name的值
        List<String> list=redisTemplate.opsForHash().multiGet("stu1",Arrays.asList("id","name"));
        System.out.println("获取到的id和name:{}"+list);
        //用法8:putIfAbsent->id存在就返回false且不执行任何动作,id不存在就添加id和value
        boolean flag=redisTemplate.opsForHash().putIfAbsent("stu1","sex","女");
        Map stu12 = redisTemplate.opsForHash().entries("stu1");
        System.out.println("不存在添加之后的数据为:{}"+stu12);


        //用法10:randomEntry->随机获取stu1中的一个键值对,如name=晓春或sex=1
        String key1=redisTemplate.opsForHash().randomEntry("stu1").toString();
        System.out.println("随机弹出的Key是:{}"+key1);

        Map stu13 = redisTemplate.opsForHash().entries("stu1");
        System.out.println("随机弹出之后的数据:{}"+stu13);
        //用法11:size->查询stu1中键值对的数量
        Long stu14 = redisTemplate.opsForHash().size("stu1");
        System.out.println("获取stu1中的键值对数量:{}"+stu14);
        //用法9:expire->设置stu1的有效为30秒
        redisTemplate.expire("stu1",3,TimeUnit.SECONDS);

        Long stu16 = redisTemplate.getExpire("stu1", TimeUnit.SECONDS);
        System.out.println("时间有效期为:{}"+stu16);
        Thread.sleep(4000);

        Map stu15 = redisTemplate.opsForHash().entries("stu1");
        System.out.println("失效后的数据为:{}"+stu15);
    }


    /**
     * redisTemplate通用操作
     */
    @Test
    public void redisTemplateTest(){
        //用法1:获取指定的keyz值,如*,??list,keys*
        redisTemplate.keys("");
        //用法2:删除指定的key
        redisTemplate.delete("");
        //用法3:给key重命名
        redisTemplate.rename("","");
        //用法4:设置key的有效期
        redisTemplate.expire("username",3000,TimeUnit.SECONDS);
        //用法5:获取key的有效期
        redisTemplate.getExpire("username",TimeUnit.SECONDS);
        //用法6:清楚key的有效期
        redisTemplate.persist("username");
        //用法7:判断key的类型
        redisTemplate.type("");

    }


}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值