Redisson 总结

1. 基础使用

1.1 引入依赖

xml 复制代码
<dependencies>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
        </dependency>
</dependencies>

包含的依赖如下

1.2 配置文件

其实默认主机就是 127.0.0.1,默认端口是 6379,一致的话可以不用配置

yaml 复制代码
spring:
  data:
    redis:
      host: 127.0.0.1
      password: redis

1.3 测试类

java 复制代码
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RBucket<String> bucket = redissonClient.getBucket("test");
        bucket.set("hello");
    }

    @GetMapping("/get")
    public String get() {
        RBucket<String> bucket = redissonClient.getBucket("test");
        return bucket.get();
    }
}

1.4 测试

访问 /test 接口,利用 Redis 管理工具可以看到数据已经添加了进来

在访问 /get 接口,成功获取到数据

2. 设置序列化

上面可以看到在 Redis 管理工具中查看数据是一串字符,并不直观,可以自定义数据序列化

2.1 配置类

java 复制代码
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        // 单机模式
        SingleServerConfig singleServerConfig = config.useSingleServer();
        singleServerConfig.setAddress("redis://127.0.0.1:6379");
        singleServerConfig.setPassword("redis");
        // JSON序列化
        config.setCodec(new JsonJacksonCodec());

        return Redisson.create(config);
    }
}

这里已经配置了主机等相关信息,因此配置文件里的配置可以去除,或者这里直接取配置文件的值,具体根据情况选择,其他的序列化类如下

编码类名称 说明
org.redisson.codec.JsonJacksonCodec Jackson JSON 编码
org.redisson.codec.AvroJacksonCodec Avro 一种二进制的 JSON 编码
org.redisson.codec.SmileJacksonCodec Smile一种 二进制的 JSON 编码
org.redisson.codec.CborJacksonCodec CBOR 一种二进制的 JSON 编码
org.redisson.codec.MsgPackJacksonCodec MsgPack 一种 二进制的 JSON 编码
org.redisson.codec.IonJacksonCodec Amazon Ion 亚马逊的 Ion 编码,格式与 JSON 类似
org.redisson.codec.KryoCodec Kryo 二进制对象序列化编码
org.redisson.codec.SerializationCodec JDK 序列化编码
org.redisson.codec.FstCodec FST 10 倍于 JDK 序列化性能而且 100% 兼容的编码
org.redisson.codec.LZ4Codec LZ4 压缩型序列化对象编码
org.redisson.codec.SnappyCodec Snappy 另一个压缩型序列化对象编码
org.redisson.client.codec.JsonJacksonMapCodec 基于 Jackson 的映射类使用的编码,可用于避免序列化类的信息,以及用于解决使用byte[] 遇到的问题
org.redisson.client.codec.StringCodec 纯字符串编码(无转换)
org.redisson.client.codec.LongCodec 纯整长型数字编码(无转换)
org.redisson.client.codec.ByteArrayCodec 字节数组编码
org.redisson.codec.CompositeCodec 用来组合多种不同编码在一起

2.2 测试

访问 /test 接口,再查看数据可以看到已经序列化成 JSON 格式

3. 基础数据结构使用

3.1 String

其实上面的示例用的就是字符串操作,通过 RBucket 对象来操作字符串数据结构

创建一个实体类

java 复制代码
import lombok.Builder;
import lombok.Data;

import java.io.Serial;
import java.io.Serializable;

@Data
@Builder
public class Article implements Serializable {

    @Serial
    private static final long serialVersionUID = -8862397425409851538L;
  
    private String title;

    private String content;
}

存储对象,简单示例如下:

java 复制代码
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RBucket<Object> bucket = redissonClient.getBucket("test");
        bucket.set(Article.builder().title("demo").content("test redisson").build());
    }
}

数据如下:

3.2 Hash

通过 RMap 对象来操作哈希数据结构,简单示例如下:

java 复制代码
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RMap<String, Article> rMap = redissonClient.getMap("test");
        rMap.put("k1", Article.builder().title("demo").content("test redisson").build());
    }
}

数据如下:

3.3 List

3.3.1 无序

通过 RList 对象来操作列表数据结构,简单示例如下:

java 复制代码
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RList<Article> rList = redissonClient.getList("test");
        rList.add(Article.builder().title("demo").content("test redisson").build());
        rList.add(Article.builder().title("demo").content("test redisson").build());
    }
}

数据如下:

3.3.2 有序

通过 RSortedSet 对象来操作有序集合数据结构

改造一下实体类,实现 Comparable 接口

java 复制代码
import lombok.Data;

import java.io.Serial;
import java.io.Serializable;

@Data
public class Article implements Serializable, Comparable<Article> {

    @Serial
    private static final long serialVersionUID = -8862397425409851538L;

    private Long id;

    private String title;

    private String content;

    @Override
    public int compareTo(Article article) {
        return this.getId().compareTo(article.getId());
    }
}

简单示例如下:

java 复制代码
import org.redisson.api.RSortedSet;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RSortedSet<Article> rSortedSet = redissonClient.getSortedSet("test");

        Article article1 = new Article();
        article1.setId(22L);
        article1.setTitle("demo");
        article1.setContent("test redisson");
        rSortedSet.add(article1);

        Article article2 = new Article();
        article2.setId(11L);
        article2.setTitle("demo");
        article2.setContent("test redisson");
        rSortedSet.add(article2);
    }
}

数据如下,可以看到数据根据 id 排序

3.4 Set

通过 RSet 对象来操作集合数据结构,简单示例如下:

java 复制代码
import org.redisson.api.RSet;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RSet<Article> rSet = redissonClient.getSet("test");
        rSet.add(Article.builder().title("demo").content("test redisson").build());
        rSet.add(Article.builder().title("demo").content("test redisson").build());
    }
}

数据如下,可以看到重复数据被去除了

3.5 Zset

通过 RScoredSortedSet 来操作带分数的有序集合数据结构,简单示例如下:

java 复制代码
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RScoredSortedSet<String> rScoredSortedSet = redissonClient.getScoredSortedSet("test");
        rScoredSortedSet.add(600.1, "test1");
        rScoredSortedSet.add(500.3, "test2");
        rScoredSortedSet.add(900.3, "test3");
        rScoredSortedSet.add(200.9, "test1");
    }
}

数据如下,可以看到根据分数来排序,并且重复数据被排除了

4. 布隆过滤器

可以用于检索一个元素是否在一个集合中

java 复制代码
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RBloomFilter<String> rBloomFilter = redissonClient.getBloomFilter("test");
        // 初始化预期插入的数据量为10000和期望误差率为0.01
        rBloomFilter.tryInit(10000, 0.01);
        // 插入部分数据
        rBloomFilter.add("100");
        rBloomFilter.add("200");
        rBloomFilter.add("300");
        // 设置过期时间
        rBloomFilter.expire(Duration.ofSeconds(30));
        // 判断是否存在
        System.out.println(rBloomFilter.contains("300")); // true
        System.out.println(rBloomFilter.contains("200")); // true
        System.out.println(rBloomFilter.contains("999")); // false
    }
}

5. 分布式自增 ID

参考代码如下:

java 复制代码
import org.redisson.api.RAtomicLong;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RAtomicLong rAtomicLong = redissonClient.getAtomicLong("test");
        System.out.println(rAtomicLong.incrementAndGet());
    }
}

6. 分布式锁

6.1 未加锁情况

模拟一个库存扣减操作

java 复制代码
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RBucket<Integer> bucket = redissonClient.getBucket("num");
        Integer num = bucket.get();
        if (num > 0) {
            System.out.println("扣减库存, 当前库存: " + --num);
            bucket.set(num);
        } else {
            System.out.println("库存不足");
        }
    }
}

使用 Jemter 模拟并发场景

点击运行后可以看到明显出现了并发问题

6.2 加锁情况

修改下库存扣减代码

java 复制代码
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RLock rLock = redissonClient.getLock("test");
        try {
            rLock.lock();
            RBucket<Integer> bucket = redissonClient.getBucket("num");
            Integer num = bucket.get();
            if (num > 0) {
                System.out.println("扣减库存, 当前库存: " + --num);
                bucket.set(num);
            } else {
                System.out.println("库存不足");
            }
        } finally {
            rLock.unlock();
        }
    }
}

再次模拟并发请求,可以看到问题已经解决

6.3 加锁操作耗时长

当加锁操作耗时较长时,如果多个请求进来,其他的请求会一直堵塞,可以使用 tryLock 来尝试获取锁,获取不到先返回响应

java 复制代码
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

    private final RedissonClient redissonClient;

    @Autowired
    public DemoController(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @GetMapping("/test")
    public void test() {
        RLock rLock = redissonClient.getLock("test");
        try {
            if (rLock.tryLock()) {
                RBucket<Integer> bucket = redissonClient.getBucket("num");
                Integer num = bucket.get();
                Thread.sleep(5000);
                if (num > 0) {
                    System.out.println("扣减库存, 当前库存: " + --num);
                    bucket.set(num);
                } else {
                    System.out.println("库存不足");
                }
            } else {
                System.out.println("请稍后再试");
            }
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
            Thread.currentThread().interrupt();
        } finally {
            // 是否是锁定状态且是当前执行线程的锁
            if (rLock.isLocked() && rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        }
    }
}

模拟并发请求,这里将时间设置长一点,可以看到获取到锁的请求则去进行库存扣减,获取不到先返回响应

相关推荐
如风暖阳2 小时前
Redis背景介绍
数据库·redis·缓存
lingllllove3 小时前
Redis脑裂问题详解及解决方案
数据库·redis·缓存
微光守望者3 小时前
Redis常见命令
数据库·redis·缓存
凌波漫步&3 小时前
通过Redisson构建延时队列并实现注解式消费
redis·电商平台·redisson订单自动取消
@_@哆啦A梦17 小时前
Redis 基础命令
java·数据库·redis
gentle coder19 小时前
Redis_Redission的入门案例、多主案例搭建、分布式锁进行加锁、解锁底层源码解析
java·redis·分布式
萝卜青今天也要开心19 小时前
读书笔记-《Redis设计与实现》(一)数据结构与对象(下)
java·数据结构·redis·学习
java1234_小锋21 小时前
说说Redis的内存淘汰策略?
数据库·redis·缓存
2的n次方_1 天前
【Redis】set 和 zset 类型的介绍和常用命令
数据库·redis·缓存
桂月二二1 天前
使用 Redis Streams 实现高性能消息队列
数据库·redis·缓存