草庐IT

Redis入门到精通(十一),Redission操作Redis

丁胜利v 2023-04-21 原文

一. 初识Redission

        前面我们讲了Redis的三大常见客户端中的Jedis和Lettuce,今天就跟大家聊聊这个Redisson。那Redisson是什么呢?Redisson是一个在Redis的基础上实现的Java内存数据网格(In-Memory Data Grid),它充分利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类,让使用Redis更加简单、便捷,从而让使用者能够将更多精力集中到业务逻辑处理上。

        也就是说Redisson不仅仅是一个Redis客户端,它是一个以内存 Redis 服务器作为后端的处理 Java 对象,它还实现了很多具有分布式特性的常用工具类。比如分布式锁、布隆过滤器等。下图是官网提供的Redisson可以做的事情。

 

 

 1.依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson-spring-boot-starter</artifactId>
        <version>3.13.0</version>
    </dependency>
</dependencies>

2.yml文件

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456

二.使用方法

1.对象存储

        Redisson将Redis中的字符串数据结构封装成了RBucket,通过RedissonClient的getBucket(key)方法获取一个RBucket对象实例,通过这个实例可以设置value或设置value和有效期。并且可以操作所有类型的对象。示例如下代码。

    /**
     * 通用对象桶,可以用来存放任类型的对象
     */
    @Test
    public void RedissonBucket(){
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);

        //====================操作对象桶来存储对象(同步)====================
        RBucket<Object> bucket = redissonClient.getBucket("name");
        //设置值为victory,过期时间为3小时
        bucket.set("victory",30, TimeUnit.HOURS);
        Object value = bucket.get();
        System.out.println(value);
        //通过key取value值
        Object name = redissonClient.getBucket("name").get();
        System.out.println(name);

        //====================关闭客户端====================
        redissonClient.shutdown();
    }

2.二进制流存储

    /**
     * 二进制流
     * 提供了InputStream接口和OutputStream接口的实现
     */
    @Test
    public void RedissonStream() throws IOException {
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);

        //====================操作流来存储对象====================
        RBinaryStream stream = redissonClient.getBinaryStream("stream");
        stream.set("name is ".getBytes());
        OutputStream outputStream = stream.getOutputStream();

        outputStream.write("victory".getBytes());
        InputStream inputStream = stream.getInputStream();

        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int length;
        while ((length = inputStream.read(bytes)) != -1) {
            result.write(bytes, 0, length);
        }
        System.out.println(result.toString());

        //====================关闭客户端====================
        redissonClient.shutdown();
    }

3.List

    /**
     * list
     * Redisson操作list
     */
    @Test
    public void list() {
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);

        //====================操作list====================
        RList<String> list = redissonClient.getList("list");
        list.add("victory1");
        list.add("victory2");
        System.out.println(list);
        //取值
        List<Object> list1 = redissonClient.getList("list").get();
        System.out.println(list1);
        //移除索引0位置元素
        list.remove(0);
        System.out.println(list);
        //通过key取value值
        List<Object> list2 = redissonClient.getList("list").get();
        System.out.println(list2);

        //====================关闭客户端====================
        redissonClient.shutdown();
    }

4.Set

    /**
     * set
     * Redisson操作set
     */
    @Test
    public void set() {
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);

        //====================操作list====================
        RSet<Object> set = redissonClient.getSet("set");
        set.add("victory1");
        set.add("victory2");
        System.out.println(set);
        //通过key取value值
        RSet<Object> set1 = redissonClient.getSet("set");
        System.out.println(set1);

        //====================关闭客户端====================
        redissonClient.shutdown();
    }

5.Map

Redisson将Redis中的字符串数据结构封装成了RMap,就是原本redis中的string类型
    /**
     * map
     * Redisson操作map
     * Redisson将Redis中的字符串数据结构封装成了RMap,就是原本redis中的string类型
     */
    @Test
    public void map() {
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);

        //====================操作list====================
        RMap<Object, Object> map = redissonClient.getMap("map");
        map.put("name1","victory1");
        map.put("name2","victory2");
        map.forEach((key,value)->{
            System.out.println("key = "+key+" ,value = "+ value);
        });

        //通过key取value值
        Object o = redissonClient.getMap("map").get("name1");
        System.out.println(o);

        //====================关闭客户端====================
        redissonClient.shutdown();
    }

6.队列

        这里的队列和下面要说的发布订阅的功能跟MQ的不能说是毫无关系,只能说是一毛一样,计划这个专栏结束下个专栏专门来聊MQ,欢迎个位大佬交流指正

    /**
     * 队列
     * Redisson操作queue
     */
    @Test
    public void queue() {
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);

        //====================操作queue====================
        RQueue<String> queue = redissonClient.getQueue("queue");
        //存值
        queue.add("victory1");
        queue.add("victory2");
        //取值
        String item = queue.poll();
        System.out.println(item);

        //
        RQueue<Object> queue1 = redissonClient.getQueue("queue");
        System.out.println(queue1);

        //====================关闭客户端====================
        redissonClient.shutdown();
    }

7.限流器

        基于Redis的分布式限流器可以用来在分布式环境下现在请求方的调用频率。既适用于不同Redisson实例下的多线程限流,也适用于相同Redisson实例下的多线程限流。该算法不保证公平性。

    /**
     * 限流器
     * Redisson操作rateLimiter
     */
    @Test
    public void rateLimiter() throws InterruptedException {
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);

        //====================操作rateLimiter====================
        RRateLimiter rateLimiter = redissonClient.getRateLimiter("rateLimiter");
        //创建限流器,最大流速:每1秒钟产生20个令牌
        rateLimiter.trySetRate(RateType.OVERALL, 20, 1, RateIntervalUnit.SECONDS);
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                int i = 0;
                @Override
                public void run() {
                    while(true) {
                        rateLimiter.acquire(1);
                        System.out.println(Thread.currentThread() + "-" + System.currentTimeMillis() + "-" + i++);
                    }
                }
            }).start();
        }
        //等待执行完成,不设置等待可能出现还未执行完成客户端就关闭的情况
        Thread.sleep(5000);

        //====================关闭客户端====================
        redissonClient.shutdown();
    }

8.可重入锁

    /**
     * 可重入锁
     * Redisson操作RLock
     */
    @Test
    public void lock() throws InterruptedException {
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);

        //====================操作lock====================
        RLock lock = redissonClient.getLock("lock");
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                lock.lock();
                try {
                    System.out.println(Thread.currentThread() + "-" + System.currentTimeMillis() + "-" + "获取了锁");
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }).start();
        }
        //等待执行完成,不设置等待可能出现还未执行完成客户端就关闭的情况
        Thread.sleep(5000);

        //====================关闭客户端====================
        redissonClient.shutdown();
    }

9.发布订阅

Redisson的分布式话题 RTopic 对象实现了发布、订阅的机制。

    /**
    *  发布订阅操作
    *  Redisson操作RTopic执行发布订阅操作
    **/
    @Test
    public void topicPublisherAndSubscriber() throws InterruptedException {
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);
        RedissonClient redissonClient1 = Redisson.create(config);

        //====================操作topic执行发布操作====================
        RTopic topic1 = redissonClient.getTopic("topic",new SerializationCodec());
        topic1.publish(new Message(1L,"victory",18));

        //====================操作topic执行订阅操作====================
        Thread.sleep(5000);
        RTopic topic = redissonClient1.getTopic("topic", new SerializationCodec());
        topic.addListener(Message.class, new MessageListener<Message>() {
            @Override
            public void onMessage(CharSequence channel, Message msg) {
                System.out.println("onMessage:=========" + channel + "; Thread:========= " + Thread.currentThread().toString());
                System.out.println(" name : " + msg.getName() + " age : " + msg.getAge());
                LoggerFactory.getLogger(RedissonDemo.class).info("Redisson接收到消息",msg);
            }
        });

        //====================关闭客户端====================
        redissonClient.shutdown();
        redissonClient1.shutdown();
    }

10.布隆过滤器

        布隆过滤器是一个非常长的二进制向量和一系列随机哈希函数的组合,可用于检索一个元素是否存在。

        主要作用

        应对缓存击穿等场景,如果高并发一波冷数据的情况下,大量的请求可能会击穿我们的缓存导致了雪崩,此时我们可以设置布隆过滤器,如果索引在我们的过滤器中,请求来我们先去查内存,如果内存没有再去查DB,如果索引不存在于过滤器中我们快速返回失败来提高程序的性能。

原理如下:

        建立一个二进制向量,所有位设置0;
        选择K个散列函数,用于对元素进行K次散列,计算向量的位下标;
        添加元素:将K个散列函数作用于该元素,生成K个值作为位下标,将向量的对应位设置为1;
        检索元素:将K个散列函数作用于该元素,生成K个值作为位下标,若向量的对应位都是1,则说明该元素可能存在;否则,该元素肯定不存在;

        当然他也有他的局限性,比如误差率。随着存入的元素数量增加,误差率随之增加。但是如果元素数量太少,则使用散列表足矣。

    /**
     * Redisson利用Redis实现了Java分布式布隆过滤器(Bloom Filter)
     * 作用:在缓存层前添加布隆过滤器,常用于高并发场景下应对缓存穿透问题
     * 布隆过滤器是一个非常长的二进制向量和一系列随机哈希函数的组合,可用于检索一个元素是否存在;
     *
     */
    @Test
    public void bloomFilter() {
        //====================创建Redisson客户端====================
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);


        //====================操作布隆过滤器====================
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("bloom-filter");
        // 初始化布隆过滤器,初始化预期插入的数据量为200,期望误差率为0.01
        bloomFilter.tryInit(200, 0.01);
        //插入数据
        bloomFilter.add("丁胜利");
        bloomFilter.add("丁向前");
        bloomFilter.add("胜利丁");
        //判断是否包含
        boolean victory = bloomFilter.contains("丁胜利");
        boolean forward = bloomFilter.contains("向前丁");
        System.out.println(victory); //true
        System.out.println(forward); //false

        //====================关闭客户端====================
        redissonClient.shutdown();
    }

 三.总结        

        Redisson相比于之前聊过的Jedis和Lettuce来说显得过于笨重,如果只是需要Redis实现简单的热点数据缓存和登录定时过期之类的小功能的话,建议在项目中慎重使用。

        到这里吧,Redisson可以做的事情还有很多,我只列举了同步操作的其中一部分就已经一万多字了,还有异步和响应式丁点都没有涉及。但是基本都大同小异,如果有理解不到的欢迎交流。

        因为之前虽然用过但是从来没有这么系统的去了解每一个功能,所以导致这一块在创作的时候需要查询大量的资料,Demo的进度也是格外的慢。学然后知不足,教然后知困。各位共勉。

        老规矩,需要Demo的话可评论或者私聊我,所有代码的Demo都有敲而且都会有保留,各位看官如果满意的话还请点个赞再走。下面我想想还要再补充一些集群相关的本专栏也就该结束了应该,暂时想不到还有其他什么需要补充的地方了。

参考文献

Redisson: Redis Java client with features of In-Memory Data Grid

概览 - 《Redisson 官方文档中文翻译》 - 书栈网 · BookStack

阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台

阿里云登录 - 欢迎登录阿里云,安全稳定的云计算服务平台

有关Redis入门到精通(十一),Redission操作Redis的更多相关文章

  1. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  2. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  3. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  4. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  5. ruby - 如何使用 Selenium Webdriver 根据 div 的内容执行操作? - 2

    我有一个使用SeleniumWebdriver和Nokogiri的Ruby应用程序。我想选择一个类,然后对于那个类对应的每个div,我想根据div的内容执行一个Action。例如,我正在解析以下页面:https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies这是一个搜索结果页面,我正在寻找描述中包含“Adoption”一词的第一个结果。因此机器人应该寻找带有className:"result"的div,对于每个检查它的.descriptiondiv是否包含单词“adoption

  6. ruby-on-rails - 如何处理 Grape 中特定操作的过滤器之前? - 2

    我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?

  7. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

  8. ruby - 在 Ruby 中是否有一种惯用的方法来操作 2 个数组? - 2

    a=[3,4,7,8,3]b=[5,3,6,8,3]假设数组长度相同,是否有办法使用each或其他一些惯用方法从两个数组的每个元素中获取结果?不使用计数器?例如获取每个元素的乘积:[15,12,42,64,9](0..a.count-1).eachdo|i|太丑了...ruby1.9.3 最佳答案 使用Array.zip怎么样?:>>a=[3,4,7,8,3]=>[3,4,7,8,3]>>b=[5,3,6,8,3]=>[5,3,6,8,3]>>c=[]=>[]>>a.zip(b)do|i,j|c[[3,5],[4,3],[7,6],

  9. ruby-on-rails - 如何让 Rails View 返回其关联的操作名称? - 2

    我有一个非常简单的Controller来管理我的Rails应用程序中的静态页面:classPagesController我怎样才能让View模板返回它自己的名字,这样我就可以做这样的事情:#pricing.html.erb#-->"Pricing"感谢您的帮助。 最佳答案 4.3RoutingParametersTheparamshashwillalwayscontainthe:controllerand:actionkeys,butyoushouldusethemethodscontroller_nameandaction_nam

  10. 区块链入门教程(6)--WeBASE-Front节点前置服务安装 - 2

    文章目录1.任务背景2.任务目标3.相关知识点4.任务实操4.1安装配置JDK4.2启动FISCOBCOS4.3下载解压WeBASE-Front4.4拷贝sdk证书文件4.5启动节点4.6访问节点4.7检查运行状态5.任务总结1.任务背景FISCOBCOS其实是有控制台管理工具,用来对区块链系统进行各种管理操作。但是对于初学者来说,还是可视化界面更友好,本节就来介绍WeBASE管理平台,这是一款微众银行开源的自研区块链中间件平台,可以降低区块链使用的门槛,大幅提高区块链应用的开发效率。微众银行是腾讯牵头设立的民营银行,在国内民营银行里还是比较出名的。微众银行参与FISCOBCOS生态建设,一定

随机推荐