Redisson 总结
1. 基础使用
1.1 引入依赖
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
</dependencies>
包含的依赖如下
1.2 配置文件
其实默认主机就是 127.0.0.1
,默认端口是 6379,一致的话可以不用配置
spring:
data:
redis:
host: 127.0.0.1
password: redis
1.3 测试类
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 配置类
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
对象来操作字符串数据结构
创建一个实体类
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;
}
存储对象,简单示例如下:
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
对象来操作哈希数据结构,简单示例如下:
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
对象来操作列表数据结构,简单示例如下:
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 接口
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());
}
}
简单示例如下:
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
对象来操作集合数据结构,简单示例如下:
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
来操作带分数的有序集合数据结构,简单示例如下:
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. 布隆过滤器
可以用于检索一个元素是否在一个集合中
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
参考代码如下:
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 未加锁情况
模拟一个库存扣减操作
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 加锁情况
修改下库存扣减代码
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
来尝试获取锁,获取不到先返回响应
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();
}
}
}
}
模拟并发请求,这里将时间设置长一点,可以看到获取到锁的请求则去进行库存扣减,获取不到先返回响应