一:簡介
SpringDataRedis是SpringData開源項目中的一部分,它可以在Spring項目中更靈活簡便的訪問和操作Redis;原先在沒有SpringDataRedis時往往使用Jedis來操作Redis,但是使用Jedis還是有那么一些不方便,Jedis各種操作Redis的方法參雜在一起沒有一個很好的歸類封裝,當然今天不是來吐槽Jedis,而是來介紹SpringDataRedis的,其實SpringDataRedis底層還是使用Jedis來間接操作Redis,它摒棄了Jedis一些不好的方面,比如它對Jedis客戶端中大量api進行了歸類封裝,將同一類型操作封裝為operation接口;而且連接池由SpringDataRedis自動管理,提供一個高度封裝的“RedisTemplate”類
注意的是,在使用Spring2.x版本之后底層則不在使用Jedis的方式,因為Jedis采用直連方式存在流阻塞(BIO);在后續的Spring版本中都是lettuce方式驅動Redis,因此我們現在的版本中都是lettuce
注:如果對Redis不是太熟悉的請參考官網或我之前寫的 《Redis入門環境搭建》 《Redis命令大全》
1:為何拋棄Jedis轉為Lettuce
Jedis優缺點: Jedis是最老牌的官方推薦的Java客戶端連接,提供了比較全面的Redis命令 優點: Jedis實現了多個接口方法,所以在Jedis對象里可以找到各種方法(方法名和Redis命令基本一樣),比較全面 缺點: 使用阻塞的I/O,其方法調用都是同步的,程序流需要等到 sockets 處理完 I/O 才能執行,不支持異步; 我們創建的Jedis實例不是線程安全的,多個線程操作會導致異常,所有常常通過連接池創建多個Jedis, 每個線程都分配池中不同的Jedis Lettuce優缺點:官網 Lettuce是一種可擴展的線程安全的Redis客戶端,支持異步模式。如果避免阻塞和事務操作,如BLPOP和MULTI/EXEC, 多個線程就可以共享一個連接。lettuce 底層基於Netty,支持高級的Redis特性,比如哨兵,集群,管道,自動重新連接和Redis數據模型。 優點: 支持同步異步通信模式; Lettuce 的 API 是線程安全的,如果不是執行阻塞和事務操作,如BLPOP和MULTI/EXEC,多個線程就可以共享一個連接。
二:快速搭建SpringDataRedis入門
環境版本說明:
IntelliJ IDEA:2020.3.2
Redis Server:6.2.5 ->必須高於2.6+
SpringBoot:2.5.5
我們使用 Spring Initializr 快速構建一個SpringBoot項目,並選擇SpringBoot指定 2.5.5 版本,或者創建完成后去pom.xml文件修改版本;在選擇SpringBoot版本的界面時我們還要去 左側找到 NoSQL 並選擇 Spring Data Redis(Acccess+Driver);創建完成后我們的pom坐標有如下:
<dependencies> <!--SpringDataRedis啟動器依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--SpringBoot測試啟動器依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

@SpringBootTest //SpringBoot測試注解,加上可以在內部注入對象 class SpringDataRedisDemoApplicationTests { //注入RedisTemplate模板對象,我們多數都是操作此對象 @Autowired private RedisTemplate<Object,Object> redisTemplate; @Test void testDemoString() { //創建String類型的K-V操作對象 ValueOperations<Object, Object> valueOperations = redisTemplate.opsForValue(); //添加K-V valueOperations.set("name","zhangsan"); //獲取打印 System.out.println(valueOperations.get("name")); } @Test void testDemoList(){ //創建List類型的操作對象 ListOperations<Object, Object> listOperations = redisTemplate.opsForList(); //和Redis中lpush一樣左添加 listOperations.leftPush("china","AnHui"); listOperations.leftPush("china","ShangHai"); listOperations.leftPush("china","BeiJing"); List<Object> china = listOperations.range("china", 0, -1); //判空並打印全部保存的元素 assert china != null; china.forEach(System.out::println); } }
在導入坐標后則可以很快使用RedisTemplate來連接Redis和操作,但是這都是默認配置(SpringBoot約定大於配置思想)后面在詳細說明具體操作
三:StringDataRedis自動注入
為什么我們在SpringBoot項目上直接使用 @Autowired 注解就可以直接把RedisTemplate對象注入呢?那我們就要知道SpringBoot自動裝配機制,具體參考
四:自定義RedisTemplate模板對象
我們往Redis設置一條數據后,可以去Redis自帶的客戶端執行 keys * 會發現我們創建的key和實際的有點偏差,每個鍵和值的前綴都攜帶一個 "\xac\xed\x00\x05t\x00\x04" 這是因為key和value都被轉義處理了;這主要是序列化方式的問題,為了解決這個問題我們則需要重新創建自己的RedisTemplate對象;
1:默認RedisTemplate對象
默認的SpringDataRedis是會為我們提供兩個對象,分別為RedisTemplate、StringRedisTemplate;從下面代碼可以看出RedisTemplate的泛型為<Object,Object>,StringRedisTemplate泛型為<String,String>;這兩個泛型不滿足我們在日常的開發,我們希望的是<String,Object>,因為這樣我們的鍵可以為字符串,值為任意類型
注:在網絡傳輸中對象需要序列化傳輸
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisOperations.class) //Redis的配置文件類 @EnableConfigurationProperties(RedisProperties.class) //Lettuce和Jedis的連接配置類,不過現在底層默認使用Lettuce連接,若還要使用Jedis則需要手動引入坐標和配置 @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean //不存在當前Bean條件則觸發 //當RedisTemplate對象Bean在IOC容器不存在時則會執行此方法創建對象放到IOC容器 //(若我們自己創建了RedisTemplate的對象Bean時,此方法則不會創建這個Bean對象) @ConditionalOnMissingBean(name = "redisTemplate") //表示當指定Bean在容器中只有一個,或者雖然有多個但是指定首選Bean, @ConditionalOnSingleCandidate(RedisConnectionFactory.class) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { //RedisTemplate<Object, Object> // 鍵和值都是Object類型的,默認序列化方式為:JdkSerializationRedisSerializer // JDK方式的序列化會導致我們的鍵和值轉義,我們解決這種方式可以用JSON方式序列化 RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean //當StringRedisTemplate的Bean對象不存在時則創建此對象放到Bean @ConditionalOnMissingBean @ConditionalOnSingleCandidate(RedisConnectionFactory.class) public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { //StringRedisTemplate // 鍵和值都是String類型的,默認序列化方式為:StringRedisSerializer StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }
2:自定義RedisTemplate對象
我們要創建一個泛型為<String,Object>的RedisTemplate模板對象,針對String鍵的序列化方式為StringRedisSerializer,針對Object值的序列化方式為Jackson2JsonRedisSerializer
<!--Jackson2JsonRedisSerializer用到了JackSon(JSON處理)--> <!--導入JackSon必備的三個坐標--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency>

package cn.xw.config; 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.springframework.boot.SpringBootConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * @Author AnHui_XiaoYang * @Email 939209948@qq.com * @Date 2021/10/23 17:38 * @Description 自定義redis配置類 */ @SpringBootConfiguration public class RedisConfig { /*** * 配置我們自定義的Redis模板,可以對對象序列化存入redis * @param redisConnectionFactory " * @return " */ @Bean(name = "redisTemplate") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { //配置我們自己的Redis模板,一般我們的key都為String,值為Object RedisTemplate<String, Object> template = new RedisTemplate<>(); //設置Redis連接工廠 template.setConnectionFactory(redisConnectionFactory); //配置JSON序列化方式 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); //om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 已過時 om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY); jackson2JsonRedisSerializer.setObjectMapper(om); //配置String序列化方式 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); //注意:下面配置是關於key使用String序列化,而value則使用json(jackson)來序列化,如果key存放對象則會報錯 //key采用String序列化方式 template.setKeySerializer(stringRedisSerializer); //value采用String序列化方式 template.setValueSerializer(jackson2JsonRedisSerializer); //hash的Key采用String序列化方式 template.setHashKeySerializer(stringRedisSerializer); //hash的value采用String序列化方式 template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
3:為什么需要序列化
我們要知道,在網絡中傳輸數據需要序列化傳輸,不管是哪種序列化方式,都有自己獨有的一面
SpringDataRedis的序列化類有下面這幾個:
GenericToStringSerializer:
可以將任何對象泛化為字符串並序列化
Jackson2JsonRedisSerializer:
跟JacksonJsonRedisSerializer實際上是一樣的
JacksonJsonRedisSerializer:
序列化object對象為json字符串
JdkSerializationRedisSerializer:
序列化java對象(被序列化的對象必須實現Serializable接口),無法轉義成對象。
StringRedisSerializer:
簡單的字符串序列化
GenericToStringSerializer:
類似StringRedisSerializer的字符串序列化
GenericJackson2JsonRedisSerializer:
類似Jackson2JsonRedisSerializer,但使用時構造函數不用特定的類參考以上序列化,自定義序列化類。
上面說到,我們默認的RedisTemplate<Object,Object>對象使用的是 JdkSerializationRedisSerializer,這就告知我們傳入的鍵或者值必須實現Serializable,比如我們傳入的String鍵或者值,String它底層也是實現了此接口,所以說我們自己創建的對象用來當鍵/值的話必須序列化,否則會出現問題
在保存未序列化的對象會出現:
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception
is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize
object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer
requires a Serializable payload but received an object of type [cn.xw.Student] at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:96) at org.springframework.data.redis.core.AbstractOperations.rawValue(AbstractOperations.java:127) at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:235)
@Test void testDemo() throws JsonProcessingException { ValueOperations<Object, Object> valueOperations = redisTemplate.opsForValue(); //把沒實例化的實體類轉為JSON字符串對象,前提被轉的對象有get、set方法 ObjectMapper objectMapper = new ObjectMapper(); String stu = objectMapper.writeValueAsString(new Student("zhangsan", 22)); //轉為JSON字符串,我們知道String的JSON字符串底層是有序列化的,所以保存成功 valueOperations.set("student",stu); }
⭐⭐注:下面的文章中在存儲值為對象時則會使用自定義的RedisTemplate對象,存儲鍵值為字符串時則使用框架自帶的StringRedisTemplate對象,不在使用自帶的JDK序列化方式的RedisTemplate對象,JDK序列化的對於我們當前來說用不着,還要對每個對象序列化(不是說默認的不好,每種序列方式各有優點)
五:SpringDataRedis配置屬性
約定大於配置,這是SpringBoot提出來的;就是說我們在不配置的情況下使用默認屬性值運行;其實SpringDataRedis也有自己一套的配置,我們可以修改默認的配置來達到我們的需求,下面就介紹一些基本的屬性

常用配置: spring.redis.host=localhost 連接Redis的IP spring.redis.port=6379 連接的Redis的端口 spring.redis.database=0 連接工廠使用的數據庫索引,Redis默認是有16個數據庫的(0~15) spring.redis.connect-timeout 連接超時 spring.redis.timeout 操作讀取超時時間(連接上Redis后的一系列操作的超時時間) spring.redis.username redis服務器登錄用戶名 spring.redis.password redis服務器登錄密碼 spring.redis.url 連接Redis的URL,如:redis://user:password@example.com:6379 spring.redis.ssl=false 是否開啟 SSL 支持 spring.redis.client-name 要在與 CLIENT SETNAME 的連接上設置的客戶端名稱 spring.redis.client-type 要使用的客戶端類型。 默認根據classpath自動檢測 關於Jedis配置: spring.redis.jedis.pool.max-active=8 連接池最大連接數(使用負值表示沒有限制) spring.redis.jedis.pool.max-wait=-1ms 連接池最大阻塞等待時間(使用負值表示沒有限制) spring.redis.jedis.pool.max-idle=8 連接池中的最大空閑連接 spring.redis.jedis.pool.min-idle=0 連接池中的最小空閑連接 spring.redis.jedis.pool.time-between-eviction-runs “空閑鏈接”檢測線程,檢測的周期,毫秒數。如果為負值,表示不運行“檢測線程” 關於Lettuce配置: spring.redis.lettuce.pool.max-active=8 連接池最大連接數(使用負值表示沒有限制) spring.redis.lettuce.pool.max-wait=-1ms 連接池最大阻塞等待時間(使用負值表示沒有限制) spring.redis.lettuce.pool.max-idle=8 連接池中的最大空閑連接 spring.redis.lettuce.pool.min-idle=0 連接池中的最小空閑連接 spring.redis.lettuce.shutdown-timeout=100ms 關機超時,關閉客戶端連接之前等待任務處理完成的最長時間,在這之后,無論任務是否執行完成,都會被執行器關閉 spring.redis.lettuce.pool.time-between-eviction-runs “空閑鏈接”檢測線程,檢測的周期,毫秒數。如果為負值,表示不運行“檢測線程” 這個設置是,每隔多少毫秒,空閑線程驅逐器會去關閉多余的空閑連接,且保持最少空閑連接可用,這個值最好設置大一點,否者影響性能。 同時我們spring.redis.lettuce.pool.min-idle=0 中的值要大於0,否則當前都為空余則一下子全關了,池子就沒連接了 lettuce連接池屬性timeBetweenEvictionRunsMillis如果不設置默認是-1,當該屬性值為負值時, lettuce連接池要維護的最小空閑連接數的目標minIdle就不會生效(就是說設置空閑線程驅逐器而不設置最小空閑連接則最小空閑連接不生效) 關於哨兵配置: spring.redis.sentinel.master Redis服務器的名稱 spring.redis.sentinel.nodes 逗號分隔的“主機:端口”對列表 spring.redis.sentinel.password 哨兵認證的密碼 關於cache緩存配置: spring.cache.redis.cache-null-values=true 是否允許緩存空值 spring.cache.redis.enable-statistics=false 是否開啟緩存統計 spring.cache.redis.use-key-prefix=true 寫入Redis時是否使用key前綴 spring.cache.redis.key-prefix 鍵前綴 spring.cache.redis.time-to-live 條目過期。 默認情況下,條目永不過期 后續介紹:關於集群 spring.redis.lettuce.cluster.refresh.adaptive=faLse spring.redis.lettuce.cluster.refresh.dynamic-refresh-sources=true spring.redis.lettuce.cluster.refresh.period spring.redis.cluster.max-redirects 跨集群執行命令時要遵循的最大重定向數 spring.redis.cLuster.nodes 以逗號分隔的“host:port”對列表,用於引導 關於Session配置: spring.session.redis.cleanup-cron=0 * * * * * 過期會話清理的 Cron 表達式 spring.session.redis.flush-mode=on-save 會話刷新模式 spring.session.redis.namespace=spring:session 用於存儲會話的鍵的命名空間 spring.session.redis.save-mode=on-set-attribute 會話保存模式 spring.session.redis.configure-action=notify-keyspace-events 當不存在用戶定義的 ConfigureRedisAction bean 時應用的配置操作 其它: spring.data.redis.repositories.enabled=true 是否啟用Redis存儲庫
spring: redis: host: localhost # 連接Redis的IP port: 6379 # 連接的Redis的端口 connect-timeout: 3000 # 連接超時 timeout: 3000 # 連接上Redis后的一系列操作的超時時間 database: 0 # 連接工廠使用的數據庫索引,Redis默認是有16個數據庫的(0~15)
六:RedisTemplate常用方法
我們在多說情況下會使用RedisTemplate及其對應包,該模塊為Redis交互提供了高級抽象、各種操作視圖和豐富的通用接口,用於針對特定的類型或鍵做不同的操作;比如操作Redis中String類型的則需要通過opsForValue()方法獲取操作對象
注:我們注入的RedisTemplate實例是線程安全的,可以在多個線程實例下重復使用
鍵類型操作:重點說明!
ValueOperations Redis 字符串操作(字符串String類型) HashOperations Redis hash操作(散列hash類型) ListOperations Redis list操作(列表list類型) SetOperations Redis set操作(集合Set類型) ZSetOperations Redis zset操作(有序集合sorted類型) GeoOperations Redis 地理空間操作(地理空間geospatial類型) HyperLogLogOperations Redis HyperLogLog操作(超長日志hyperloglog類型)
鍵綁定操作:
BoundKeyOperations Redis 鍵綁定操作 BoundValueOperations Redis綁定鍵操作 字符串操作(字符串String類型) BoundHashOperations Redis綁定鍵操作 hash操作(散列hash類型) BoundListOperations Redis綁定鍵操作 list操作(列表list類型) BoundSetOperations Redis綁定鍵操作 set操作(集合Set類型) BoundZSetOperations Redis綁定鍵操作 zset操作(有序集合sorted類型) BoundGeoOperations Redis綁定鍵操作 地理空間操作(地理空間geospatial類型)
鍵類型操作:創建后我們可以在此對象內部設置或者更新各種鍵值
鍵綁定操作:創建時我們是綁定一個key(鍵),它內部的方法都是圍繞當前創建的key來操作
# 創建ValueOperations並獲取key為“student”的值
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
valueOperations.get("student");
# 創建BoundValueOperations並獲取key為“student”的值
BoundValueOperations<String, Object> BoundValueOperations = redisTemplate.boundValueOps("student");
BoundValueOperations.get();
1:redisTemplate之Key操作
其實引入redisTemplate后可以直接簡單操作Redis數據庫中的key,當然也可以操作事務等一系列操作,但在這我只簡單說說它的key操作,后續文章中會為大家列出

基本方法: 方法:Set<K> keys(K pattern) # 獲取Redis數據庫中指定篩選的鍵 pattern刪選條件 * 代表全部 方法:Boolean hasKey(K key) # 查詢給定的鍵是否存在 方法:Long countExistingKeys(Collection<K> keys) # 返回給定的key集合在Redis中存在的個數 方法:K randomKey() # 從Redis數據庫中隨機返回一個鍵 方法:DataType type(K key) # 返回在Redis存儲的key類型 方法:Boolean delete(K key) # 在Redis數據庫中刪除指定的鍵 方法:Boolean unlink(K key) 方法:Long unlink(Collection<K> keys) # 從Redis數據庫中取消鍵的鏈接 方法:Boolean move(K key, final int dbIndex) # 將給定的鍵移動到指定索引的數據庫下(注意是移動) 方法:rename(K oldKey, K newKey) # key名稱重命名 注:若更改的新key在庫中已存在,則會導致那個新key里的值覆蓋 方法:Boolean renameIfAbsent(K oldKey, K newKey) # 僅當newKey不存在時,才將oleName重命名為newKey 方法:List<RedisClientInfo> getClientList() # 獲取當前Redis服務所有連接它的客戶端信息 方法:killClient(final String host, final int port) # 關閉指定的客戶端,不知道ip和端口可以執行getClientList()獲取,addr屬性就有 設置時間: 方法:Boolean expire(K key, long timeout, TimeUnit unit); 方法:Boolean expire(K key, Duration timeout) 方法:Boolean expireAt(K key, final Date date) 方法:Boolean expireAt(K key, Instant expireAt) # 設置key的過期時間 方法:Long getExpire(K key) 返回秒 方法:Long getExpire(K key, TimeUnit timeUnit) 指定返回方式 # 獲取key的當前過期時間 補充: TimeUnit.NANOSECONDS 納秒 TimeUnit.MICROSECONDS 微秒 TimeUnit.MILLISECONDS 毫秒 TimeUnit.SECONDS 秒 TimeUnit.MINUTES 分鍾 TimeUnit.HOURS 小時 TimeUnit.DAYS 天 Duration.ofNanos(6000000L) 設置6000000納秒 Duration.ofMillis(6000L) 設置6000毫秒 Duration.ofSeconds(60L) 設置60秒 Duration.ofMinutes(5L) 設置5分鍾 Duration.ofHours(2L) 設置2小時 Duration.ofDays(1L) 設置1天 Duration.ofSeconds(60L,80000000L) 設置60秒,並且再往上調整80000000納秒;合計60080000000納秒=60.08秒 Duration.between(Temporal startInclusive, Temporal endExclusive) 設置時間范圍的起始和終止中間的值時間秒 方法:List<V> sort(SortQuery<K> query); 方法:Long sort(SortQuery<K> query, K storeKey); 方法:<T> List<T> sort(SortQuery<K> query, BulkMapper<T, V> bulkMapper); 方法:<T> List<T> sort(SortQuery<K> query, RedisSerializer<T> resultSerializer); 方法:<T, S> List<T> sort(SortQuery<K> query, BulkMapper<T, S> bulkMapper, RedisSerializer<S> resultSerializer); # 排序
//注:因為我們操作的K-V都是String類型,所以使用系統自帶的StringRedisTemplate模板對象
@Autowired
private StringRedisTemplate redisTemplate;
@Test void redisTemplateBase() { //獲取Redis數據庫中指定篩選的鍵 pattern刪選條件 * 代表全部 Set<String> keys = redisTemplate.keys("*"); //查詢給定的鍵是否存在 Boolean studentHasBoo = redisTemplate.hasKey("student"); //返回給定的key集合在Redis中存在的個數 Long aLong = redisTemplate.countExistingKeys(Arrays.asList("name", "age", "address")); //從Redis數據庫中隨機返回一個鍵 String randomKey = redisTemplate.randomKey(); //返回在Redis存儲的key類型 DataType dataType = redisTemplate.type("student"); //在Redis數據庫中刪除指定的鍵 Boolean studentDelBoo = redisTemplate.delete("student"); //從Redis數據庫中取消鍵的鏈接 Long unlink = redisTemplate.unlink(Arrays.asList("name", "age", "address")); //將給定的鍵移動到指定索引的數據庫下(注意是移動) Boolean salary = redisTemplate.move("salary", 1); //key名稱重命名 注:若更改的新key在庫中已存在,則會導致那個新key里的值覆蓋 redisTemplate.rename("sex", "mySex"); //僅當newKey不存在時,才將oleName重命名為newKey Boolean aBoolean = redisTemplate.renameIfAbsent("weight", "myWeight"); //獲取當前Redis服務所有連接它的客戶端信息 List<RedisClientInfo> clientList = redisTemplate.getClientList(); //關閉指定的客戶端,不知道ip和端口可以執行getClientList()獲取,addr屬性就有 redisTemplate.killClient("127.0.0.1",51019); //設置key的過期時間 //設置name過期時間60000毫秒=60秒 redisTemplate.expire("name", 60000L, TimeUnit.MILLISECONDS); redisTemplate.expire("sex", Duration.ofSeconds(60L)); Long name = redisTemplate.getExpire("name"); }
RedisTemplate之Sort方法使用:
方法:List<V> sort(SortQuery<K> query); 方法:Long sort(SortQuery<K> query, K storeKey); 方法:<T> List<T> sort(SortQuery<K> query, BulkMapper<T, V> bulkMapper); 方法:<T> List<T> sort(SortQuery<K> query, RedisSerializer<T> resultSerializer); 方法:<T, S> List<T> sort(SortQuery<K> query, BulkMapper<T, S> bulkMapper, RedisSerializer<S> resultSerializer);
此命令是用來對list,set或sorted中元素排序,具體參考可以去參考我之前寫的Redis命令詳解

//基本測試數據導入 //lpush listNumber 8.4 13 14 10.5 4 19.6 10 14 5.2 10 3 2.5 7 4.7 10 11.2 8 2.2 15.7 20.9 //lpush listString remini Momen Pledg Memo Tende Biode Revie silen Romanti AusL Simpl Promis Romanti Bautifu smil Initiall sunse lemo firs Chaffere /*參數介紹 查詢條件: SortQuery 使用SortQueryBuilder對象builder()方法創建 SortQuery 參數: by() 和Redis命令中by一樣,通過引用外部key來排序 get() 獲取外部key的值 noSort() 不使用by(),就是不通過引用外部key來排序;注by()和noSort()只能存在一個 limit() 分頁和mysql寫法一樣 order() 默認asc從小到大 desc從大到小 SortParameters.Order.DESC SortParameters.Order.ASC alphabetical(true) 當排序的集合中存在字符串則需要使用此屬性 build() 構建出對象 */ //基本的Sort使用 //List<V> sort(SortQuery<K> query); @Test void redisTemplateSortA() { //對應Redis命令:sort listString limit 0 3 desc alpha //具體為啥查詢出來和在Redis查詢出來的不一樣,也沒有細了解 SortQuery<String> sortQuery = SortQueryBuilder.sort("listString") .noSort() .limit(0, 3) .order(SortParameters.Order.DESC) .alphabetical(true) .build(); List<String> sort = stringRedisTemplate.sort(sortQuery); sort.forEach(System.out::println); } //通過外部key來排序 @Test void redisTemplateSortB() { //測試數據 //lpush mylist 20 15 18 //set n_20 b //set n_15 a //set n_18 c SortQuery<String> sortQuery = SortQueryBuilder.sort("mylist") .by("n_*") .order(SortParameters.Order.DESC) .alphabetical(true) .build(); List<String> sort = stringRedisTemplate.sort(sortQuery); sort.forEach(System.out::println); }
2:ValueOperations之String類型操作

基本方法: 方法:void set(K key, V value); 方法:void set(K key, V value, long offset); # 對應命令:setrange key offset value 方法:void set(K key, V value, Duration timeout) 方法:void set(K key, V value, long timeout, TimeUnit unit); # 對應命令:psetex key milliseconds value # 設置K-V鍵值的String類型 # offset:偏移量 timeout:失效時間 unit:失效時間類型 # 時間取值參考“redisTemplate之Key操作” 方法:Boolean setIfAbsent(K key, V value); 方法:Boolean setIfAbsent(K key, V value, Duration timeout) 方法:Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit); # 設置鍵值,若當前鍵存在則不在更新覆蓋 方法:Boolean setIfPresent(K key, V value); 方法:Boolean setIfPresent(K key, V value, Duration timeout) 方法:Boolean setIfPresent(K key, V value, long timeout, TimeUnit unit); # 設置鍵值,若當前鍵存在則更新覆蓋,key原本不存在則返回false 方法:V get(Object key); 方法:String get(K key, long start, long end); # 對應命令:getrange key start end # 獲取指定的鍵,start和end是獲取值里的指定部分 方法:Integer append(K key, String value); # 對指定的key后面追加指定的值 方法:Long increment(K key); 方法:Long increment(K key, long delta); 方法:Double increment(K key, double delta); # 將key中儲存的數字值增加1,或增加指定值 方法:Long decrement(K key); 方法:Long decrement(K key, long delta); # 將key中儲存的數字值減1,或減指定值 方法:V getAndSet(K key, V value); # 設置更新key值,設置前先把原有的值返回出來,並設置新的值 方法:Long size(K key); # 對應命令:strlen key # 獲取指定key所儲存的字符串值的長度 方法:void multiSet(Map<? extends K, ? extends V> map); # 批量設置鍵值 方法:List<V> multiGet(Collection<K> keys); # 批量根據鍵獲取值 方法:RedisOperations<K, V> getOperations(); # 其它公共方法,就和RedisTemplate下的公共方法差不多 注:下面的位圖方法操作放在后面介紹 方法:Boolean setBit(K key, long offset, boolean value); 方法:Boolean getBit(K key, long offset); 方法:List<Long> bitField(K key, BitFieldSubCommands subCommands);
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { //獲取基本的 ValueOperations 操作對象(操作Redis的String類型) ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue(); //設置一個普通的鍵值 命令:set message "Hello World" opsForValue.set("message", "Hello World"); //設置一個普通的鍵值,存在偏移的設置 opsForValue.set("messages", "Redis", 6); //設置一個普通鍵值,並設置過期時間60秒 opsForValue.set("name", "zhangsan", Duration.ofSeconds(6000)); //獲取key為“name”的值 opsForValue.get("name"); //獲取key為“message”的值,並指定偏移,打印為 "llo Wor" opsForValue.get("message", 2, 8); //在key為“message”的值后面追加 " Good" 最終的message為"Hello World Good" opsForValue.append("message", " Good"); opsForValue.set("age", "22"); opsForValue.set("salary", "1000.3"); //將key中儲存的數字值增加 1 或者后面的增加指定值 Long age1 = opsForValue.increment("age"); Long age2 = opsForValue.increment("age", 2L); Double salary = opsForValue.increment("salary", 3.4D); //將key中儲存的數字值減 1 或減去指定值 Long age3 = opsForValue.decrement("age"); Long age4 = opsForValue.decrement("age", 2L); //先獲取age值,並把age值更改為23 String age = opsForValue.getAndSet("age", "23"); //獲取指定key所儲存的字符串值的長度 此時獲取為16 Long message = opsForValue.size("message"); //setIfAbsent只能新增操作,setIfPresent只能更新操作 Boolean aBoolean1 = opsForValue.setIfAbsent("address", "anhui"); Boolean aBoolean2 = opsForValue.setIfPresent("address", "anhui"); Map<String,String> maps = new HashMap<>(); maps.put("keyA","valueA");maps.put("keyB","valueB");maps.put("keyC","valueC"); //批量設置String的鍵值 opsForValue.multiSet(maps); //批量獲取值 List<String> strings = opsForValue.multiGet(Arrays.asList("keyA", "keyB", "keyC")); //其它公共方法操作,就和RedisTemplate下的公共方法差不多 RedisOperations<String, String> operations = opsForValue.getOperations(); }
3:HashOperations之Hash類型操作

基本方法: 方法:void put(H key, HK hashKey, HV value); # 往Hash表中插入K-V 方法:void putAll(H key, Map<? extends HK, ? extends HV> m); # 往Hash表中批量插入K-V 方法:HV get(H key, Object hashKey); # 獲取Hash表中指定key 方法:List<HV> multiGet(H key, Collection<HK> hashKeys); # 批量獲取Hash表中的key值 方法:Long lengthOfValue(H key, HK hashKey); # 獲取Hash表中其中一個key里的值的字符長度 方法:Boolean putIfAbsent(H key, HK hashKey, HV value); # 往Hash表中插入K-V(前提當前hash表中的key不存在,存在返回false,不做操作) 方法:Boolean hasKey(H key, Object hashKey); # 判斷Hash表中是否包含此key 方法:Long size(H key); # 獲取Hash表中的key數量 方法:Set<HK> keys(H key); 方法:List<HV> values(H key); # 獲取Hash表中全部key和全部value 方法:Long increment(H key, HK hashKey, long delta); 方法:Double increment(H key, HK hashKey, double delta); # 對Hash表中指定key的值(整數、浮點數)的累加和相減(負數代表減) 方法:Cursor<Map.Entry<HK, HV>> scan(H key, ScanOptions options); # 迭代Hash表數據 方法:Long delete(H key, Object... hashKeys); # 刪除指定Hash表中的key
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { //獲取基本的 HashOperations 操作對象(操作Redis的Hash類型) HashOperations<String, Object, Object> opsForHash = stringRedisTemplate.opsForHash(); //往Student的Hash表中插入一個name為“zhangsan” opsForHash.put("Student", "name", "zhangsan"); //批量往Student的Hash表中插入age、salary Map<String, String> map = new HashMap<>(); map.put("age", "22"); map.put("salary", "3000.88"); opsForHash.putAll("Student", map); //獲取Student的Hash中key為name的值 Object name = opsForHash.get("Student", "name"); //往Student的Hash表中插入name為list的值,注:若name的key存在則無法插入,也無法更新,用於插入不存在的key Boolean aBoolean = opsForHash.putIfAbsent("Student", "name", "lisi"); //獲取當前Student的HAsh表中的key數量 Long stuSize = opsForHash.size("Student"); //獲取Student的Hash表中的全部key和value Set<Object> stuKeys = opsForHash.keys("Student"); List<Object> stuValue = opsForHash.values("Student"); //獲取Student的Hash中key為name的字符長度 Long aLong = opsForHash.lengthOfValue("Student", "name"); //獲取Student的Hash中是否存在key為name的數據 Boolean aBoolean1 = opsForHash.hasKey("Student", "name"); //為Hash中某個key(整數、小數)累加指定的值;為負數就相減 Long increment = opsForHash.increment("Student", "age", -30L); Double increment1 = opsForHash.increment("Student", "salary", -5000D); //批量獲取Hash表中指定多個key的值 List<Object> objects = opsForHash.multiGet("Student", Arrays.asList("name", "age")); //獲取基本操作key對象 RedisOperations<String, ?> operations = opsForHash.getOperations(); //刪除Hash表中指定key Long delName = opsForHash.delete("Student", "name"); //迭代,具體參考hcsan //測試數據:hmset testHash k1 v1 k2 v2 k3 v3 k4 v4 k5 v5 k6 v6 k7 v7 ScanOptions build = ScanOptions.scanOptions() .count(1) .match("k*") //過濾符合指定的key *代表以 k 開頭的鍵 .build(); //ScanOptions.NONE迭代全部 Cursor<Map.Entry<Object, Object>> kv = opsForHash.scan("testHash", ScanOptions.NONE); System.out.println(kv.getCursorId());//獲取光標ID System.out.println(kv.getPosition());//獲取當前迭代的位置 System.out.println(kv.isClosed()); //是否已關閉 while (kv.hasNext()) { Map.Entry<Object, Object> next = kv.next(); System.out.println(next.getKey() + ":" + next.getValue()); } }
4:ListOperations之List類型操作

基本方法: 方法:Long leftPush(K key, V value); # 往集合頭部添加元素 方法:Long leftPushAll(K key, V... values); # 往集合頭部批量添加元素 方法:Long leftPushAll(K key, Collection<V> values); # 往集合頭部批量添加元素,接收集合對象 方法:Long leftPush(K key, V pivot, V value); # 在集合指定元素pivot前插入value元素左往右 方法:Long leftPushIfPresent(K key, V value); # 在指定集合內部添加元素(保證當前元素存在) 方法:V leftPop(K key); # 從集合頭部彈出一個元素 方法:V leftPop(K key, long timeout, TimeUnit unit); 方法:V leftPop(K key, Duration timeout) # 從集合頭部彈出一個元素(阻塞) ## 注:還有尾插元素,以上面方法為主,把left換為right及是尾插法 方法:V rightPopAndLeftPush(K sourceKey, K destinationKey); 方法:V rightPopAndLeftPush(K sourceKey, K destinationKey, Duration timeout) 方法:V rightPopAndLeftPush(K sourceKey, K destinationKey, long timeout, TimeUnit unit) # 從sourceKey集合尾部彈出一個元素插入到destinationKey集合頭部(后面參數為阻塞參數) 方法:List<V> range(K key, long start, long end); # 獲取集合中指定范圍的元素 方法:V index(K key, long index); # 獲取集合中指定下標元素的值 方法:Long indexOf(K key, V value); # 通過元素值查找當期位置坐標(從左往右) 方法:Long lastIndexOf(K key, V value); # 通過元素值查找當期位置坐標(從右往左) 方法:Long size(K key); # 獲取集合的全部元素大小 方法:void set(K key, long index, V value); # 更新指定下標的元素值 方法:void trim(K key, long start, long end); # 對集合中指定范圍的元素截取(並會保存截取后的元素) 方法:Long remove(K key, long count, Object value); # 從集合key中刪除前count個值等於value的元素 ,返回刪除的元素個數
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { //獲取基本的 HashOperations 操作對象(操作Redis的Hash類型) ListOperations<String, String> opsForList = stringRedisTemplate.opsForList(); //在names集合頭部插入zhangsan;返回插入成功后集合里的元素總數 Long aLong = opsForList.leftPush("names", "zhangsan"); //在names集合頭部批量插入lisi、mazi;返回插入成功后集合里的元素總數 Long aLong1 = opsForList.leftPushAll("names", "lisi", "mazi"); //在names集合頭部批量插入一個集合對象wangwu、wangwu;返回插入成功后集合里的元素總數 Long aLong2 = opsForList.leftPushAll("names", Arrays.asList("wangwu", "xiehao")); //在names集合中的lisi元素前面插入 WeiHua 元素 Long aLong3 = opsForList.leftPush("names", "lisi", "WeiHua"); //在names集合上添加“mazi”元素,返回元素個數,0代表未添加(若集合names不存在則無法插入) Long aLong4 = opsForList.leftPushIfPresent("names", "mazi"); //移除names集合中的頭部第一個元素 String s = opsForList.leftPop("names"); //移除names集合中的頭部第一個元素,延遲阻塞 參考命令blpop key [key ...] timeout String s1 = opsForList.leftPop("names", 1000L, TimeUnit.SECONDS); //移除names集合中的頭部第一個元素,延遲阻塞 參考命令blpop key [key ...] timeout String s2 = opsForList.leftPop("names", Duration.ofSeconds(30)); //### 還有尾插元素,以上面方法為主,把left換為right及是尾插法 //案例示例命令: LPUSH testNames xulingyue wangyanke xiaogege //從testNames集合尾部彈出一個元素插入到names集合頭部,返回添加的元素;;后兩個為阻塞版 String s3 = opsForList.rightPopAndLeftPush("testNames", "names"); String s4 = opsForList.rightPopAndLeftPush("testNames", "names", Duration.ofSeconds(60L)); String s5 = opsForList.rightPopAndLeftPush("testNames", "names", 60L,TimeUnit.SECONDS); //查詢集合里全部元素 List<String> names = opsForList.range("names", 0L, -1L); //獲取names集合里坐標為3的元素 0開始 String names1 = opsForList.index("names", 3L); //獲取names集合里指定元素的坐標位置,頭部查詢 Long aLong5 = opsForList.indexOf("names", "mazi"); //獲取names集合里指定元素的坐標位置,尾部查詢 Long aLong6 = opsForList.lastIndexOf("names", "mazi"); //獲取集合names內的元素數量 Long sizes = opsForList.size("names"); //修改集合names下坐標為3的元素為”aha“ opsForList.set("names", 3L, "aha"); //截取集合指定范圍的元素 opsForList.trim("names",2L,4L); //從集合key中刪除前count個值等於element的元素 ,返回刪除的元素個數 Long remove = opsForList.remove("names", 2L, "aha"); }
5:SetOperations之Set類型操作

基本方法: 方法:Long add(K key, V... values); # 往集合中添加一個或多個元素 ## 差異方法 方法:Set<V> difference(K key, K otherKey); 方法:Set<V> difference(Collection<K> keys); 方法:Set<V> difference(K key, Collection<K> otherKeys); 方法:Long differenceAndStore(K key, K otherKey, K destKey); 方法:Long differenceAndStore(Collection<K> keys, K destKey); 方法:Long differenceAndStore(K key, Collection<K> otherKeys, K destKey); # 返回第一個集合與其它集合之間的差異;說白就是第一個集合的某個元素在其它集合都不存在則這個元素會被返回, 方法:Set<V> intersect(K key, K otherKey); 方法:Set<V> intersect(Collection<K> keys); 方法:Set<V> intersect(K key, Collection<K> otherKeys); 方法:Long intersectAndStore(K key, K otherKey, K destKey); 方法:Long intersectAndStore(Collection<K> keys, K destKey); 方法:Long intersectAndStore(K key, Collection<K> otherKeys, K destKey); # 返回第一個集合與其它集合之間的交集;說白就是第一個集合的某個元素在其它集合都存在則這個元素會被返回,(交集) 方法:Set<V> union(K key, K otherKey); 方法:Set<V> union(Collection<K> keys); 方法:Set<V> union(K key, Collection<K> otherKeys); 方法:Long unionAndStore(K key, K otherKey, K destKey); 方法:Long unionAndStore(Collection<K> keys, K destKey); 方法:Long unionAndStore(K key, Collection<K> otherKeys, K destKey); # 用於返回所有給定集合的並集 方法:Long size(K key); # 返回集合中的元素數量 方法:Set<V> members(K key); # 返回集合中全部元素 方法:Boolean isMember(K key, Object o); # 返回集合中是否存在此元素 方法:V randomMember(K key); # 隨機返回集合中一個元素 方法:List<V> randomMembers(K key, long count); # 隨機返回集合中多個元素(可能重復) 方法:Set<V> distinctRandomMembers(K key, long count); # 隨機返回集合中多個元素(不重復) 方法:Boolean move(K key, V value, K destKey); # 將一個集合中的某個元素彈出放到另一個集合中 方法:Cursor<V> scan(K key, ScanOptions options); # 遍歷集合 方法:RedisOperations<K, V> getOperations(); # 返回基本操作對象 方法:Long remove(K key, Object... values); # 從集合中彈出指定的多個元素 方法:V pop(K key); # 隨機從集合中彈出一個元素刪除 方法:List<V> pop(K key, long count); # 隨機從集合中彈出多個元素刪除
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { //獲取基本的 SetOperations 操作對象(操作Redis的Set類型) SetOperations<String, String> opsForSet = stringRedisTemplate.opsForSet(); //往ChinaA的Set集合中添加 北京、上海、安徽、河南、廣東、江蘇;並返回添加成功的個數(已存在的不會被添加);ChinaB一樣 Long add1 = opsForSet.add("ChinaA", "Beijing", "Shanghai", "Anhui", "Henan", "Guangdong", "Jiangsu"); Long add2 = opsForSet.add("ChinaB", "Shanxi", "Shanghai", "Fujian", "Anhui", "Zhejiang"); //=================================================================== //差異比較 //=====返回第一個集合與其它集合之間的差異;說白就是第一個集合的某個元素在其它集合都不存在則這個元素會被返回, // key1 = {a,b,c,d} // key2 = {c} // key3 = {a,c,e} // SDIFF key1 key2 key3 = {b,d} //第一個集合和其它集合比較差異,並返回有差異的元素 Set<String> difference1 = opsForSet.difference("ChinaA", "ChinaB"); Set<String> difference2 = opsForSet.difference("ChinaA", Arrays.asList("ChinaB")); Set<String> difference3 = opsForSet.difference(Arrays.asList("ChinaA", "ChinaB")); //第一個集合和其它集合比較差異,並把差異的元素存放到指定的集合中(注:存放差異的集合會把原來的數據覆蓋) Long aLong1 = opsForSet.differenceAndStore("ChinaA", "ChinaB", "diffStoreA"); Long aLong2 = opsForSet.differenceAndStore("ChinaA", Arrays.asList("ChinaB"), "diffStoreB"); Long aLong3 = opsForSet.differenceAndStore(Arrays.asList("ChinaA", "ChinaB"), "diffStoreC"); //=====返回第一個集合與其它集合之間的交集;說白就是第一個集合的某個元素在其它集合都存在則這個元素會被返回, // key1 = {a,b,c,d} // key2 = {c} // key3 = {a,c,e} // SINTER key1 key2 key3 = {c} // 使用上和上面6個方法一樣,只不過這個是獲取它的 交集 Set<String> intersect1 = opsForSet.intersect("ChinaA", "ChinaB"); Set<String> intersect2 = opsForSet.intersect("ChinaA", Arrays.asList("ChinaB")); Set<String> intersect3 = opsForSet.intersect(Arrays.asList("ChinaA", "ChinaB")); Long iLong1 = opsForSet.intersectAndStore("ChinaA", "ChinaB", "interStoreA"); Long iLong2 = opsForSet.intersectAndStore("ChinaA", Arrays.asList("ChinaB"), "interStoreB"); Long iLong3 = opsForSet.intersectAndStore(Arrays.asList("ChinaA", "ChinaB"), "interStoreC"); //=====用於返回所有給定集合的並集 // key1 = {a,b,c,d} // key2 = {c} // key3 = {a,c,e} // sunion key1 key2 key3 = {a,b,c,d,e} // 使用上和上面12個方法一樣,只不過這個是獲取它的 並集 Set<String> union1 = opsForSet.union("ChinaA", "ChinaB"); Set<String> union2 = opsForSet.union("ChinaA", Arrays.asList("ChinaB")); Set<String> union3 = opsForSet.union(Arrays.asList("ChinaA", "ChinaB")); Long uLong1 = opsForSet.unionAndStore("ChinaA", "ChinaB", "unionStoreA"); Long uLong2 = opsForSet.unionAndStore("ChinaA", Arrays.asList("ChinaB"), "unionStoreB"); Long uLong3 = opsForSet.unionAndStore(Arrays.asList("ChinaA", "ChinaB"), "unionStoreC"); //=================================================================== //返回集合中全部元素個數 Long size = opsForSet.size("ChinaA"); //返回集合中全部元素的set集合 Set<String> chinaA = opsForSet.members("ChinaA"); //判斷集合中是否存在“Shanghai”這個元素 Boolean member = opsForSet.isMember("ChinaA", "Shanghai"); //隨機返回集合中一個或多個元素(注意返回多個元素可能會存在重復的) String s = opsForSet.randomMember("ChinaA"); List<String> strings = opsForSet.randomMembers("ChinaA", 3); //隨機返回集合中一個或多個元素(注意返回多個元素不會存在重復的) Set<String> chinaA2 = opsForSet.distinctRandomMembers("ChinaA", 3L); //將ChinaA集合里的Beijing彈出放到ChinaB集合中(若A集合彈出元素在B集合存在,那么A集合元素也將被清理) Boolean move = opsForSet.move("ChinaA", "Shanghai", "ChinaB"); //帶條件的獲取指定元素 ScanOptions scanOptions = ScanOptions.scanOptions().match("*e*").count(2L).build(); Cursor<String> chinaA1 = opsForSet.scan("ChinaA", ScanOptions.NONE); while (chinaA1.hasNext()){ System.out.println(chinaA1.next()); } //獲取RedisTemplate基本操作方法對象 RedisOperations<String, String> operations = opsForSet.getOperations(); //雙重ChinaA集合中指定的元素,返回刪除成功的元素個數 Long remove = opsForSet.remove("ChinaA", "Guangdong", "Beijing", "LboLa"); //從集合ChinaA中隨機彈出(刪除)一個或者多個元素,並返回被彈出的元素名稱 String pop1 = opsForSet.pop("ChinaA"); List<String> pop2 = opsForSet.pop("ChinaA", 2L); }
6:ZSetOperations之SortedSet類型操作
關於SortedSet里一些不是好理解的命令可以參考官方文檔或者參考我之前寫的 Redis命令大全 里的有序集合

基本方法: 方法:Boolean add(K key, V value, double score); # 為有序集合中添加一個元素和分數 方法:Long add(K key, Set<TypedTuple<V>> tuples); # 為有序集合中批量添加一個元素和分數 方法:Boolean addIfAbsent(K key, V value, double score); # 為有序集合中添加一個元素和分數(存在此元素則不執行當前添加) 方法:Long addIfAbsent(K key, Set<TypedTuple<V>> tuples); # 為有序集合中批量添加一個元素和分數(存在此元素則不執行當前添加) 方法:Double incrementScore(K key, V value, double delta); # 為有序集合中的莫個元素分數累計相應的值 方法:Long count(K key, double min, double max); # 計算有序集合中元素個數(按照分數篩選計算) 方法:Long lexCount(K key, Range range); # 計算有序集合中元素個數(按照元素篩選計算) 方法:Long rank(K key, Object o); # 正向(從小到大順序)獲取某個元素在當前有序集合的位置 方法:Long reverseRank(K key, Object o); # 反向向(從大到小順序)獲取某個元素在當前有序集合的位置 方法:Long size(K key); 方法:Long zCard(K key); # 上面兩個方法都是計算當前有序集合的全部元素個數 方法:Double score(K key, Object o); # 獲取有序集合中某個元素的分數 方法:Cursor<TypedTuple<V>> scan(K key, ScanOptions options); # 查詢獲取元素 方法:RedisOperations<K, V> getOperations(); # 獲取基本的key操作 ####### 關於ZSetOperations查詢Range及reverseRange 方法:Set<V> range(K key, long start, long end); # 獲取有序集合中指定范圍的元素(下標條件) 方法:Set<V> rangeByScore(K key, double min, double max); # 獲取有序集合中指定范圍的元素(分數條件) 方法:Set<V> rangeByScore(K key, double min, double max, long offset, long count); # 獲取有序集合中指定范圍的元素(分數條件)並設置偏移量和返回總個數count 方法:Set<V> rangeByLex(K key, Range range) # 獲取有序集合中指定范圍的元素(元素條件) 方法:Set<V> rangeByLex(K key, Range range, Limit limit); # 獲取有序集合中指定范圍的元素(元素條件)並設置limit條件里的offset/count 方法:Set<TypedTuple<V>> rangeByScoreWithScores(K key, double min, double max); # 獲取有序集合中指定范圍的元素和分數(分數條件) 方法:Set<TypedTuple<V>> rangeByScoreWithScores(K key, double min, double max, long offset, long count); # 獲取有序集合中指定范圍的元素和分數(分數條件)並設置偏移量和返回總個數count 方法:Set<TypedTuple<V>> rangeWithScores(K key, long start, long end); # 獲取有序集合中指定范圍的元素(下標條件) # 關於:reverseRange的一套方法和上面的幾個方法使用上一樣,只不過輸出的是從大到小的返回 # 把上面方法的range換為reverseRange就是從大到小返回了,這里就不列舉了 方法:Long intersectAndStore(K key, K otherKey, K destKey); # 以key為主對比otherKey的交集,並把符合的元素存放到destKey有序集合上 方法:Long intersectAndStore(K key, Collection<K> otherKeys, K destKey); # 以key為主對比otherKeys多個有序集合的交集,並把符合的元素存放到destKey有序集合上 方法:Long intersectAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate) # 以key為主對比otherKeys多個有序集合的交集,並把符合的元素存放到destKey有序集合上;並設置合並的分數處理方式sum/max/min 方法:Long intersectAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights); # 和上面一樣,就多出一個Weights權重計算(乘法因子) # 關於:unionAndStore 並集方法參考上面四個,用法一樣,只不過 方法:Long remove(K key, Object... values); # 刪除有序集合中指定的元素 方法:Long removeRange(K key, long start, long end); # 刪除有序集合中指定的元素(下標條件) 方法:Long removeRangeByScore(K key, double min, double max); # 刪除有序集合中指定的元素(分數條件) 方法:Long removeRangeByLex(K key, Range range); # 刪除有序集合中指定的元素(元素條件)
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { //測試數據,在Redis客戶端執行 //zadd myzsetA -5 && -2 ## 0 @@ 1 aa 5 bb 10 cc 15 dd 20 ee 25 ff 30 gg 35 hh 40 ii 45 jj 50 kk //zadd chinas -10 Anhui 45 Shanghai 24 Beijing 33 Henan 87 Guangdong //zadd zsetA 20 zhangsan 25 lisi 33 wanger 15 mazi 33 babao 23 xiechao //zadd zsetB 5 zhangsan 10 lisi 15 mazi 20 babao //zadd zsetC 10 aa 10 bb 10 cc 10 dd 10 ee 10 ff 10 gg 10 hh //獲取基本的 ZSetOperations 操作對象(操作Redis的ZSet類型) ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet(); //往有序集合里面插入一個元素 Boolean add1 = opsForZSet.add("ChinaA", "Beijing", 23D); //往有序集合內部批量插入元素(返回添加成功的元素個數) Set<ZSetOperations.TypedTuple<String>> batchKV1 = new HashSet<>(); batchKV1.add(ZSetOperations.TypedTuple.of("Shanghai", 22D)); batchKV1.add(ZSetOperations.TypedTuple.of("Anhui", 27D)); Long add2 = opsForZSet.add("ChinaA", batchKV1); //往有序集合里面插入一個或多個元素(若當前集合存在此元素則不會添加)(返回添加成功的個數或返回true|false) Boolean aBoolean = opsForZSet.addIfAbsent("ChinaA", "Beijing", 40D); batchKV1.add(ZSetOperations.TypedTuple.of("Jiangsu", 66D)); Long chinaA = opsForZSet.addIfAbsent("ChinaA", batchKV1); //對有序集合中的某個元素進行分數的累加 返回累加后的元素分數,此時我對“Beijing”累加10,返回33.0 Double increment = opsForZSet.incrementScore("ChinaA", "Beijing", 10D); //返回有序集合中指定范圍的元素個數,根據分數條件 Long count1 = opsForZSet.count("myzsetA", 10D, 35D); //返回有序集合中指定范圍的元素個數,根據lex元素條件(紅面方法有提到) RedisZSetCommands.Range rq = RedisZSetCommands.Range.range(); rq.gt("bb"); rq.lt("ff"); Long count2 = opsForZSet.lexCount("zsetC", rq); //返回當前元素“Beijing”在集合“chinas”中的位置(從小到大) 返回1 Long rank1 = opsForZSet.rank("chinas", "Beijing"); //返回當前元素“Beijing”在集合“chinas”中的位置(從大到小) 返回3 Long rank2 = opsForZSet.reverseRank("chinas", "Beijing"); //返回集合中元素個數 Long size1 = opsForZSet.size("chinas"); Long size2 = opsForZSet.zCard("chinas"); //返回有序集合中指定元素的分數 Double score = opsForZSet.score("chinas", "Shanghai"); //設置查詢條件並且查詢有序集合chinas的全部元素”*“,為什么count沒起效果沒太注意 ScanOptions build = ScanOptions.scanOptions().match("*").count(2L).build(); Cursor<ZSetOperations.TypedTuple<String>> scan = opsForZSet.scan("chinas", build); while (scan.hasNext()) { ZSetOperations.TypedTuple<String> next = scan.next(); System.out.println(next.getValue() + " : " + next.getScore()); } //返回基本操作方法對象 RedisOperations<String, String> operations = opsForZSet.getOperations(); } //關於ZSetOperations查詢Range及reverseRange // Range:分數按照從低到高查詢 // reverseRange:分數從高到地查詢 @Test void redisTemplateBaseRange() { //關於 lex 查詢 //創建關於lex的查詢方式 //若某個有序集合使用元素查詢時(lex),那么我推薦你最好使用分數都是相同的有序集合! // RedisZSetCommands.Range對象說明: // RedisZSetCommands.Range rq = RedisZSetCommands.Range.range(); // rq.gt("bb"); 代表查詢條件大於 “bb” 對於命令 (bb // rq.lt("ff"); 代表查詢條件小於 “ff” 對於命令 (ff // rq.gte("bb"); 代表查詢條件大於等於 “bb” 對於命令 [bb // rq.lte("ff"); 代表查詢條件小於等於 “ff” 對於命令 [ff // rq.getMax(); 代表查詢條件從最大 對於命令 + // rq.getMin(); 代表查詢條件從最小 對於命令 - // RedisZSetCommands.Limit對象說明: // RedisZSetCommands.Limit limit = new RedisZSetCommands.Limit(); // limit.count(2); 代表查詢2個元素 // limit.offset(3); 代表查詢偏移量3,就是從第4個開始,必須存在count屬性,偏移后查詢幾個 //獲取基本的 ZSetOperations 操作對象(操作Redis的ZSet類型) ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet(); //基本方式查詢,0 -1 代表查詢全部 Set<String> range1 = opsForZSet.range("zsetA", 0L, -1L); //關於 score 查詢 //分數查詢,查詢集合元素分數在1~45的元素 Set<String> range2 = opsForZSet.rangeByScore("myzsetA", 1D, 45D); //分數查詢,查詢集合元素分數在1~45的元素,偏移量1,查詢數3(就是第二個開始查詢,查詢3個) Set<String> range3 = opsForZSet.rangeByScore("myzsetA", 1D, 45D, 1L, 3L); //條件 RedisZSetCommands.Range rq = RedisZSetCommands.Range.range(); rq.getMin(); rq.getMax(); RedisZSetCommands.Limit limit = new RedisZSetCommands.Limit(); limit.count(2); limit.offset(3); //查詢有序集合zsetC的全部元素 - + Set<String> range4 = opsForZSet.rangeByLex("zsetC", rq); //查詢有序集合zsetC的全部元素 - + 並且返回偏移3,count = 2 Set<String> range5 = opsForZSet.rangeByLex("zsetC", rq, limit); //查詢有序集合chinas分數在-30 ~ 40之間的全部元素(返回元素和分數) Set<ZSetOperations.TypedTuple<String>> range6 = opsForZSet.rangeByScoreWithScores("chinas", -30D, 40D); Iterator<ZSetOperations.TypedTuple<String>> iterator = range6.iterator(); while (iterator.hasNext()) { ZSetOperations.TypedTuple<String> next = iterator.next(); System.out.println(next.getValue() + " : " + next.getScore()); } //查詢有序集合chinas分數在-30 ~ 40之間的全部元素 並且返回偏移1,count = 2 Set<ZSetOperations.TypedTuple<String>> range7 = opsForZSet.rangeByScoreWithScores("chinas", -30D, 40D, 1L, 2L); //查詢有序集合chinas分數在 - + (全部元素) Set<ZSetOperations.TypedTuple<String>> range8 = opsForZSet.rangeWithScores("chinas", 0L, -1L); // reverseRange // 關於:reverseRange的一套方法和上面的幾個方法使用上一樣,只不過輸出的是從大到小的返回 //從大到小的基本方式查詢,0 -1 代表查詢全部 Set<String> reverse1 = opsForZSet.reverseRange("myzsetA", 0L, -1L); } //關於ZSetOperations查詢差異查詢 @Test void redisTemplateBaseDiff() { //指定交集、並集的結果集的聚合方式: // 枚舉:RedisZSetCommands.Aggregate.MIN 指定min則交集並集的元素的分數取最小 // 枚舉:RedisZSetCommands.Aggregate.MAX 指定max則交集並集的元素的分數取最大 // 枚舉:RedisZSetCommands.Aggregate.SUM 指定sum(默認)則交集並集的元素的分數結合 //RedisZSetCommands.Weights: // RedisZSetCommands.Weights.fromSetCount(2):輸入排序集的集合計算,有幾個集合就寫幾 // RedisZSetCommands.Weights.of() :里面傳入int或者double(一種)的可變參,有幾個集合就傳幾個(做乘法計算) //獲取基本的 ZSetOperations 操作對象(操作Redis的ZSet類型) ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet(); //有序集合“zsetA” 對比 “zsetB” 獲取交集數據存放到"interZSetA"集合上(分數為兩個元素的相加) Long intersect1 = opsForZSet.intersectAndStore("zsetA", "zsetB", "interZSetA"); //以有序集合“zsetA”為主對比其它一個或多個集合的交集(接收集合keys) Long intersect2 = opsForZSet.intersectAndStore("zsetA", Arrays.asList("zsetB"), "interZSetA"); // Aggregate為max(代表並、交集取最大分數) Long intersect3 = opsForZSet.intersectAndStore("zsetA", Arrays.asList("zsetB"), "interZSetB", RedisZSetCommands.Aggregate.MAX); // Weights權重計算(乘法因子) Long intersect4 = opsForZSet.intersectAndStore("zsetA", Arrays.asList("zsetB"), "interZSetC", RedisZSetCommands.Aggregate.MAX, RedisZSetCommands.Weights.of(5, 10)); //unionAndStore //關於:unionAndStore 並集方法參考上面四個,用法一樣 //並集就是把幾個集合的元素並到一起(不漏任何元素),然后單個元素單獨計算,多個元素計算后合並到一起 } //刪除方法 @Test void redisTemplateBaseRemove() { //獲取基本的 ZSetOperations 操作對象(操作Redis的ZSet類型) ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet(); //刪除有序集合chinas里兩個元素,返回刪除成功的個數 Long remove1 = opsForZSet.remove("chinas", "Shanghai", "Henan"); //刪除有序集合chinas里全部元素 0 -1 Long remove2 = opsForZSet.removeRange("chinas", 0L, -1L); //刪除有序集合myzsetA里5 ~ 30 之間的元素 Long remove3 = opsForZSet.removeRangeByScore("myzsetA", 5D, 30D); //刪除有序集合zsetC里全部元素 RedisZSetCommands.Range rq = RedisZSetCommands.Range.range(); rq.getMin(); rq.getMax(); Long remove4 = opsForZSet.removeRangeByLex("zsetC", rq); }
7:GeoOperations之Geo地理空間類型

依賴補充方法: 接口:Metric 對象:public CustomMetric(double multiplier) 對象:public CustomMetric(double multiplier, String abbreviation) # 自定義指標接口 # 屬性: # multiplier:乘數,計算距離的乘積 # abbreviation:距離單位縮寫;用來指定單位 m、km、ft、mi # 方法: # String getAbbreviation():獲取單位縮寫 # double getMultiplier():獲取乘數 對象:public Point(double x, double y) # 設置地理空間點值(X、Y) # 方法: # getX()、getY():獲取X、Y坐標 對象:public Distance(double value) 對象:public Distance(double value, Metric metric) # 值對象表示給定度量中的距離 # 屬性: # double value:設置距離 # Metric metric:設置自定義指標 # 方法: # Metric getMetric():獲取Metric對象 # double getNormalizedValue():獲取規格化值 # String getUnit():獲取單位 # double getValue():獲取距離值 對象:public Circle(Point center, double radius) 對象:public Circle(Point center, Distance radius) # 設置地理空間范圍值(半徑) # 屬性: # Point center:地理空間點值 # double radius:半徑范圍值 # Distance radius:值對象表示給定度量中的距離 # 基本方法: 方法:Long add(K key, Point point, M member); 方法:Long add(K key, GeoLocation<M> location); 方法:Long add(K key, Map<M, Point> memberCoordinateMap); 方法:Long add(K key, Iterable<GeoLocation<M>> locations); # 添加地理空間元素 方法:List<Point> position(K key, M... members); # 獲取元素地點坐標 方法:List<String> hash(K key, M... members); # 獲取地點hash值 方法:Distance distance(K key, M member1, M member2, Metric metric); 方法:Distance distance(K key, M member1, M member2); # 獲取兩點距離 方法:GeoResults<GeoLocation<M>> radius(K key, Circle within); 方法:GeoResults<GeoLocation<M>> radius(K key, M member, double radius); 方法:GeoResults<GeoLocation<M>> radius(K key, M member, Distance distance); 方法:GeoResults<GeoLocation<M>> radius(K key, Circle within, GeoRadiusCommandArgs args); 方法:GeoResults<GeoLocation<M>> radius(K key, M member, Distance distance, GeoRadiusCommandArgs args); # 計算獲取范圍內的空間元素
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { //獲取基本的 GeoOperations 操作對象(操作Redis的Geo地理空間類型) GeoOperations<String, String> opsForGeo = stringRedisTemplate.opsForGeo(); // 初始化基本的坐標 Point hefei = new Point(117.30794D, 31.79322D); // 合肥 Point wuhu = new Point(118.38548D, 31.34072D); // 蕪湖 Point bengbu = new Point(117.36779D, 32.94448D); // 蚌埠 Point luan = new Point(116.53949D, 31.74933D); // 六安 Point bozhou = new Point(115.77914D, 33.87641D); // 亳州 Point chuzhou = new Point(118.30553D, 32.2948D); // 滁州 Point fuyang = new Point(115.85668D, 32.91303D); // 阜陽 // 添加 hefei 地理空間坐標 opsForGeo.add("Anhui", hefei, "hefei"); // 添加 wuhu 地理空間坐標 RedisGeoCommands.GeoLocation<String> wuhu1 = new RedisGeoCommands.GeoLocation<>("wuhu", wuhu); opsForGeo.add("Anhui", wuhu1); // 使用map批量添加"bengbu" 、 "luan" 地理空間坐標 Map<String, Point> points = new HashMap<>(); points.put("bengbu", bengbu); points.put("luan", luan); opsForGeo.add("Anhui", points); // 使用集合批量添加 “bozhou”、“chuzhou”、“fuyang” 地理空間坐標 List<RedisGeoCommands.GeoLocation<String>> lists = new ArrayList<>(); lists.add(new RedisGeoCommands.GeoLocation<>("bozhou", bozhou)); lists.add(new RedisGeoCommands.GeoLocation<>("chuzhou", chuzhou)); lists.add(new RedisGeoCommands.GeoLocation<>("fuyang", fuyang)); opsForGeo.add("Anhui", lists); // 從地理坐標集合里獲取 “hefei”,"luan"地理坐標值,若獲取的空間不存在則為null List<Point> position = opsForGeo.position("Anhui", "hefei", "luan"); //for (Point p : position) //System.out.println("X:" + p.getX() + " Y:" + p.getY()); //X:117.30793744325638 Y:31.793219150805264 //X:116.53948992490768 Y:31.749330453931314 //返回一個有效的hash字符串,返回的字符串是11位的字符,它與Redis內部的52位表示精度相差可以忽略 //若兩個11位的hash字符串越接近,那么代表坐標越接近 List<String> hash = opsForGeo.hash("Anhui", "hefei", "luan"); //hash.forEach(System.out::println); //wtekv7v0cj0 //wtduegv3qb0 //計算 "hefei" 到 "luan" 的元素位置距離 Metric ms = new CustomMetric(1000D, "m"); Distance de = opsForGeo.distance("Anhui", "hefei", "luan", ms); System.out.println("獲取傳入的Metric對象:" + de.getMetric()); System.out.println("獲取傳入的Metric對象的單位縮寫:" + de.getMetric().getAbbreviation()); System.out.println("獲取傳入的Metric對象的乘數:" + de.getMetric().getMultiplier()); System.out.println("獲取規格化值:" + de.getNormalizedValue() + " km"); System.out.println("獲取單位:" + de.getUnit()); System.out.println("獲取距離值:" + de.getValue() + " " + de.getMetric().getAbbreviation()); //計算 "wuhu" 到 "bengbu" 的元素位置距離(默認就是m) Distance distance = opsForGeo.distance("Anhui", "wuhu", "bengbu"); System.out.println(distance.getValue() + "米"); // 設置范圍指標 Circle circle = new Circle(hefei, 100000D); // 獲取 ”Anhui“ 元素范圍點的空間元素 GeoResults<RedisGeoCommands.GeoLocation<String>> radiusA = opsForGeo.radius("Anhui", circle); List<Map<String, Object>> list = radiusPars(opsForGeo, radiusA); System.out.println(list); //刪除元素 Long remove = opsForGeo.remove("Anhui", "hefei"); } private static List<Map<String, Object>> radiusPars(GeoOperations<String, String> opsForGeo, GeoResults<RedisGeoCommands.GeoLocation<String>> radiusA) { //用來存儲具體的地理空間元素信息 List<Map<String, Object>> listInfo = new ArrayList<>(); if (radiusA != null) { //用來存儲符合范圍條件全部的地理空間元素 List<String> geos = new ArrayList<>(); //獲取全部地理空間半徑范圍內的元素 List<GeoResult<RedisGeoCommands.GeoLocation<String>>> contents = radiusA.getContent(); for (GeoResult<RedisGeoCommands.GeoLocation<String>> s : contents) { //獲取一個個地理空間元素 並保存起來 RedisGeoCommands.GeoLocation<String> content = s.getContent(); geos.add(content.getName()); } for (int i = 0; i < geos.size(); i++) { String placeName = geos.get(i); //地名 Point point = Objects.requireNonNull(opsForGeo.position("Anhui", placeName)).get(0); //獲取具體坐標 Map<String, Object> map = new HashMap<>(); map.put("placeName", placeName); map.put("point", point); listInfo.add(map); } } return listInfo; }
8:HyperLogLog之超級基數統計類型
HyperLogLog主要是用來大數據量統計的類型算法,比如我們統計網站的一天訪問量;雖然我們可以使用Redis中String類型的incr、incrby來實現,但是它只能統計訪問本網站的每個請求計數累加(除了程序控制),但是我要說每個IP請求多少次都算作一次,對於多個相同IP的請求需要去重計數,在這種環境下HyperLogLog是優選,雖然hash、set、bitmaps可以解決這種問題,但隨着數據不斷增加,導致占用空間越來越大,對於非常大的數據集是不切實際的;
基本方法: 方法:Long add(K key, V... values); # 添加指定元素到hyperloglog中 方法:Long size(K... keys); # 返回一個或多個鍵內統計基數(就是返回不相同的元素個數,用來統計),計算統計誤差在0.81% 方法:Long union(K destination, K... sourceKeys); # 統計一個或多個鍵內統計基數並放到外部集合里 方法:void delete(K key); # 刪除元素
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { //獲取基本的 HyperLogLogOperations 操作對象(操作Redis的超級基數統計類型) HyperLogLogOperations<String, String> opsForHyperLogLog = stringRedisTemplate.opsForHyperLogLog(); //添加指定元素到hyperloglog中,如果指定的鍵不存在,該命令會自動創建一個空的hyperloglog結構 //注:重復時只保存一個 Long nameA = opsForHyperLogLog.add("nameA", "Tom", "Jack", "Lucy", "Lucy"); Long nameB = opsForHyperLogLog.add("nameB", "Jack", "Mia", "Emma", "Ava"); //獲取存儲的個數 Long size = opsForHyperLogLog.size("nameA"); //多鍵統計,把多個集合存放到一個集合中(重復則被剔除) Long union = opsForHyperLogLog.union("nameC", "nameA", "nameB"); System.out.println(opsForHyperLogLog.size("nameC")); //刪除元素 opsForHyperLogLog.delete("nameC"); }
9:BitMap[位圖]=>ValueOperations之String類型操作
位圖我則以一個示例來講解,比如來記錄一個員工月簽到情況;簽到只會分已簽到和未簽到,那就用1 簽到 和0 未簽到 來表示,一個月最大31天,那我可以用4個byte位來記錄一個月,因為4*8=32byte;具體的BitMap不知道咋操作的可以參考我的 Redis基本命令(我們測試數據從這篇文章復制)
下面我來帶你們看看螞蟻小哥的簽到情況吧:
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { //這里我只是簡單介紹了,后期遇到了會再次補充 //獲取基本的 ValueOperations 操作對象(操作Redis的String類型) ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue(); //記錄螞蟻小哥 2、10、15、17、30這幾天上班了(后面會執行測試數據覆蓋這些數據) opsForValue.setBit("record", 2, true); opsForValue.setBit("record", 10, true); opsForValue.setBit("record", 15, true); opsForValue.setBit("record", 17, true); opsForValue.setBit("record", 30, true); //查詢螞蟻小哥20號和30號這兩天是否上班了 有記錄為true,未簽到為false Boolean record20 = opsForValue.getBit("record", 20L); Boolean record30 = opsForValue.getBit("record", 30L); // 注:RedisTemplate中並沒有直接提供方法來調用bitCount方法,需要通過redisTemplate.execute來執行bitCount方法 // 統計螞蟻小哥簽到的record 記錄前 16天(具體為什么16天參考上面的基礎命令) Long statisticsA = (Long) opsForValue.getOperations() .execute((RedisCallback<Object>) e -> e.bitCount("record".getBytes(), 0L, 1L)); // 統計螞蟻小哥簽到的record一個月的全部記錄 Long statisticsB = (Long) opsForValue.getOperations() .execute((RedisCallback<Object>) e -> e.bitCount("record".getBytes())); System.out.println("這個月螞蟻小哥上班打開:" + statisticsB); //補充 在多個鍵中執行位運算,並將結果存儲到目標鍵中 // RedisStringCommands.BitOperation.(AND|OR|XOR|NOT) 四種位運算 // “newBit” 把多個鍵運算后存放到此鍵中 // "bitA"、“bitB” 待處理的要運算的多個鍵 Long bitOp = (Long) opsForValue.getOperations().execute((RedisCallback<Object>) e -> e.bitOp(RedisStringCommands.BitOperation.AND, "newBit".getBytes(), "bitA".getBytes(), "bitB".getBytes())); System.out.println(bitOp); }
七:RedisTemplate批處理(重要)
在RedisTemplate里我們可以看到execute(處理)和executePipelined(批處理)兩種方法;下面我就針對這兩種方式做個基本介紹說明
1:基本介紹
StringRedisTemplate繼承RedisTemplate,只是提供字符串的操作,復雜的Java對象還要自行處理 RedisCallback:
讓RedisTemplate進行回調,通過他們可以在同一條連接中執行多個redis命令(接近底層,不推薦使用,都是使用byte[]進行操作)
SessionCallback:
他比RedisCallback的優勢在於SessionCallback提供了良好的封裝
注:RedisCallback和Sessionallback都是在一個連接里,防止每執行一條命令創建一次連接
RedisConnection:
redis連接對象,內部封裝了redis的全部命令
基本封裝對象方法:
RedisKeyCommands keyCommands() 獲取基本key操作
RedisStringCommands stringCommands() 獲取String類型的命令
RedisHashCommands hashCommands() 獲取Hash類型的命令
RedisListCommands listCommands() 獲取List類型的命令
RedisSetCommands setCommands() 獲取Set類型的命令
RedisZSetCommands zSetCommands() 獲取ZSet類型的命令
RedisGeoCommands geoCommands() 獲取Geo地理空間類型的命令
RedisHyperLogLogCommands hyperLogLogCommands()獲取HyperLogLog超級基數統計類型的命令
RedisServerCommands serverCommands() 獲取服務器的命令
2:execute處理
不管execute內執行多少條redis命令,最終只會返回一個結果,事務的除外,后面會說
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private RedisTemplate redisTemplate; @Test void redisTemplateBase() { //使用 RedisCallback來處理 Object ex1 = redisTemplate.execute(new RedisCallback() { @Override public Object doInRedis(RedisConnection rc) throws DataAccessException { return rc.set("name".getBytes(), "zhangsan".getBytes()); } }); System.out.println("使用redisTemplate來操作execute設置值:" + ex1); //使用 RedisCallback來處理 String ex1Str = stringRedisTemplate.execute(new RedisCallback<String>() { @Override public String doInRedis(RedisConnection rc) throws DataAccessException { RedisStringCommands stringCommands = rc.stringCommands(); stringCommands.set("address".getBytes(), "anhui dianzi xueyuan".getBytes()); return new String(Objects.requireNonNull(stringCommands.get("address".getBytes()))); } }); System.out.println("使用stringRedisTemplate來操作execute獲取address:" + ex1Str); //使用 SessionCallback來處理(推薦) Object ex2 = redisTemplate.execute(new SessionCallback() { @Override public Object execute(RedisOperations operations) throws DataAccessException { ValueOperations opsForValue = operations.opsForValue(); opsForValue.set("address", "anhui"); return opsForValue.get("address"); } }); System.out.println("使用redisTemplate來操作execute獲取address值:" + ex2); //使用 SessionCallback來處理(推薦) String ex2Str = stringRedisTemplate.execute(new SessionCallback<String>() { @Override public <K, V> String execute(RedisOperations<K, V> operations) throws DataAccessException { ValueOperations<K, V> opsForValue = operations.opsForValue(); opsForValue.set((K)"sex",(V)"female"); return (String)opsForValue.get("sex"); } }); System.out.println("使用stringRedisTemplate來操作execute獲取sex:" + ex2Str); }
3:executePipelined批處理
我們在executePipelined內不管執行多少條redis命令,最終都會把每條的執行結果以集合方式返回
//注入鍵值都為String對象的RedisTemplate對象 @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { // 推薦使用 SessionCallback 因為對此有封裝,而 RedisCallback 不推薦;;在這內部可以執行事務 List<Object> dataA = stringRedisTemplate.executePipelined(new SessionCallback<Object>() { @Override public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException { //把RedisOperations接口賦值給StringRedisTemplate(字符串的K,V) StringRedisTemplate stringRT = (StringRedisTemplate) operations; //stringRT.multi(); 開啟事務 //獲取String類型的k,v操作 ValueOperations<String, String> opsForValue = stringRT.opsForValue(); //執行Redis命令,這執行的每一條語句最終都會返回,雖然后面返回 return null, 但是結果已經被封裝返回了 opsForValue.set("name", "zhansgan"); opsForValue.set("address", "anhui"); opsForValue.set("salary", "40000.50"); opsForValue.get("name"); opsForValue.get("address"); opsForValue.get("salary"); //stringRT.exec(); 結束事務 return null; } }); System.out.println(dataA); // 未開事務打印結果 [true, true, true, zhansgan, anhui, 40000.50] // 開事務打印結果 [[true, true, true, zhansgan, anhui, 40000.50]] }
.