目录
RedisTemplate 概述
1、Redis 是一个缓存、消息代理和功能丰富的键值存储。Spring Boot 为 Lettuce 和 Jedis 客户端库提供基本的自动配置,并为 Spring Data Redis 提供抽象。官网传送。
2、spring-boot-starter-data-redis 启动器,整合了需要的依赖项,默认情况下,它使用 Lettuce 作为客户端。这个启动器同时处理传统应用程序和反应性应用程序(reactive applications)。
3、官方还提供了一个 spring-boot-starter-data-redis-reactive 启动器,用于与具有 reactive 支持的其他存储保持一致。
4、可以像注入任何其他 Spring Bean 一样注入一个自动配置的 RedisConnectionFactory、StringRedisTemplate 或普通的 RedisTemplate 实例。class StringRedisTemplate extends RedisTemplate<String, String>
5、默认情况下,Redis 实例尝试连接本地主机(localhost)端口为 6379 的 Redis 服务器,默认使用空密码,连接 0 号数据库:
REDIS (RedisProperties)
spring.redis.cluster.max-redirects= # Maximum number of redirects to follow when executing commands across the cluster.
spring.redis.cluster.nodes= # Comma-separated list of "host:port" pairs to bootstrap from.
spring.redis.database=0 # Database index used by the connection factory.
spring.redis.url= # Connection URL. Overrides host, port, and password. User is ignored. Example: redis://user:password@example.com:6379
spring.redis.host=localhost # Redis server host.
spring.redis.jedis.pool.max-active=8 # 池中可以分配的最大连接数,使用负值表示无限制.
spring.redis.jedis.pool.max-idle=8 # 池中'空闲'连接的最大数量,使用负值表示空闲连接的数量不限.
spring.redis.jedis.pool.max-wait=-1ms # 池中连接耗尽时引发异常之前,等待连接分配的最长时间。使用负值可以无限期等待.
spring.redis.jedis.pool.min-idle=0 # 池中要维护的最小空闲连接数的目标,此设置仅在为正时有效.
spring.redis.lettuce.pool.max-active=8 # 池中可以分配的最大连接数,使用负值表示无限制.
spring.redis.lettuce.pool.max-idle=8 #池中'空闲'连接的最大数量,使用负值表示空闲连接的数量不限.
spring.redis.lettuce.pool.max-wait=-1ms # 池中连接耗尽时引发异常之前,等待连接分配的最长时间。使用负值可以无限期等待.
spring.redis.lettuce.pool.min-idle=0 # 池中要维护的最小空闲连接数的目标,此设置仅在为正时有效.
spring.redis.lettuce.shutdown-timeout=100ms # Shutdown timeout.
spring.redis.password= # Login password of the redis server.
spring.redis.port=6379 # Redis server port.
spring.redis.sentinel.master= # Name of the Redis server.
spring.redis.sentinel.nodes= # Comma-separated list of "host:port" pairs.
spring.redis.ssl=false # Whether to enable SSL support.
spring.redis.timeout= # Connection timeout.
6、可以实现 LettuceClientConfigurationBuilderCustomizer 接口,以实现更高级的定制。如果使用的是 Jedis 客户端,则实现 JedisClientConfigurationBuilderCustomizer 接口。
RedisTemplate 默认自己的序列化速度并不理想,实际生产中遇到一次需要将数据库查出来的一个 List 对象放到 Redis 中,当时数据量达到 20 万行,结果 put 的时间长达 6、7 秒,然后当使用 alibaba 的 fastjson 先将 List 对象序列化为字符串,然后在 put 到缓存,此时时间降到了 100 毫秒内,而 fastjson 序列化同样非常之快。取值时,同样将取出的字符串使用 fastjosn 反序列化成 List 即可。 |
Redis 依赖 与 配置
RedisAutoConfiguration 中已经自动将 RedisTemplate 添加到了容器中,直接获取使用即可.
1、RedisTemplate 对 lettuce 与 Jedis 封装之后,使用更加简单,如同使用 JDBCTemplate 对 JDBC 进行了封装。封装之后不需要再关心打开与关闭连接等繁琐的操作,而且它提供性能更好的连接池等。
2、本文环境 Spring Boot 2.1.3 + Java JDK 1.8,pom.xml 文件中导入依赖,然后在需要地方直接注入 RedisTemplate 或者 StringRedisTemplate 使用即可:
<!--Spring Boot redis 启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--为 redis 导入 commons-pool2 连接池依赖 -->
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
3、全局配置文件配置 Redis 如下,连接本地没有设置密码的 Redis 服务器:
#Redis 配置,以下除了 timeout 外全部为默认配置.
spring:
redis:
host: localhost
port: 6379
database: 0
password:
timeout: 10000
lettuce:
pool:
max-active: 1000 #池中可以分配的最大连接数,使用负值表示无限制。
max-idle: 500 #池中'空闲'连接的最大数量,使用负值表示空闲连接的数量不限。
min-idle: 500 #池中要维护的最小空闲连接数的目标,此设置仅在为正时有效。
max-wait: -1ms #池中连接耗尽时引发异常之前,等待连接分配的最长时间。使用负值可以无限期等待。
3、Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合),RedisTemplate 分别提供了对应的 API 进行操作它们。
字符串(String) 处理
org.springframework.data.redis.core.ValueOperations 处理 String 类型数据:
方法 | 描述 |
---|---|
set(K key, V value) | 存储到 redis。key 已经存在时,覆盖旧值,不存在时新增。key、value 不能为 null |
set(K key, V value, long timeout, TimeUnit unit) | 设置失效时间为指定时间。超过后 key 会被移除 |
set(K key, V value, Duration timeout) | 设置超时时间,Duration 是 Jdk 8 的 API。超过指定时间后,key 被移除 |
set(K key, V value, long offset) | 覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始 |
get(Object key) | 获取指定 key 的 value,key 不存在时,返回 null。key 不能 null |
getAndSet(K key, V value) | 获取 key 的旧值,并赋值新的 value。key 不能 null |
get(K key, long start, long end) | 获取指定 key 指定范围的字符串 [start,end],从索引 0 开始。key 不能 null |
append(K key, String value) | 将新的 value 追加到 key 的旧值尾部,key 不存在时,自动新建。key 不能为 null。 |
Long size(K key) | 获取 key 的 value 的长度,key 不存在时,返回 0。key 不能为 null。 |
Boolean setIfAbsent(K key, V value) | 如果 key 不存在,则新增,否则不改变原来的值。其余重载方法与 set 方法对应。key、value 不能为 null。 |
Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit) Boolean setIfAbsent(K key, V value, Duration timeout) | 1、key 不存在时进行设值,返回 true; 否则 key 存在时,不进行设值,返回 false. 2、此方法相当于先设置 key,然后设置 key 的过期时间,它的操作是原子性的,是事务安全的。 3、相当于:SET anyLock unique_value NX PX 30000,NX是指如果key不存在就成功,key存在返回false,PX可以指定过期时间 |
Boolean setIfPresent(K key, V value, Duration timeout) | key 存在时,才修改它的值,否则不新增,与 setIfAbsent 相反。 |
Long increment(K key) | 将存储为字符串值的整数值增加1,并返回结果。key 不存在时自动新增 |
Long increment(K key, long delta) | 将存储为字符串值的整数值增加 delta,并返回结果。key 不存在时自动新增。delta 为负数时,做减法 |
Double increment(K key, double delta) | 将存储为字符串值的小数值增加 delta,并返回结果。key 不存在时自动新增 |
Long decrement(K key) | 将存储为字符串的整数值减1,key 不存在时自动创建 |
Long decrement(K key, long delta) | 减去指定的数值,key 不存在时自动创建。delta |
List multiGet(Collection keys) | 同时获取多个 key 的值,按请求的 key 的顺序返回对应的 value,key 不存在的返回 null。 |
multiSet(Map<? extends K, ? extends V> map) | 同时存储多个 key-value |
Boolean multiSetIfAbsent(Map<? extends K, ? extends V> map) | 当 key 不存在时新增,否则进行操作。 |
本文演示源码:src/test/java/com/wmx/wmxredis/RedisTest.java · 汪少棠/wmx-redis - Gitee.com
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisStuWebApplicationTests {
//注入 RedisTemplate 或者 StringRedisTemplate 其中一个即可,前者是后者的父类,它们都已经默认在 Spring 容器中了。
//org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 中已经自动将 RedisTemplate 添加到了容器中,直接获取使用即可.
@Resource
private RedisTemplate redisTemplate;
@Resource
private StringRedisTemplate stringRedisTemplate;
//org.springframework.data.redis.core.ValueOperations 处理 String 类型数据
@Test
public void test1() {
redisTemplate.getConnectionFactory().getConnection().flushAll();//清空 redis 所有缓存的数据。
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
valueOperations.set("user.name", "张无忌 ");//添加值
valueOperations.set("upload.process", "65", 60, TimeUnit.SECONDS);//添加值,失效时间60秒
valueOperations.set("user.status", "online", Duration.ofSeconds(20));//添加值,失效时间20秒
String user_name = valueOperations.get("user.name");//取值
String upload_process = valueOperations.get("upload.process");//取值
String user_status = valueOperations.get("user.status");//取值
String user_status2 = valueOperations.get("user.status", 2, 4);//取值,截取子符串
//输出:张无忌 , 65, online, lin
System.out.println(user_name + ", " + upload_process + ", " + user_status + ", " + user_status2);
Integer hello = valueOperations.append("user.hit", "Hello");//为末尾追加值
String hit1 = valueOperations.get("user.hit");//取值
Integer hello2 = valueOperations.append("user.hit", " World");//为末尾追加值
String hit2 = valueOperations.get("user.hit");//取值
Long size = valueOperations.size("user.hit");//字符串的长度
//输出:5 - Hello, 11 - Hello World
System.out.println(hello + " - " + hit1 + ", " + hello2 + " - " + hit2 + ", " + size);
//key 不存在时,才进行添加
Boolean aBoolean1 = valueOperations.setIfAbsent("name", "三丰", 60, TimeUnit.SECONDS);
Boolean aBoolean2 = valueOperations.setIfAbsent("name", "四喜", 60, TimeUnit.SECONDS);
System.out.println(aBoolean1 + ", " + aBoolean2 + ", " + valueOperations.get("name"));//输出:true, false, 三丰
//key 存在时,才进行添加
Boolean age1 = valueOperations.setIfPresent("age", "45", Duration.ofSeconds(60));
valueOperations.set("age", "35");
Boolean age2 = valueOperations.setIfPresent("age", "55", Duration.ofSeconds(60));
System.out.println(age1 + ", " + age2 + ", " + valueOperations.get("age"));//输出:false, true, 55
valueOperations.set("l1", "1", Duration.ofSeconds(60));
valueOperations.set("d1", "3.14", Duration.ofSeconds(60));
valueOperations.set("total", "100", Duration.ofSeconds(60));
Long count1 = valueOperations.increment("l1");//数值做加法
Long count2 = valueOperations.increment("l1", 10L);
Double d1 = valueOperations.increment("d1", 40.00);
Long total1 = valueOperations.decrement("total");//数值做减法
Long total2 = valueOperations.decrement("total2", 35);
System.out.println(count1 + ", " + count2 + ", " + d1 + ", " + total1 + ", " + total2);//输出:2, 12, 43.14, 99, 64
List<String> keyList = new ArrayList<>();
keyList.add("l1");
keyList.add("d1");
keyList.add("f1");
List<String> list = valueOperations.multiGet(keyList);//同时获取多个 key 的 value
System.out.println(list);//[12, 43.140000000000001, null]
Map<String, String> dataMap = new LinkedHashMap<>();
dataMap.put("multi_name", "雄霸");
dataMap.put("multi_age", "44");
valueOperations.multiSet(dataMap);//同时设置多个key-value
//输出:[雄霸, 44]
System.out.println(valueOperations.multiGet(Arrays.asList(new String[]{"multi_name", "multi_age"})));
}
}
本文演示源码:src/test/java/com/wmx/wmxredis/RedisTest.java · 汪少棠/wmx-redis - Gitee.com
列表(List) 处理
org.springframework.data.redis.core.ListOperations 处理 List 类型数据:
方法 | 描述 |
---|---|
Long size(K key) | 返回存储在键中的列表的长度。键不存在时,返回 0;key 存储的值不是列表时返回错误。 |
Long leftPush(K key, V value | 将指定的值插入到列表的头部。键不存在自动创建。(从左边插入)。返回 list 的大小(size)。 |
Long leftPushAll(K key, V... values) | 批量从左插入。key 不能为 null。返回 list 的大小(size)。 |
Long leftPushAll(K key, Collection values) | 批量从左插入。key,values 不能为 null。 |
Long leftPush(K key, V pivot, V value) | 对于名字为 key 的 list,从左往右先查询第一个出现的 pivot 元素,然后将 value 插入到它的左边。 |
Long leftPushIfPresent(K key, V value) | 只有当 key 存在时,才新增,否则不处理。 |
Long rightPush(K key, V value) | 右侧插入,key 不存在时,自动创建 |
Long rightPush(K key, V pivot, V value) | 从左往右查找第一个 pivot 元素,然后将新的 value 插入到它的右侧。 |
Long rightPushAll(K key, Collection values) | 右侧批量插入 |
Long rightPushAll(K key, V... values) | 右侧批量插入 |
Long rightPushIfPresent(K key, V value) | 当 key 存在时,进行右侧插入,否则不处理。 |
set(K key, long index, V value) | 为名字为 key 的列表索引为 index 的元素替换为新的 value 值。key 必须存在,index 索引不能越界 |
Long remove(K key, long count, Object value) | 删除列表(key) 中指定个数(count)的元素(value)。count=0:删除所有 value;count>0:从左往右删除 count 个 value;count<0,从右往左删除。 |
List range(K key, long start, long end) | 查询 list 的元素值,索引范围为 [start,end],-1 表示倒数第一个元素 |
V index(K key, long index) | 获取列表中指定索引处的元素。index 越界时,返回 null;key 不存在时,返回 null。索引 index=-1 表示倒数第一个元素。 |
V leftPop(K key) | 弹出最左边的元素,弹出之后该值在列表中将不复存在。当列表为空时,返回 null。 |
V rightPop(K key) | 弹出最右边的元素,弹出之后该值在列表中将不复存在。当列表为空时,返回 null。 |
V leftPop(K key, long timeout, TimeUnit unit) | 弹出最左边的元素,当元素不存在时,线程阻塞等待,直到存在新元素,或者超时抛出异常 |
V rightPop(K key, long timeout, TimeUnit unit) | 弹出最右边的元素,当元素不存在时,线程阻塞等待,直到存在新元素,或者超时抛出异常 |
本文演示源码:src/test/java/com/wmx/wmxredis/RedisTest.java · 汪少棠/wmx-redis - Gitee.com
//org.springframework.data.redis.core.ListOperations 处理 List 类型数据
@Test
public void test2() {
redisTemplate.getConnectionFactory().getConnection().flushAll();//清空 redis 所有缓存的数据。
ListOperations<String, String> opsForList = stringRedisTemplate.opsForList();
Long aLong1 = opsForList.leftPush("list_1", "USA");//将值添加到 list 头部
Long aLong2 = opsForList.leftPush("list_1", "China");//将值添加到 list 头部
Long aLong3 = opsForList.leftPushAll("list_1", new String[]{"Java", "C++", "USA", "WPF"});//将多个值添加到 list 头部
Long size = opsForList.size("list_1");//获取 list 大小
Long aLong4 = opsForList.leftPush("list_1", "Java", "C#");//将值添加到 list 头部
Long aLong5 = opsForList.rightPush("list_1", "USA", "Tomcat");//将值添加到 list 头部
//输出:1, 2, 6, 7, 8, 6
System.out.println(aLong1 + ", " + aLong2 + ", " + aLong3 + ", " + aLong4 + ", " + aLong5 + ", " + size);
System.out.println(opsForList.range("list_1", 0, -1));//输出:[WPF, USA, Tomcat, C++, C#, Java, China, USA]
Long aLong = opsForList.leftPushIfPresent("list_2", "Apple");//当 key 存在时,才进行添加
System.out.println(aLong + ", " + aLong5);//0, 8
opsForList.rightPush("list_2", "2019");//将值添加到 list 尾部
opsForList.rightPush("list_2", "2018");
opsForList.rightPush("list_2", "2017");
opsForList.set("list_2", 1, "2008");
System.out.println(opsForList.range("list_2", 0, -1));//获取指定索引范围的值,[2019, 2008, 2017]
//同时将多个值添加到右侧
opsForList.rightPushAll("list_3", new String[]{"mysql", "oracle", "sql server", "redis", "oracle", "sql server", "redis", "oracle"});
System.out.println(opsForList.range("list_3", 0, -1));//输出:[mysql, oracle, sql server, redis, oracle, sql server, redis, oracle]
//删除 list 中所有的 "sql server" 值
Long remove1 = opsForList.remove("list_3", 0, "sql server");//2 - [mysql, oracle, redis, oracle, redis, oracle]
System.out.println(remove1 + " - " + opsForList.range("list_3", 0, -1));
//从右往左删除第一次出现的 redis 值
Long remove2 = opsForList.remove("list_3", -1, "redis");//1 - [mysql, oracle, redis, oracle, oracle]
System.out.println(remove2 + " - " + opsForList.range("list_3", 0, -1));
//从左往右删除第一次、第二次出现的 oracle 值
Long remove3 = opsForList.remove("list_3", 2, "oracle");//2 - [mysql, redis, oracle]
System.out.println(remove3 + " - " + opsForList.range("list_3", 0, -1));
opsForList.rightPushAll("list_4", new String[]{"mysql", "oracle", "redis", "DB2"});
String index1 = opsForList.index("list_4", -1);//查询倒数第一个元素
String leftPop1 = opsForList.leftPop("list_4");//弹出头部第一个元素
String rightPop1 = opsForList.rightPop("list_4");//弹出尾部第一个元素
System.out.println(index1 + ", " + leftPop1 + ", " + rightPop1);//DB2, mysql, DB2
opsForList.rightPushAll("list_5", new String[]{"Python", "C#", "Java"});
for (int i = 0; i < 4; i++) {
//弹出头部元素,当超过60秒还没有元素时,抛出异常 RedisCommandTimeoutException
//System.out.println(opsForList.leftPop("list_5", 60, TimeUnit.SECONDS));//可以手动通过 redis 客户添加元素:lpush list_5 wmx
}
}
本文演示源码:src/test/java/com/wmx/wmxredis/RedisTest.java · 汪少棠/wmx-redis - Gitee.com
哈希(Hash) 处理
org.springframework.data.redis.core.HashOperations 处理 Hash 类型数据:
方法 | 描述 |
---|---|
Long size(H key) | 获取指定 hash(key) 的元素个数,kye 不存在时,返回 0 |
Boolean hasKey(H key, Object hashKey) | 判断 hash 中 是否存在指定的元素,key 不存在时,返回 false |
Set keys(H key) | 获取指定 hash(key) 中所有的 hashKey。key 不存在时,返回空集合[]. |
List values(H key) | 获取指定 hash(key) 的所有值。key 不存在时,返回空 list |
Long delete(H key, Object... hashKeys) | 删除指定的 hash(key) 中指定的元素(hansKeys),返回成功删除元素个数,key 不存在时返回 0 |
void put(H key, HK hashKey, HV value) | 往 key 中插入键值对 hashKey-value。hashKey 重复时,后插入的覆盖旧值。key 不存在时,自动新增 |
void putAll(H key, Map<? extends HK, ? extends HV> m) | 单次插入多个元素。key 不存在时自动新增 |
Boolean putIfAbsent(H key, HK hashKey, HV value) | key 或者 hashKey 不存在时,才插入值 |
HV get(H key, Object hashKey) | 获取 hash 中指定的 hashKey,key 不存在时,返回 null. |
Map<HK, HV> entries(H key) | 获取指定 key 的 hash map。key 不存在时,返回空 map。 |
Long lengthOfValue(H key, HK hashKey) | 获取 key 对应的 hash map 的 hashKey 对应的元素值的长度 |
List multiGet(H key, Collection hashKeys) | 同时获取 key 对应的 hash map 中的多个元素值,hashKey 不存在时,返回 null |
Long increment(H key, HK hashKey, long delta) | 为指定 key 的 hash map 中的元素 hashKey 的数值加上整数值 delta。key 不存在时,直接新增。 |
Double increment(H key, HK hashKey, double delta) | 为指定 key 的 hash map 中的元素 hashKey 的数值加上浮点数值 delta。key 不存在时,直接新增 |
//org.springframework.data.redis.core.HashOperations 处理 hash 数据类型
@Test
public void test3() {
redisTemplate.getConnectionFactory().getConnection().flushAll();//清空 redis 所有缓存的数据。
HashOperations<String, Object, Object> opsForHash = stringRedisTemplate.opsForHash();
Long delete = opsForHash.delete("hash_1", "name", "age");//删除指定元素
Boolean hasKey = opsForHash.hasKey("hash_1", "name");//判断是否含有指定的元素
Object name1 = opsForHash.get("hash_1", "name");//获取指定的元素
Set<Object> keys = opsForHash.keys("hash_1");//获取所有的 key
Long hash_1 = opsForHash.size("hash_1");//获取 hash 的大小
System.out.println(delete + ", " + hasKey + ", " + name1 + ", " + keys + ", " + hash_1);//输出:0, false, null, [], 0
Map<String, String> dataMap = new HashMap<>();
dataMap.put("age", "33");
dataMap.put("name", "Li Si");
dataMap.put("address", "人民东路45号");
opsForHash.put("hash_1", "name", "Zan San");//为 hash 添加单个元素
opsForHash.putAll("hash_1", dataMap);//为 hash 添加多个元素
opsForHash.putIfAbsent("hash_2", "name", "Chang Sha");//当 key 或者 hashKey 不存在时,才插入值
List<Object> list = opsForHash.values("hash_1");//获取所有的 values
Map<Object, Object> entries = opsForHash.entries("hash_1");//获取整个 hash
Long lengthOfValue1 = opsForHash.lengthOfValue("hash_1", "name");//获取元素的长度
Long lengthOfValue2 = opsForHash.lengthOfValue("hash_1", "age");
List<Object> multiGet = opsForHash.multiGet("hash_1", Arrays.asList(new String[]{"name", "address", "birthday"}));//同时获取多个元素值
//[Li Si, 人民东路45号, 33], {name=Li Si, address=人民东路45号, age=33}, 5, 2, [Li Si, 人民东路45号, null]
System.out.println(list + ", " + entries + ", " + lengthOfValue1 + ", " + lengthOfValue2 + ", " + multiGet);
Long increment_age1 = opsForHash.increment("hash_1", "age", 20);//数值元素做加法
Double increment_age2 = opsForHash.increment("hash_1", "age", 20.45D);
System.out.println(increment_age1 + ", " + increment_age2);//53, 73.45
}
本文演示源码:src/test/java/com/wmx/wmxredis/RedisTest.java · 汪少棠/wmx-redis - Gitee.com
集合(Set) 处理
org.springframework.data.redis.core.SetOperations 处理无序集合(Set) 类型数据:
方法 | 描述 |
---|---|
Long add(K key, V... values) | 往 set 中添加元素,key 不存在时,自动新建。value 重复时,覆盖旧值。返回 set 中的元素个数。 |
Long remove(K key, Object... values) | 移除 set 中的元素。返回 set 中剩余的元素个数。set 中没有元素时,自动删除 key。key 不存在时不影响。 |
Boolean move(K key, V value, K destKey) | 将 key 对应的 set 中的值 value 剪切到目标 set(destKey) 中。key、value、destKey 不存在时,不做任何操作. |
V pop(K key) | 随机弹出 set 中的一个元素,弹出后,set 中就没有了。当 set 为空时,返回 null. |
List pop(K key, long count) | 随机弹出 set 中 count 个元素,如果 set 的大小为 3,而 count 为 5,此时只会返回 3 个元素。 |
Long size(K key) | 获取 key 对应 set 的集合大小. key 不存在时,返回 0 |
Set members(K key) | 获取 set 中所有的元素,key 不存在时,返回空 set. |
V randomMember(K key) | 从 set 中随机返回一个元素,set 中此元素还会存在。key 不存在时,返回 null. |
List randomMembers(K key, long count) | 随机返回 set 中 count 个元素。 |
Boolean isMember(K key, Object o) | 判断 key 对应的 set 中是否包含元素 o。key 不存在时,返回 false。 |
Set union(K key, K otherKey) | 获取 key 与 otherKey 对应的 set 的并集。旧值不受影响,返回合并后的值。 |
Set union(K key, Collection otherKeys) | 获取多个 set 的并集 |
Long unionAndStore(K key, K otherKey, K destKey) | 求 key 、otherKey 的 set 并集,并将结果添加到 destKey 中。destKey 不存在时,自动创建。返回新集合中的元素个数。当 destKey 中事先已经有值,则会被先清除。 |
Long unionAndStore(K key, Collection otherKeys, K destKey) | 将多个 set 的并集结果放入到 destKey 对应的 set 中,destKey 不存在时,自动创建。当 destKey 中事先已经有值,则会被先清除。 |
Set difference(K key, K otherKey) | 求 key 与 otherKey 对应的 set 的差集,key 减去 otherKey 后剩余的结果。 |
Set difference(K key, Collection otherKeys) | 获取 key 与所有 otherKeys 的差集。 |
Long differenceAndStore(K key, K otherKey, K destKey) | 将差集结果放入到 destKey 中。当 destKey 中事先已经有值,则会被先清除。 |
Long differenceAndStore(K key, Collection otherKeys, K destKey) | 将差集结果放入到 destKey 中。当 destKey 中事先已经有值,则会被先清除。 |
Set intersect(K key, K otherKey) | 获取 key 与 otherKey 的交集,双方都拥有的元素 |
Set intersect(K key, Collection otherKeys) | 获取 key 与其它所有 otherKeys 的交集 |
Long intersectAndStore(K key, K otherKey, K destKey) | 将交集结果放到 destKey 集合中。当 destKey 中事先已经有值,则会被先清除。 |
Long intersectAndStore(K key, Collection otherKeys, K destKey) | 将交集结果放到 destKey 集合中。当 destKey 中事先已经有值,则会被先清除。 |
本文演示源码:src/test/java/com/wmx/wmxredis/RedisTest.java · 汪少棠/wmx-redis - Gitee.com
//org.springframework.data.redis.core.SetOperations 处理 Set 类型数据:
@Test
public void test4() {
stringRedisTemplate.getConnectionFactory().getConnection().flushAll();//情况所有缓存数据
SetOperations<String, String> opsForSet = stringRedisTemplate.opsForSet();
Long add = opsForSet.add("set_1", "C", "Java", "Python");//往集合添加元素
Long remove = opsForSet.remove("set_1", "C");//删除元素
String set_1 = opsForSet.pop("set_1");//随机弹出 set 中的一个元素
System.out.println(add + ", " + remove + ", " + set_1);//3, 1, Python
opsForSet.add("set_2", "华山", "泰山", "衡山");
Set<String> set_2 = opsForSet.members("set_2");//查询 set 中的所有元素
opsForSet.move("set_1", "Java", "set_2");
System.out.println(set_2);//[泰山, 衡山, 华山]
Long size = opsForSet.size("set_2");//获取 set 的大小
System.out.println(opsForSet.members("set_2") + ", " + size);//[泰山, 衡山, 华山], 3
Boolean member1 = opsForSet.isMember("set_2", "泰山");//判断 set 中是否含义指定的元素值
Boolean member2 = opsForSet.isMember("set_2", "嵩山");
System.out.println(member1 + ", " + member2);//true, false
opsForSet.add("set_3", "Oracle", "Mysql", "DB2", "H2", "Sql Server");
List<String> set_3 = opsForSet.pop("set_3", 3);
System.out.println(set_3);//[Oracle, DB2, Mysql]
opsForSet.add("set_4", "段誉", "乔峰", "虚竹", "王语嫣", "慕容复");
System.out.println(opsForSet.members("set_4") + ", " + opsForSet.randomMember("set_41"));
System.out.println(opsForSet.randomMembers("set_4", 3));//随机弹出 set 中指定个数的元素,如随机弹出其中3个元素
opsForSet.add("set_5_1", "a", "b", "c", "1", "2", "3");
opsForSet.add("set_5_2", "x", "y", "c", "21", "2", "3");
Set<String> union1 = opsForSet.union("set_5_1", "set_5_2");//两个集合求并集
System.out.println(opsForSet.members("set_5_1") + ", " + opsForSet.members("set_5_2"));
System.out.println(union1);//[3, a, x, c, b, 2, 21, 1, y]
//多个集合求并集
Set<String> set_5_1 = opsForSet.union("set_5_1", Arrays.asList(new String[]{"set_5_2", "set_4"}));
System.out.println(set_5_1);//[王语嫣, 段誉, 21, a, 3, 虚竹, x, b, c, 慕容复, 1, y, 乔峰, 2]
//多个集合求并集,并将结果存储到新的集合中
Long aLong = opsForSet.unionAndStore("set_5_1", "set_5_2", "set_5_3");
System.out.println(aLong + ", " + opsForSet.members("set_5_3"));//9, [3, a, x, c, b, 2, 21, 1, y]
Set<String> difference = opsForSet.difference("set_5_1", "set_5_2");//求两个集合的差集
System.out.println(difference);//[a, 1, b]
Set<String> intersect = opsForSet.intersect("set_5_1", "set_5_2");//求两个集合的交集
opsForSet.intersectAndStore("set_5_1", "set_5_2", "set_5_3");//当set_5_3集合中有值时,会先被清除
System.out.println(intersect);//[3, c, 2]
}
有序集合(sorted set)
org.springframework.data.redis.core.ZSetOperations 处理有序集合(Set) 的数据:
方法 | 描述 |
---|---|
Boolean add(K key, V value, double score) | 为有序集合添加元素. 如果 value 已经存在,则更新. score 表示权重,用于排序,值越小,排序越靠前(由小到大)。key 不存在时,直接新增。成功插入时返回 true,失败时返回 false。 |
Long rank(K key, Object o) | 获取元素 o 在有序集合中的索引 |
Set range(K key, long start, long end) | 获取有序的集合中指定索引范围的值,索引从0开始,-1 表示倒数第一个元素。 |
Set rangeByScore(K key, double min, double max) | 根据权重范围查询 |
Set rangeByScore(K key, double min, double max, long offset, long count) | offset 表示偏移量,count 表示需要获取的个数 |
Long count(K key, double min, double max) | 获取权重在 [min,max] 直接的元素个数 |
Long remove(K key, Object... values) | 删除有序集合中指定的元素(value) |
Long removeRange(K key, long start, long end) | 删除有序集合中指定索引范围内的元素 |
Long removeRangeByScore(K key, double min, double max) | 删除有序集合中指定权重范围内的元素 |
Long zCard(K key) | 获取集合的大小 |
Long size(K key) | 获取集合的大小,底层调用 zCard |
Double score(K key, Object o) | 获取集合中指定元素的权重。 |
本文演示源码:src/test/java/com/wmx/wmxredis/RedisTest.java · 汪少棠/wmx-redis - Gitee.com
//org.springframework.data.redis.core.ZSetOperations 处理有序集合(Set) 的数据:
@Test
public void test5() {
stringRedisTemplate.getConnectionFactory().getConnection().flushAll();//清空所有缓存
ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();
Boolean add1 = opsForZSet.add("zset1", "C++", 3.8);
Boolean add2 = opsForZSet.add("zset1", "Java", 0.3);
Boolean add3 = opsForZSet.add("zset1", "Python", 2.5);
Set<String> zset1 = opsForZSet.range("zset1", 0, -1);
System.out.println(add1 + ", " + add2 + ", " + add3 + ", " + zset1);//输出:true, true, true, [Java, Python, C++]
Long rank = opsForZSet.rank("zset1", "C++");
Long count = opsForZSet.count("zset1", 0.3, 2.5);
Long size = opsForZSet.size("zset1");
Double score = opsForZSet.score("zset1", "Java");
//输出:2, 2, 3, 0.3, [Java, Python, C++]
System.out.println(rank + ", " + count + ", " + size + ", " + score + ", " +
opsForZSet.range("zset1", 0, -1));
opsForZSet.add("zset2", "12", 0);
opsForZSet.add("zset2", "3c", 1);
opsForZSet.add("zset2", "4f", 2);
opsForZSet.add("zset2", "6h", 3);
opsForZSet.add("zset2", "7p", 4);
Long remove = opsForZSet.remove("zset2", "4f");
//Long aLong = opsForZSet.removeRange("zset2", 1, 2);
Long zset2 = opsForZSet.removeRangeByScore("zset2", 2.0, 3.0);
System.out.println(opsForZSet.range("zset2", 0, -1));
}
RedisTemplate 事务
1、事务提供了一种将多个命令打包,然后一次性、有序地执行机制,多个命令会被入列到事务队列中,然后按先进先出(FIFO)的顺序执行。事务在执行过程中不会被中断,当事务队列中的所有命令都被执行完毕之后,事务才会结束。
2、RedisTemplate 的 setEnableTransactionSupport(true|false) 表示是否开启事务支持(默认不开启)。如果设置为 true,RedisTemplate 将使用 MULTI…EXEC|DISCARD}来跟踪操作,Redis Multi 命令用于标记一个事务块的开始,事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。
3、Redis 的事务命令主要是 multi(开启事务) 、exec(执行事务) 、discard(丢弃事务,类似回滚)。
1 | 业务方法无论有没有加 Spring @Transactional 事务注解,方法发生异常时,默认情况下 RedisTemplate 的增、删、改等操作都不会回滚,RedisTemplate API 一执行,redis 数据库中就会立马生效。 |
2 | RedisTemplate 使用 setEnableTransactionSupport 手动管理事务时,multi() 开启事务后,可以提交(exec) 、丢弃(discard)-类似回滚。 只要未提交事务,则命令都不会执行,一旦提交事务,则全部命令会立即执行,redis中的数据会立马更新。 如果丢弃事务,则所有命令都将丢弃,不会再生效。 |
@GetMapping("redis/setValue")
@Transactional(rollbackFor = Exception.class)
public ResultData<Object> setValue(@RequestParam String key,
@RequestParam String value) {
redisTemplate.opsForValue().set(key, value, 60, TimeUnit.SECONDS);
// 故意发生异常(上面的方法执行完成,redis 中就会看到数据,即使发生异常,redis 数据也不会回滚)
System.out.println(Integer.parseInt(key));
Long setValueCount = redisTemplate.opsForValue().increment("setValue_count", 1);
return new ResultData(setValueCount);
}
@GetMapping("redis/setValue2")
@Transactional(rollbackFor = Exception.class)
public ResultData<Object> setValue2(@RequestParam String key,
@RequestParam String value) {
Long setValueCount = null;
try {
// 设置启用事务支持(默认是false)
redisTemplate.setEnableTransactionSupport(true);
// 开启事务
redisTemplate.multi();
// 设置缓存
redisTemplate.opsForValue().set(key, value, 60, TimeUnit.SECONDS);
// 自增,并返回自增后的结果,key 不存在时,自动新增为1.
// 因为此时事务还未提交,所以会返回 null.
setValueCount = redisTemplate.opsForValue().increment("setValue_count", 1);
// 故意发生异常(上面的方法执行完成,redis 中就会看到数据,即使发生异常,redis 数据也不会回滚)
System.out.println(Integer.parseInt(key));
// 提交/执行事务(只要未提交事务,则上面的命令都不会执行),一旦提交事务,则全部命令会立即执行,redis中的数据会立马更新
// 注意:如果事务已经丢弃(discard),则不能再提交,否则异常:InvalidDataAccessApiUsageException: No ongoing transaction. Did you forget to call multi?
redisTemplate.exec();
} catch (Exception e) {
// 丢弃事务(所有命令都将丢弃,不会再生效)
// 如果事务已经提交,则不能在丢弃事务,否则报错:java.lang.IllegalStateException: Connection has no active transaction
redisTemplate.discard();
throw e;
} finally {
// 关闭事务支持
// redisTemplate.setEnableTransactionSupport(false);
}
return new ResultData(setValueCount);
}
src/main/java/com/wmx/wmxredis/controller/RedisController.java · 汪少棠/wmx-redis - Gitee.com
4、如果需要使用 Redis 事务,建议提供两个 RedisTemplate Bean,一个是用来执行 Redis 事务,一个是用来执行普通 Redis 命令(不支持事务,即默认的)。不同的地方引入不同的 Bean,只需要在配置 RedisTemplate 实例的时候加上 redisTemplate.setEnableTransactionSupport(true); ,这样使用时就无需在 set,可以直接使用事务。
src/main/java/com/wmx/wmxredis/config/RedisConfig.java · 汪少棠/wmx-redis - Gitee.com