1. 从零开始认识 redis

1. Redis 概述

Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库在内存当中进行操作,定期通过异步操作把数据库数据写到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的KeyValue DB。 Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能。默认16个数据库,类似数组下标是从零开始,初始默认使用零号库,统一的密码管理,16个库都是同样密码,要么都连上要么一个也连接不上,redis默认端口是6379。

redis的优点

  • 速度快:因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
  • 持久化:定期通过异步操作把数据库数据写到硬盘上进行保存
  • 支持丰富数据类型:支持string,list,set,sorted set,hash
  • 支持事务:操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
  • 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

2. redis 安装

  • 下载好软件,我这里是用绿色压缩包版
  • 解压到相应路径(非中文,非空格的目录下)
  • 启动redis server (进入redis找到redis-server.exe双击启动)
  • 启动redis cli (进入redis找到redis-cli.exe 双击启动)
  • 测试
  1. set name zhangsan
  2. OK
  3. get name
  4. "zhangsan"

这样redis就安装成功了(windows)

3. redis 图形管理工具的安装

官方出了RedisDesktopManager-0.9.8版本后要购买了。所以本次我们要使用的就是RedisDesktopManager-0.9.8版本

  • 下载安装
  • 解压到相应的目录(非中文,非空格)
  • 进入到RedisDesktopManager找到rdm.exe 双击
  • 创建链接
  • 测试

2. redis 常用命令操作

1. redis的数据类型介绍

redis存储的是:key,value格式的数据,其中key都是字符串,value有5种不同的数据结构

1. 字符串类型 string
  • string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value;
  • string类型是二进制安全的。意思是redis的string可以存储任何数据。如图片或者序列化的对象 ;

2. 哈希类型 hash
  • redis的hash 是一个键值对集合;
  • redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象;
  • 类似Java里面的Map

3. 列表类型 list

Redis的list是每个子元素都是String类型的双向链表,可以通过push和pop操作从列表的头部或者尾部添加或者删除元素,这样
List即可以作为栈,也可以作为队列。使用List结构,我们可以轻松地实现最新消息排行等功能

4. 集合类型 set
  • redis的set是string类型的无序集合。它是通过HashTable实现的。

5. 有序集合类型 sortedset
  • redis的sortedset和 set 一样也是string类型元素的集合;
  • 不同的是每个元素都会关联一个double类型的分数;
  • redis正是通过分数来为集合中的成员进行从小到大的排序。sortedset的成员是唯一的,但分数(score)却可以重复;

2. 常用的指令

2.1 字符串类型 String
  • 赋值命令:SET key value

如果key不存在创建该key并赋值,返回“OK”,如果该Key已经存在,则覆盖其原有值。返回”OK

  • 取值命令:GET key

获取指定 Key 的 Value。如果 key 存在返回对应的 value,如果该 Key 不存在,返回 nil

  • 数字递增:INCR key

将指定 Key 的 Value 原子性的递增1。如果该 Key 不存在,其初始值为0,在 incr 之后其值为1

  • 数字递减:DECR key

将指定 Key 的 Value 原子性的递减1。如果该 Key 不存在,其初始值为0,在 decr 之后其值为-1。操作返回递减后的
value值。

  • 增加指定的整数:INCRBY key increment

将指定Key的Value原子性的增加increment。如果该Key不存在,其初始值为0,在incrby之后其值为increment。操作成
功则返回增加后的value值。

  • 减少指定的整数:DECRBY key decrement

将指定Key的Value原子性的减少decrement。如果该Key不存在,其初始值为0,在decrby之后其值为-decrement。操
作成功则返回减少后的value值。

  • 向尾部追加值:APPEND key value

如果该Key已经存在,APPEND命令将参数Value的数据追加到已存在Value的末尾。如果该Key不存在,APPEND命令将
会创建一个新的Key/Value。返回追加后Value的字符串长度。

2.2 哈希类型 hash
  • 赋值命令:HSET key field value

为指定的 Key (对象/集合)添加属性并设置值,如果 Key 不存在,该命令将创建新 Key (对象/集合) 以用于存储参数中的
属性对,如果参数中的 Field(属性) 在该 Key 中已经存在,则用新值覆盖其原有值。 返回1表示新的 Field(属性) 被设置了新值,
0表示Field已经存在,用新值覆盖原有值。

  • 取值命令:HGET key field

返回指定 Key (对象/集合)中指定 Field(属性) 的关联值,如果参数中的 Key 或 Field 不存在,返回nil

  • 删除字段 :HDEL key field [field …]

从指定Key (对象/集合)的Hashes Value中删除参数中指定的多个字段,如果不存在的字段将被忽略,返回实际删除的
Field数量。如果Key不存在,则将其视为空Hashes,并返回0。

  • 设置多个字段的值:HMSET key field value [field value …]

逐对依次设置参数中给出的Field/Value对。如果其中某个Field已经存在,则用新值覆盖原有值。如果Key (对象/集合)不
存在,则创建新Key (对象/集合),同时设定参数中的Field/Value。

  • 获取多个字段的值:HMGET key field [field …]

获取和参数中指定 Fields 关联的一组 Values,其返回顺序等同于 Fields 的请求顺序。如果请求的 Field 不存在,其值返
回null。

2.3 列表类型 list
  • 向列表头部添加元素:LPUSH key value [value …]

向列表左边添加元素。如果该 Key 不存在,创建一个与该 Key 关联的空链表,之后再将数据从链表的头部插入。操作成
功则返回插入后链表中元素的数量。

  • 向列表尾部添加元素:RPUSH key value [value …]

向列表右边添加元素。如果该 Key 不存在,创建一个与该 Key 关联的空链表,之后再将数据从链表的尾部插入。操作成
功则返回插入后链表中元素的数量。

  • 获得列表:LRANGE key start stop

该命令的参数start和end都是0-based。即0表示链表头部(leftmost)的第一个元素。其中start的值也可以为负值,-1将表
示链表中的最后一个元素,即尾部元素,-2表示倒数第二个并以此类推。该命令在获取元素时,start和end位置上的元素也会被
取出。如果start的值大于链表中元素的数量,空链表将会被返回。如果end的值大于元素的数量,该命令则获取从start(包括
start)开始,链表中剩余的所有元素。注:Redis的列表起始索引为0。显然,LRANGE numbers 0 -1 可以获取列表中的所有元
素。返回指定范围内元素的列表。

  • 列表进行修剪:LTRIM KEY_NAME START STOP

让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 下标 0 表示列表头部第一个元素,以 1 表示列
表头部的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表尾部的最后一个元素, -2 表示列表的倒数第二个元
素,以此类推。

  • 元素从左边出栈:LPOP key

返回并弹出指定 Key 关联的链表中的第一个元素,即头部元素。如果该 Key 不存在,返回 nil。LPOP 命令执行两步操
作:第一步是将列表左边的元素从列表中移除,第二步是返回被移除的元素值。

  • 元素从右边出栈:RPOP key

返回并弹出指定 Key 关联的链表中的最后一个元素,即头部元素。如果该 Key 不存在,返回 nil。RPOP 命令执行两步操
作:第一步是将列表右边的元素从列表中移除,第二步是返回被移除的元素值。

  • 获取列表中元素的个数:LLEN key

返回指定 Key 关联的链表中元素的数量,如果该 Key 不存在,则返回 0。如果与该 Key 关联的 Value 的类型不是链表,
则返回相关的错误信息。

  • 获取指定索引的元素值:LINDEX key index

该命令将返回链表中指定位置(index)的元素,index 是0-based,表示从头部位置开始第 index 的元素,如果 index 为
-1,表示尾部元素。如果与该 Key 关联的不是链表,该命令将返回相关的错误信息。 如果超出 index 返回这返回 nil。

2.4 集合类型 set
  • 增加元素:SADD key member [member …]

如果在插入的过程用,参数中有的成员在Set中已经存在,该成员将被忽略,而其它成员仍将会被正常插入。如果执行该
命令之前,该Key并不存在,该命令将会创建一个新的Set,此后再将参数中的成员陆续插入。返回实际插入的成员数量。

  • 获得集合中元素个数:SCARD key

返回Set中成员的数量,如果该Key并不存在,返回0

  • 判断元素是否在集合中:SISMEMBER key member

判断参数中指定成员是否已经存在于与Key相关联的Set集合中。返回1表示已经存在,0表示不存在,或该Key本身并不
存在

  • 获得集合中的所有元素:SMEMBERS key

获取与该Key关联的Set中所有的成员。如果ket不存在返回空集合

  • 从集合中弹出一个元素:SPOP key

随机的移除并返回Set中的某一成员。 由于Set中元素的布局不受外部控制,因此无法像List那样确定哪个元素位于Set的
头部或者尾部。返回移除的成员,如果该Key并不存在,则返回nil。

  • 删除元素:SREM key member [member …]

从与Key关联的Set中删除参数中指定的成员,不存在的参数成员将被忽略,如果该Key并不存在,将视为空Set处理。返
回从Set中实际移除的成员数量,如果没有则返回0。

  • 随机获得集合中的元素:SRANDMEMBER key [count]

和SPOP一样,随机的返回Set中的一个成员,不同的是该命令并不会删除返回的成员。还可以传递count参数来一次随机
获得多个元素,根据count的正负不同,具体表现也不同。当count 为正数时,SRANDMEMBER 会随机从集合里获得count个
不重复的元素。如果count的值大于集合中的元素个数,则SRANDMEMBER 会返回集合中的全部元素。当count为负数时,
SRANDMEMBER 会随机从集合里获得|count|个的元素,如果|count|大与集合中的元素,就会返回全部元素不够的以重复元
素补齐,如果key不存在则返回nil

2.5 有序集合类型 sorted set
  • 增加元素:ZADD key score member [score] [member]

添加参数中指定的所有成员及其分数到指定key的Sorted Set中,在该命令中我们可以指定多组score/member作为参
数。如果在添加时参数中的某一成员已经存在,该命令将更新此成员的分数为新值,同时再将该成员基于新值重新排序。如果
键不存在,该命令将为该键创建一个新的Sorted Set Value,并将score/member对插入其中。如果该键已经存在,但是与其关
联的Value不是Sorted Set类型,相关的错误信息将被返回。添加成功返回实际插入的成员数量

  • 获得集合中元素个数:ZCARD key

返回Sorted Set中的成员数量,如果该Key不存在,返回0。

  • 获得指定分数范围内的元素个数:ZCOUNT key min max

该命令用于获取分数(score)在min和max之间的成员数量。(min=zcount key (min max 则 表示(min量。

  • 获得元素的分数:ZSCORE key member

如果该成员存在,以字符串的形式返回其分数,否则返回nil

  • 增加某个元素的分数:ZINCRBY key increment member

该命令将为指定Key中的指定成员增加指定的分数。如果成员不存在,该命令将添加该成员并假设其初始分数为0,此后
再将其分数加上increment。如果Key不存在,该命令将创建该Key及其关联的Sorted Set,并包含参数指定的成员,其分数为
increment参数。如果与该Key关联的不是Sorted Set类型,相关的错误信息将被返回。如果不报错则以串形式表示的新分数。

  • 获得排名在某个范围的元素列表(分数从小到大):ZRANGE key start stop [WITHSCORES]

该命令返回顺序在参数start和stop指定范围内的成员,这里start和stop参数都是0-based,即0表示第一个成员,-1表示
最后一个成员。如果start大于该Sorted Set中的最大索引值,或start > stop,此时一个空集合将被返回。如果stop大于最大索
引值,该命令将返回从start到集合的最后一个成员。如果命令中带有可选参数WITHSCORES选项,该命令在返回的结果中将包含
每个成员的分数值,如value1,score1,value2,score2…

  • 获得排名在某个范围的元素列表(元素分数从大到小排序):ZREVRANGE key start stop [WITHSCORES]

该命令的功能和ZRANGE基本相同,唯一的差别在于该命令是通过反向排序获取指定位置的成员,即从高到低的顺序。如
果成员具有相同的分数,则按降序字典顺序排序

  • 获得指定分数范围的元素(分数从小到大):ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

该命令将返回分数在min和max之间的所有成员,即满足表达式min <= score <= max的成员,其中返回的成员是按照其
分数从低到高的顺序返回,如果成员具有相同的分数,则按成员的字典顺序返回。可选参数LIMIT用于限制返回成员的数量范
围。可选参数offset表示从符合条件的第offset个成员开始返回,同时返回count个成员。可选参数WITHSCORES的含义参照
ZRANGE中该选项的说明。*最后需要说明的是参数中min和max的规则可参照命令ZCOUNT。

  • 获得指定分数范围的元素(元素分数从大到小排序):ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset
    count]

该命令除了排序方式是基于从高到低的分数排序之外,其它功能和参数含义均与ZRANGEBYSCORE相同。需要注意的是
该命令中的min和max参数的顺序和ZRANGEBYSCORE命令是相反的

  • 获得元素的排名:ZRANK key member

Sorted Set中的成员都是按照分数从低到高的顺序存储,该命令将返回参数中指定成员的位置值,其中0表示第一个成
员,它是Sorted Set中分数最低的成员。 如果该成员存在,则返回它的位置索引值。否则返回nil

  • 删除一个或多个元素:ZREM key member [member …]

该命令将移除参数中指定的成员,其中不存在的成员将被忽略。如果与该Key关联的Value不是Sorted Set,相应的错误
信息将被返回。 如果操作成功则返回实际被删除的成员数量

3. SpringBoot+jedis 企业实战开发工具类封装

因为 Jedis 集成了 redis 的命令操作,所以 Jedis 是 Redis 官方推荐的面向 Java 的操作 Redis 的客户端

1. 添加依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>redis.clients</groupId>
  7. <artifactId>jedis</artifactId>
  8. </dependency>

2. jedisPool连接池管理 jedis

客户端连接 Redis 使用的是 TCP 协议,直连的方式每次需要建立 TCP 连接,而连接池的方式是可以预先初始化好 Jedis 连接,所以每次只需要从 Jedis 连接池借用即可,而借用和归还操作是在本地进行的,只有少量的并发同步开销,远远小于新建TCP连接的开销。另外直连的方式无法限制Jedis对象的个数,在极端情况下可能会造成连接泄露,而连接池的形式可以有效的保护和控制资源的使用

  1. #jedisPool配置开始
  2. # 连接池中的最大空闲连接
  3. redis.maxIdle=30
  4. # 连接池中的最小空闲连接
  5. redis.minIdle=1
  6. #连接池最大连接数(使用负值表示没有限制)
  7. redis.maxTotal=100
  8. # 连接池最大阻塞等待时间(使用负值表示没有限制)10秒
  9. redis.maxWait=10000
  10. # Redis服务器地址
  11. redis.host=localhost
  12. # Redis服务器连接端口
  13. redis.port=6379
  14. # Redis链接超时时间 10秒
  15. redis.timeout=10000

3. 新建RedisConfig 配置链接词

  1. import org.springframework.beans.factory.annotation.Value;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import redis.clients.jedis.JedisPool;
  5. import redis.clients.jedis.JedisPoolConfig;
  6. /**
  7. * @ClassName: RedisConfig
  8. * TODO:类文件简单描述
  9. * @Author: 小霍
  10. * @UpdateUser: 小霍
  11. * @Version: 0.0.1
  12. */
  13. @Configuration
  14. public class RedisConfig {
  15. @Value("${redis.host}")
  16. private String host;
  17. @Value("${redis.port}")
  18. private int port;
  19. @Value("${redis.maxTotal}")
  20. private int maxTotal;
  21. @Value("${redis.maxIdle}")
  22. private int maxIdle;
  23. @Value("${redis.minIdle}")
  24. private int minIdle;
  25. @Value("${redis.timeout}")
  26. private int timeout;
  27. @Value("${redis.maxWait}")
  28. private long maxWait;
  29. @Bean
  30. public JedisPool getJedisPool(){
  31. JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();
  32. //连接池阻塞最大等待时间
  33. jedisPoolConfig.setMaxWaitMillis(maxWait);
  34. //连接池最大空闲连接数
  35. jedisPoolConfig.setMaxIdle(maxIdle);
  36. //连接池最小空闲连接数
  37. jedisPoolConfig.setMinIdle(maxIdle);
  38. //连接池最大链接数
  39. jedisPoolConfig.setMaxTotal(maxTotal);
  40. JedisPool jedisPool=new JedisPool(jedisPoolConfig,host,port,timeout);
  41. return jedisPool;
  42. }
  43. }

4. 创建 RedisService

  1. import org.springframework.stereotype.Service;
  2. /**
  3. * @ClassName: RedisService
  4. * redis 封装工具类
  5. * @Author: 小霍
  6. * @UpdateUser: 小霍
  7. * @Version: 0.0.1
  8. */
  9. @Service
  10. public class RedisService {
  11. }

5. jedis 企业开发工具类封装

Jedis实例都在JedisPool中,所以:

  1. 获取Jedis实例需要从JedisPool中获取;
  2. 用完Jedis实例需要还给JedisPool;
  3. 如果Jedis在使用过程中出错,则也需要还给JedisPool;

修改RedisService 加入如下代码

  1. import java.util.List;
  2. import java.util.Map;
  3. import java.util.Set;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. import redis.clients.jedis.Jedis;
  7. import redis.clients.jedis.JedisPool;
  8. /**
  9. * @UpdateDate: 2019/11/13 19:36
  10. * @Version: 0.0.1
  11. * @TODO : RedisService redis 封装工具类
  12. */
  13. @Service
  14. public class RedisService {
  15. @Autowired
  16. private RedisTemplate<String, Object> redisTemplate;
  17. /** -------------------key相关操作--------------------- */
  18. /**
  19. * @TODO : 是否存在key
  20. * @Author: 小霍
  21. * @UpdateUser:
  22. * @Version: 0.0.1
  23. * @param key
  24. * @return java.lang.Boolean
  25. *
  26. */
  27. public Boolean hasKey(String key) {
  28. if (null == key) {
  29. return false;
  30. }
  31. return redisTemplate.hasKey(key);
  32. }
  33. /**
  34. * @TODO : 删除key
  35. * @Author: 小霍
  36. * @UpdateUser:
  37. * @Version: 0.0.1
  38. * @param key
  39. * @return Boolean 成功返回true 失败返回false
  40. *
  41. */
  42. public Boolean delete(String key) {
  43. if (null == key) {
  44. return false;
  45. }
  46. return redisTemplate.delete(key);
  47. }
  48. /**
  49. * @TODO : 批量删除key
  50. * @Author: 小霍
  51. * @CreateDate: 2019/8/27 20:27
  52. * @UpdateUser:
  53. * @UpdateDate: 2019/8/27 20:27
  54. * @Version: 0.0.1
  55. * @param keys
  56. * @return Long 返回成功删除key的数量
  57. *
  58. */
  59. public Long delete(Collection<String> keys) {
  60. return redisTemplate.delete(keys);
  61. }
  62. /**
  63. * @TODO : 设置过期时间
  64. * @Author: 小霍
  65. * @UpdateUser:
  66. * @Version: 0.0.1
  67. * @param key
  68. * @param timeout
  69. * @param unit
  70. * @return java.lang.Boolean
  71. *
  72. */
  73. public Boolean expire(String key, long timeout, TimeUnit unit) {
  74. if (null == key || null == unit) {
  75. return false;
  76. }
  77. return redisTemplate.expire(key, timeout, unit);
  78. }
  79. /**
  80. * @TODO : 查找匹配的key
  81. * @Author: 小霍
  82. * @UpdateUser:
  83. * @Version: 0.0.1
  84. * @param pattern
  85. * @return java.util.Set<java.lang.String>
  86. *
  87. */
  88. public Set<String> keys(String pattern) {
  89. if (null == pattern) {
  90. return null;
  91. }
  92. return redisTemplate.keys(pattern);
  93. }
  94. /**
  95. * @TODO : 移除 key 的过期时间,key 将持久保持
  96. * @Author: 小霍
  97. * @UpdateUser:
  98. * @Version: 0.0.1
  99. * @param key
  100. * @return java.lang.Boolean
  101. *
  102. */
  103. public Boolean persist(String key) {
  104. if (null == key) {
  105. return false;
  106. }
  107. return redisTemplate.persist(key);
  108. }
  109. /**
  110. * @TODO : 返回 key 的剩余的过期时间
  111. * @Author: 小霍
  112. * @UpdateUser:
  113. * @Version: 0.0.1
  114. * @param key
  115. * @param unit
  116. * @return java.lang.Long 当 key 不存在时,返回 -2 。当 key 存在但没有设置剩余生存时间时,返回 -1
  117. * 。否则,以秒为单位,返回 key的剩余生存时间
  118. *
  119. */
  120. public Long getExpire(String key, TimeUnit unit) {
  121. if (null == key || null == unit) {
  122. throw new BusinessException(4001004, "key or TomeUnit 不能为空");
  123. }
  124. return redisTemplate.getExpire(key, unit);
  125. }
  126. // *************String相关数据类型***************************
  127. /**
  128. * @TODO : 设置指定 key 的值
  129. * @Author: 小霍
  130. * @UpdateUser:
  131. * @Version: 0.0.1
  132. * @param key
  133. * @param value
  134. * @return void
  135. *
  136. */
  137. public void set(String key, Object value) {
  138. if (null == key || null == value) {
  139. return;
  140. }
  141. redisTemplate.opsForValue().set(key, value);
  142. }
  143. /**
  144. * @TODO : 设置key 的值 并设置过期时间
  145. * @Author: 小霍
  146. * @UpdateUser:
  147. * @Version: 0.0.1
  148. * @param key
  149. * @param value
  150. * @param time
  151. * @param unit
  152. * @return void
  153. *
  154. */
  155. public void set(String key, Object value, long time, TimeUnit unit) {
  156. if (null == key || null == value || null == unit) {
  157. return;
  158. }
  159. redisTemplate.opsForValue().set(key, value, time, unit);
  160. }
  161. /**
  162. * @TODO : 设置key 的值 并设置过期时间 key存在 不做操作返回false key不存在设置值返回true
  163. * @Author: 小霍
  164. * @UpdateUser:
  165. * @Version: 0.0.1
  166. * @param key
  167. * @param value
  168. * @param time
  169. * @param unit
  170. * @return java.lang.Boolean
  171. *
  172. */
  173. public Boolean setifAbsen(String key, Object value, long time, TimeUnit unit) {
  174. if (null == key || null == value || null == unit) {
  175. throw new BusinessException(4001004, "kkey、value、unit都不能为空");
  176. }
  177. return redisTemplate.opsForValue().setIfAbsent(key, value, time, unit);
  178. }
  179. /**
  180. * @TODO : 获取指定Key的Value。如果与该Key关联的Value不是string类型,Redis将抛出异常,
  181. * 因为GET命令只能用于获取string Value,如果该Key不存在,返回null
  182. * @Author: 小霍
  183. * @UpdateUser:
  184. * @Version: 0.0.1
  185. * @param key
  186. * @return java.lang.Object
  187. *
  188. */
  189. public Object get(String key) {
  190. if (null == key) {
  191. return null;
  192. }
  193. return redisTemplate.opsForValue().get(key);
  194. }
  195. /**
  196. * @TODO : 很明显先get再set就说先获取key值对应的value然后再set 新的value 值。
  197. * @Author: 小霍
  198. * @UpdateUser:
  199. * @Version: 0.0.1
  200. * @param key
  201. * @param value
  202. * @return java.lang.Object
  203. *
  204. */
  205. public Object getSet(String key, Object value) {
  206. if (null == key) {
  207. return null;
  208. }
  209. return redisTemplate.opsForValue().getAndSet(key, value);
  210. }
  211. /**
  212. * @TODO : 通过批量的key获取批量的value
  213. * @Author: 小霍
  214. * @UpdateUser:
  215. * @Version: 0.0.1
  216. * @param keys
  217. * @return java.util.List<java.lang.Object>
  218. *
  219. */
  220. public List<Object> mget(Collection<String> keys) {
  221. if (null == keys) {
  222. return Collections.emptyList();
  223. }
  224. return redisTemplate.opsForValue().multiGet(keys);
  225. }
  226. /**
  227. * @TODO : 将指定Key的Value原子性的增加increment。如果该Key不存在,其初始值为0,在incrby之后其值为increment。
  228. * 如果Value的值不能转换为整型值,如Hi,该操作将执行失败并抛出相应异常。操作成功则返回增加后的value值。
  229. * @Author: 小霍
  230. * @UpdateUser:
  231. * @Version: 0.0.1
  232. * @param key
  233. * @param increment
  234. * @return long
  235. *
  236. */
  237. public long incrby(String key, long increment) {
  238. if (null == key) {
  239. throw new BusinessException(4001004, "key不能为空");
  240. }
  241. return redisTemplate.opsForValue().increment(key, increment);
  242. }
  243. /**
  244. *
  245. * @TODO : 将指定Key的Value原子性的减少decrement。如果该Key不存在,其初始值为0,
  246. * 在decrby之后其值为-decrement。如果Value的值不能转换为整型值,
  247. * 如Hi,该操作将执行失败并抛出相应的异常。操作成功则返回减少后的value值。
  248. * @Author: 小霍
  249. * @UpdateUser:
  250. * @Version: 0.0.1
  251. * @param key
  252. * @param decrement
  253. * @return java.lang.Long
  254. *
  255. */
  256. public Long decrby(String key, long decrement) {
  257. if (null == key) {
  258. throw new BusinessException(4001004, "key不能为空");
  259. }
  260. return redisTemplate.opsForValue().decrement(key, decrement);
  261. }
  262. /**
  263. * @TODO : 如果该Key已经存在,APPEND命令将参数Value的数据追加到已存在Value的末尾。如果该Key不存在,
  264. * APPEND命令将会创建一个新的Key/Value。返回追加后Value的字符串长度。
  265. * @Author: 小霍
  266. * @UpdateUser:
  267. * @Version: 0.0.1
  268. * @param key
  269. * @param value
  270. * @return java.lang.Integer
  271. *
  272. */
  273. public Integer append(String key, String value) {
  274. if (key == null) {
  275. throw new BusinessException(4001004, "key不能为空");
  276. }
  277. return redisTemplate.opsForValue().append(key, value);
  278. }
  279. //******************hash数据类型*********************
  280. /**
  281. * @TODO : 通过key 和 field 获取指定的 value
  282. * @Author: 小霍
  283. * @UpdateUser:
  284. * @Version: 0.0.1
  285. * @param key
  286. * @param field
  287. * @return java.lang.Object
  288. *
  289. */
  290. public Object hget(String key, Object field) {
  291. if (null == key || null == field) {
  292. return null;
  293. }
  294. return redisTemplate.opsForHash().get(key, field);
  295. }
  296. /**
  297. * @TODO : 为指定的Key设定Field/Value对,如果Key不存在,该命令将创建新Key以用于存储参数中的Field/Value对,
  298. * 如果参数中的Field在该Key中已经存在,则用新值覆盖其原有值。
  299. * 返回1表示新的Field被设置了新值,0表示Field已经存在,用新值覆盖原有值。
  300. * @Author: 小霍
  301. * @UpdateUser:
  302. * @Version: 0.0.1
  303. * @param key
  304. * @param field
  305. * @param value
  306. * @return
  307. *
  308. */
  309. public void hset(String key, Object field, Object value) {
  310. if (null == key || null == field) {
  311. return;
  312. }
  313. redisTemplate.opsForHash().put(key, field, value);
  314. }
  315. /**
  316. * @TODO : 判断指定Key中的指定Field是否存在,返回true表示存在,false表示参数中的Field或Key不存在。
  317. * @Author: 小霍
  318. * @UpdateUser:
  319. * @Version: 0.0.1
  320. * @param key
  321. * @param field
  322. * @return java.lang.Boolean
  323. *
  324. */
  325. public Boolean hexists(String key, Object field) {
  326. if (null == key || null == field) {
  327. return false;
  328. }
  329. return redisTemplate.opsForHash().hasKey(key, field);
  330. }
  331. /**
  332. * @TODO : 从指定Key的Hashes Value中删除参数中指定的多个字段,如果不存在的字段将被忽略,
  333. * 返回实际删除的Field数量。如果Key不存在,则将其视为空Hashes,并返回0。
  334. * @Author: 小霍
  335. * @UpdateUser:
  336. * @Version: 0.0.1
  337. * @param key
  338. * @param fields
  339. * @return java.lang.Long
  340. *
  341. */
  342. public Long hdel(String key, Object... fields) {
  343. if (null == key || null == fields || fields.length == 0) {
  344. return 0L;
  345. }
  346. return redisTemplate.opsForHash().delete(key, fields);
  347. }
  348. /**
  349. * @TODO : 通过key获取所有的field和value
  350. * @Author: 小霍
  351. * @UpdateUser:
  352. * @Version: 0.0.1
  353. * @param key
  354. * @return java.util.Map<java.lang.Object,java.lang.Object>
  355. *
  356. */
  357. public Map<Object, Object> hgetall(String key) {
  358. if (key == null) {
  359. return null;
  360. }
  361. return redisTemplate.opsForHash().entries(key);
  362. }
  363. /**
  364. * @TODO : 逐对依次设置参数中给出的Field/Value对。如果其中某个Field已经存在,则用新值覆盖原有值。
  365. * 如果Key不存在,则创建新Key,同时设定参数中的Field/Value。
  366. * @Author: 小霍
  367. * @UpdateUser:
  368. * @Version: 0.0.1
  369. * @param key
  370. * @param hash
  371. * @return
  372. *
  373. */
  374. public void hmset(String key, Map<String, Object> hash) {
  375. if (null == key || null == hash) {
  376. return;
  377. }
  378. redisTemplate.opsForHash().putAll(key, hash);
  379. }
  380. /**
  381. * @TODO : 获取和参数中指定Fields关联的一组Values,其返回顺序等同于Fields的请求顺序。
  382. * 如果请求的Field不存在,其值对应的value为null。
  383. * @Author: 小霍
  384. * @UpdateUser:
  385. * @Version: 0.0.1
  386. * @param key
  387. * @param fields
  388. * @return java.util.List<java.lang.Object>
  389. *
  390. */
  391. public List<Object> hmget(String key, Collection<Object> fields) {
  392. if (null == key || null == fields) {
  393. return null;
  394. }
  395. return redisTemplate.opsForHash().multiGet(key, fields);
  396. }
  397. /**
  398. * @TODO : 对应key的字段自增相应的值
  399. * @Author: 小霍
  400. * @UpdateUser:
  401. * @Version: 0.0.1
  402. * @param key
  403. * @param field
  404. * @param increment
  405. * @return java.lang.Long
  406. *
  407. */
  408. public Long hIncrBy(String key, Object field, long increment) {
  409. if (null == key || null == field) {
  410. throw new BusinessException(4001004, "key or field 不能为空");
  411. }
  412. return redisTemplate.opsForHash().increment(key, field, increment);
  413. }
  414. // ***************List数据类型***************
  415. /**
  416. * @TODO : 向列表左边添加元素。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的头部插入。
  417. * 如果该键的Value不是链表类型,该命令将将会抛出相关异常。操作成功则返回插入后链表中元素的数量。
  418. * @Author: 小霍
  419. * @UpdateUser:
  420. * @Version: 0.0.1
  421. * @param key
  422. * @param strs 可以使一个string 也可以使string数组
  423. * @return java.lang.Long 返回操作的value个数
  424. *
  425. */
  426. public Long lpush(String key, Object... strs) {
  427. if (null == key) {
  428. return 0L;
  429. }
  430. return redisTemplate.opsForList().leftPushAll(key, strs);
  431. }
  432. /**
  433. * @TODO : 向列表右边添加元素。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的尾部插入。
  434. * 如果该键的Value不是链表类型,该命令将将会抛出相关异常。操作成功则返回插入后链表中元素的数量。
  435. * @Author: 小霍
  436. * @UpdateUser:
  437. * @Version: 0.0.1
  438. * @param key
  439. * @param strs 可以使一个string 也可以使string数组
  440. * @return java.lang.Long 返回操作的value个数
  441. *
  442. */
  443. public Long rpush(String key, Object... strs) {
  444. if (null == key) {
  445. return 0L;
  446. }
  447. return redisTemplate.opsForList().rightPushAll(key, strs);
  448. }
  449. /**
  450. * @TODO : 返回并弹出指定Key关联的链表中的第一个元素,即头部元素。如果该Key不存在,
  451. * 返回nil。LPOP命令执行两步操作:第一步是将列表左边的元素从列表中移除,第二步是返回被移除的元素值。
  452. * @Author: 小霍
  453. * @UpdateUser:
  454. * @Version: 0.0.1
  455. * @param key
  456. * @return java.lang.Object
  457. *
  458. */
  459. public Object lpop(String key) {
  460. if (null == key) {
  461. return null;
  462. }
  463. return redisTemplate.opsForList().leftPop(key);
  464. }
  465. /**
  466. * @TODO : 返回并弹出指定Key关联的链表中的最后一个元素,即头部元素。如果该Key不存在,返回nil。
  467. * RPOP命令执行两步操作:第一步是将列表右边的元素从列表中移除,第二步是返回被移除的元素值。
  468. * @Author: 小霍
  469. * @UpdateUser:
  470. * @Version: 0.0.1
  471. * @param key
  472. * @return java.lang.Object
  473. *
  474. */
  475. public Object rpop(String key) {
  476. if (null == key) {
  477. return null;
  478. }
  479. return redisTemplate.opsForList().rightPop(key);
  480. }
  481. /**
  482. * @TODO : 该命令的参数start和end都是0-based。即0表示链表头部(leftmost)的第一个元素。
  483. * 其中start的值也可以为负值,-1将表示链表中的最后一个元素,即尾部元素,-2表示倒数第二个并以此类推。
  484. * 该命令在获取元素时,start和end位置上的元素也会被取出。如果start的值大于链表中元素的数量,
  485. * 空链表将会被返回。如果end的值大于元素的数量,该命令则获取从start(包括start)开始,链表中剩余的所有元素。
  486. * 注:Redis的列表起始索引为0。显然,LRANGE numbers 0 -1 可以获取列表中的所有元素。返回指定范围内元素的列表。
  487. * @Author: 小霍
  488. * @UpdateUser:
  489. * @Version: 0.0.1
  490. * @param key
  491. * @param start
  492. * @param end
  493. * @return java.util.List<java.lang.Object>
  494. *
  495. */
  496. public List<Object> lrange(String key, long start, long end) {
  497. if (null == key) {
  498. return null;
  499. }
  500. return redisTemplate.opsForList().range(key, start, end);
  501. }
  502. /**
  503. * @TODO : 让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
  504. * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
  505. * @Author: 小霍
  506. * @UpdateUser:
  507. * @Version: 0.0.1
  508. * @param key
  509. * @param start
  510. * @param end
  511. * @return
  512. *
  513. */
  514. public void ltrim(String key, long start, long end) {
  515. if (null == key) {
  516. return;
  517. }
  518. redisTemplate.opsForList().trim(key, start, end);
  519. }
  520. /**
  521. * @TODO : 该命令将返回链表中指定位置(index)的元素,index是0-based,表示从头部位置开始第index的元素,
  522. * 如果index为-1,表示尾部元素。如果与该Key关联的不是链表,该命令将返回相关的错误信息。 如果超出index返回这返回nil。
  523. * @Author: 小霍
  524. * @UpdateUser:
  525. * @Version: 0.0.1
  526. * @param key
  527. * @param index
  528. * @return java.lang.Object
  529. *
  530. */
  531. public Object lindex(String key, long index) {
  532. if (null == key) {
  533. return null;
  534. }
  535. return redisTemplate.opsForList().index(key, index);
  536. }
  537. /**
  538. * @TODO : 返回指定Key关联的链表中元素的数量,如果该Key不存在,则返回0。如果与该Key关联的Value的类型不是链表,则抛出相关的异常。
  539. * @Author: 小霍
  540. * @UpdateUser:
  541. * @Version: 0.0.1
  542. * @param key
  543. * @return java.lang.Long
  544. *
  545. */
  546. public Long llen(String key) {
  547. if (null == key) {
  548. return 0L;
  549. }
  550. return redisTemplate.opsForList().size(key);
  551. }
  552. // ***************Set数据类型*************
  553. /**
  554. * @TODO : 如果在插入的过程用,参数中有的成员在Set中已经存在,该成员将被忽略,而其它成员仍将会被正常插入。
  555. * 如果执行该命令之前,该Key并不存在,该命令将会创建一个新的Set,此后再将参数中的成员陆续插入。返回实际插入的成员数量。
  556. * @Author: 小霍
  557. * @UpdateUser:
  558. * @Version: 0.0.1
  559. * @param key
  560. * @param members 可以是一个String 也可以是一个String数组
  561. * @return java.lang.Long 添加成功的个数
  562. *
  563. */
  564. public Long sadd(String key, Object... members) {
  565. if (null == key) {
  566. return 0L;
  567. }
  568. return redisTemplate.opsForSet().add(key, members);
  569. }
  570. /**
  571. * @TODO : 返回Set中成员的数量,如果该Key并不存在,返回0。
  572. * @Author: 小霍
  573. * @UpdateUser:
  574. * @Version: 0.0.1
  575. * @param key
  576. * @return java.lang.Long
  577. *
  578. */
  579. public Long scard(String key) {
  580. if (null == key) {
  581. return 0L;
  582. }
  583. return redisTemplate.opsForSet().size(key);
  584. }
  585. /**
  586. * @TODO : 判断参数中指定成员是否已经存在于与Key相关联的Set集合中。返回true表示已经存在,false表示不存在,或该Key本身并不存在。
  587. * @Author: 小霍
  588. * @UpdateUser:
  589. * @Version: 0.0.1
  590. * @param key
  591. * @param member
  592. * @return java.lang.Boolean
  593. *
  594. */
  595. public Boolean sismember(String key, Object member) {
  596. if (null == key) {
  597. return false;
  598. }
  599. return redisTemplate.opsForSet().isMember(key, member);
  600. }
  601. /**
  602. * @TODO : 和SPOP一样,随机的返回Set中的一个成员,不同的是该命令并不会删除返回的成员。
  603. * @Author: 小霍
  604. * @UpdateUser:
  605. * @Version: 0.0.1
  606. * @param key
  607. * @return java.lang.String
  608. *
  609. */
  610. public Object srandmember(String key) {
  611. if (null == key) {
  612. return null;
  613. }
  614. return redisTemplate.opsForSet().randomMember(key);
  615. }
  616. /**
  617. * @TODO : 和SPOP一样,随机的返回Set中的一个成员,不同的是该命令并不会删除返回的成员。
  618. * 还可以传递count参数来一次随机获得多个元素,根据count的正负不同,具体表现也不同。 当count 为正数时,SRANDMEMBER
  619. * 会随机从集合里获得count个不重复的元素。 如果count的值大于集合中的元素个数,则SRANDMEMBER 会返回集合中的全部元素。
  620. * 当count为负数时,SRANDMEMBER 会随机从集合里获得|count|个的元素,如果|count|大与集合中的元素,
  621. * 就会返回全部元素不够的以重复元素补齐,如果key不存在则返回nil。
  622. * @Author: 小霍
  623. * @UpdateUser:
  624. * @Version: 0.0.1
  625. * @param key
  626. * @param count
  627. * @return java.util.List<java.lang.String>
  628. *
  629. */
  630. public List<Object> srandmember(String key, int count) {
  631. if (null == key) {
  632. return null;
  633. }
  634. return redisTemplate.opsForSet().randomMembers(key, count);
  635. }
  636. /**
  637. * @TODO : 通过key随机删除一个set中的value并返回该值
  638. * @Author: 小霍
  639. * @UpdateUser:
  640. * @Version: 0.0.1
  641. * @param key
  642. * @return java.lang.String
  643. *
  644. */
  645. public Object spop(String key) {
  646. if (null == key) {
  647. return null;
  648. }
  649. return redisTemplate.opsForSet().pop(key);
  650. }
  651. /**
  652. * @TODO : 通过key获取set中所有的value
  653. * @Author: 小霍
  654. * @UpdateUser:
  655. * @Version: 0.0.1
  656. * @param key
  657. * @return java.util.Set<java.lang.String>
  658. *
  659. */
  660. public Set<Object> smembers(String key) {
  661. if (null == key) {
  662. return null;
  663. }
  664. return redisTemplate.opsForSet().members(key);
  665. }
  666. /**
  667. * @TODO : 从与Key关联的Set中删除参数中指定的成员,不存在的参数成员将被忽略,
  668. * 如果该Key并不存在,将视为空Set处理。返回从Set中实际移除的成员数量,如果没有则返回0。
  669. * @Author: 小霍
  670. * @UpdateUser:
  671. * @Version: 0.0.1
  672. * @param key
  673. * @param members
  674. * @return java.lang.Long
  675. *
  676. */
  677. public Long srem(String key, Object... members) {
  678. if (null == key) {
  679. return 0L;
  680. }
  681. return redisTemplate.opsForSet().remove(key, members);
  682. }
  683. /**
  684. * @TODO : 将元素value从一个集合移到另一个集合
  685. * @Author: 小霍
  686. * @UpdateUser:
  687. * @Version: 0.0.1
  688. * @param srckey
  689. * @param dstkey
  690. * @param member
  691. * @return java.lang.Long
  692. *
  693. */
  694. public Boolean smove(String srckey, String dstkey, Object member) {
  695. if (null == srckey || null == dstkey) {
  696. return false;
  697. }
  698. return redisTemplate.opsForSet().move(srckey, member, dstkey);
  699. }
  700. /**
  701. * @TODO : 获取两个集合的并集
  702. * @Author: 小霍
  703. * @UpdateUser:
  704. * @Version: 0.0.1
  705. * @param key
  706. * @param otherKeys
  707. * @return java.util.Set<java.lang.Object> 返回两个集合合并值
  708. *
  709. */
  710. public Set<Object> sUnion(String key, String otherKeys) {
  711. if (null == key || otherKeys == null) {
  712. return null;
  713. }
  714. return redisTemplate.opsForSet().union(key, otherKeys);
  715. }
  716. // **********Sorted Set 数据类型********************
  717. /**
  718. * @TODO : 添加参数中指定的所有成员及其分数到指定key的Sorted Set中,在该命令中我们可以指定多组score/member作为参数。
  719. * 如果在添加时参数中的某一成员已经存在,该命令将更新此成员的分数为新值,同时再将该成员基于新值重新排序。
  720. * 如果键不存在,该命令将为该键创建一个新的Sorted Set Value,并将score/member对插入其中。
  721. * @Author: 小霍
  722. * @UpdateUser:
  723. * @Version: 0.0.1
  724. * @param key
  725. * @param score
  726. * @param member
  727. * @return java.lang.Long
  728. *
  729. */
  730. public Boolean zadd(String key, double score, Object member) {
  731. if (null == key) {
  732. return false;
  733. }
  734. return redisTemplate.opsForZSet().add(key, member, score);
  735. }
  736. /**
  737. * @TODO : 该命令将移除参数中指定的成员,其中不存在的成员将被忽略。 如果与该Key关联的Value不是Sorted Set,相应的错误信息将被返回。
  738. * 如果操作成功则返回实际被删除的成员数量。
  739. * @Author: 小霍
  740. * @UpdateUser:
  741. * @Version: 0.0.1
  742. * @param key
  743. * @param members 可以使一个string 也可以是一个string数组
  744. * @return java.lang.Long
  745. *
  746. */
  747. public Long zrem(String key, Object... members) {
  748. if (null == key || null == members) {
  749. return 0L;
  750. }
  751. return redisTemplate.opsForZSet().remove(key, members);
  752. }
  753. /**
  754. * @TODO : 返回Sorted Set中的成员数量,如果该Key不存在,返回0。
  755. * @Author: 小霍
  756. * @UpdateUser:
  757. * @Version: 0.0.1
  758. * @param key
  759. * @return java.lang.Long
  760. *
  761. */
  762. public Long zcard(String key) {
  763. if (null == key) {
  764. return 0L;
  765. }
  766. return redisTemplate.opsForZSet().size(key);
  767. }
  768. /**
  769. * @TODO : 该命令将为指定Key中的指定成员增加指定的分数。如果成员不存在,该命令将添加该成员并假设其初始分数为0,
  770. * 此后再将其分数加上increment。如果Key不存在,该命令将创建该Key及其关联的Sorted Set,
  771. * 并包含参数指定的成员,其分数为increment参数。如果与该Key关联的不是Sorted Set类型,
  772. * 相关的错误信息将被返回。如果不报错则以串形式表示的新分数。
  773. * @Author: 小霍
  774. * @UpdateUser:
  775. * @Version: 0.0.1
  776. * @param key
  777. * @param score
  778. * @param member
  779. * @return java.lang.Double
  780. *
  781. */
  782. public Double zincrby(String key, double score, Object member) {
  783. if (null == key) {
  784. throw new BusinessException(4001004, "key 不能为空");
  785. }
  786. return redisTemplate.opsForZSet().incrementScore(key, member, score);
  787. }
  788. /**
  789. * @TODO : 该命令用于获取分数(score)在min和max之间的成员数量。
  790. * (min=<score<=max)如果加上了“(”着表明是开区间例如zcount key (min max 则
  791. * 表示(min<score=<max) 同理zcount key min (max 则表明(min=<score<max) 返回指定返回数量。
  792. * @Author: 小霍
  793. * @UpdateUser:
  794. * @Version: 0.0.1
  795. * @param key
  796. * @param min
  797. * @param max
  798. * @return java.lang.Long
  799. *
  800. */
  801. public Long zcount(String key, double min, double max) {
  802. if (null == key) {
  803. return 0L;
  804. }
  805. return redisTemplate.opsForZSet().count(key, min, max);
  806. }
  807. /**
  808. * @TODO : Sorted Set中的成员都是按照分数从低到高的顺序存储,该命令将返回参数中指定成员的位置值, 其中0表示第一个成员,它是Sorted
  809. * Set中分数最低的成员。 如果该成员存在,则返回它的位置索引值。否则返回nil。
  810. * @Author: 小霍
  811. * @UpdateUser:
  812. * @Version: 0.0.1
  813. * @param key
  814. * @param member
  815. * @return java.lang.Long
  816. *
  817. */
  818. public Long zrank(String key, Object member) {
  819. if (null == key) {
  820. return null;
  821. }
  822. return redisTemplate.opsForZSet().rank(key, member);
  823. }
  824. /**
  825. * @TODO : 如果该成员存在,以字符串的形式返回其分数,否则返回null
  826. * @Author: 小霍
  827. * @UpdateUser:
  828. * @Version: 0.0.1
  829. * @param key
  830. * @param member
  831. * @return java.lang.Double
  832. *
  833. */
  834. public Double zscore(String key, Object member) {
  835. if (null == key) {
  836. return null;
  837. }
  838. return redisTemplate.opsForZSet().score(key, member);
  839. }
  840. /**
  841. * @TODO :
  842. * 该命令返回顺序在参数start和stop指定范围内的成员,这里start和stop参数都是0-based,即0表示第一个成员,-1表示最后一个成员。如果start大于该Sorted
  843. * Set中的最大索引值,或start > stop,此时一个空集合将被返回。如果stop大于最大索引值,
  844. * 该命令将返回从start到集合的最后一个成员。如果命令中带有可选参数WITHSCORES选项,
  845. * 该命令在返回的结果中将包含每个成员的分数值,如value1,score1,value2,score2...。
  846. * @Author: 小霍
  847. * @UpdateUser:
  848. * @Version: 0.0.1
  849. * @param key
  850. * @param min
  851. * @param max
  852. * @return java.util.Set<java.lang.String> 指定区间内的有序集成员的列表。
  853. *
  854. */
  855. public Set<Object> zrange(String key, long min, long max) {
  856. if (null == key) {
  857. return null;
  858. }
  859. return redisTemplate.opsForZSet().range(key, min, max);
  860. }
  861. /**
  862. * @TODO : 该命令的功能和ZRANGE基本相同,唯一的差别在于该命令是通过反向排序获取指定位置的成员,
  863. * 即从高到低的顺序。如果成员具有相同的分数,则按降序字典顺序排序。
  864. * @Author: 小霍
  865. * @UpdateUser:
  866. * @Version: 0.0.1
  867. * @param key
  868. * @param start
  869. * @param end
  870. * @return java.util.Set<java.lang.String>
  871. *
  872. */
  873. public Set<Object> zReverseRange(String key, long start, long end) {
  874. if (null == key) {
  875. return null;
  876. }
  877. return redisTemplate.opsForZSet().reverseRange(key, start, end);
  878. }
  879. /**
  880. * @TODO : 该命令将返回分数在min和max之间的所有成员,即满足表达式min <= score <= max的成员,
  881. * 其中返回的成员是按照其分数从低到高的顺序返回,如果成员具有相同的分数, 则按成员的字典顺序返回。可选参数LIMIT用于限制返回成员的数量范围。
  882. * 可选参数offset表示从符合条件的第offset个成员开始返回,同时返回count个成员。
  883. * 可选参数WITHSCORES的含义参照ZRANGE中该选项的说明。*最后需要说明的是参数中min和max的规则可参照命令ZCOUNT。
  884. * @Author: 小霍
  885. * @UpdateUser:
  886. * @Version: 0.0.1
  887. * @param key
  888. * @param max
  889. * @param min
  890. * @return java.util.Set<java.lang.String>
  891. *
  892. */
  893. public Set<Object> zrangebyscore(String key, double min, double max) {
  894. if (null == key) {
  895. return null;
  896. }
  897. return redisTemplate.opsForZSet().rangeByScore(key, min, max);
  898. }
  899. /**
  900. * @TODO : 该命令除了排序方式是基于从高到低的分数排序之外,其它功能和参数含义均与ZRANGEBYSCORE相同。
  901. * 需要注意的是该命令中的min和max参数的顺序和ZRANGEBYSCORE命令是相反的。
  902. * @Author: 小霍
  903. * @UpdateUser:
  904. * @Version: 0.0.1
  905. * @param key
  906. * @param max
  907. * @param min
  908. * @return java.util.Set<java.lang.String>
  909. *
  910. */
  911. public Set<Object> zrevrangeByScore(String key, double min, double max) {
  912. if (null == key) {
  913. return null;
  914. }
  915. return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
  916. }
  917. }

4. Spring Boot+RedisTemplate工具类封装自定义序列化方式

Spring Boot 提供了对 Redis 集成的组件包:spring-boot-starter-data-redis,它依赖于 spring-data-redis 和lettuce。Spring Boot
1.0 默认使用的是 Jedis 客户端,2.0 替换成了 Lettuce,但如果你从 Spring Boot 1.5.X切换过来,几乎感受不大差异,这是因为
spring-boot-starter-data-redis 为我们隔离了其中的差异性

  • Lettuce:是一个可伸缩线程安全的 Redis 客户端,多个线程可以共享同一个 RedisConnection,它利用优秀 Netty NIO 框架来
    高效地管理多个连接。
  • Spring Data:是 Spring 框架中的一个主要项目,目的是为了简化构建基于 Spring 框架应用的数据访问,包括非关系数据库、
    Map-Reduce 框架、云数据服务等,另外也包含对关系数据库的访问支持。
  • Spring Data Redis:是 Spring Data 项目中的一个主要模块,供了一个高度封装的“RedisTemplate”类实现了对 Redis 客户端
    API 的高度封装,使得对 Redis 的操作更加便捷而且Spring data redis 的连接池可以自动管理,针对数据的“序列化/反序列
    化”,提供了多种可选择策略(RedisSerializer)等等…。

RedisTemplate中定义了对应redis 5种数据结构操作

  • opsForValue();//操作字符串
  • opsForHash();//操作hash
  • opsForList();//操作list
  • opsForSet();//操作set
  • opsForZSet();//操作有序set

1. 引入依赖
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.apache.commons</groupId>
  7. <artifactId>commons-pool2</artifactId>
  8. </dependency>

引入 commons-pool2是因为Lettuce需要使用 commons-pool2 创建连接池

2. 加入配置信息
  1. # Redis 服务器地址
  2. spring.redis.host=localhost
  3. # Redis 服务器连接端⼝
  4. spring.redis.port=6379
  5. # 连接池最大连接数(使用负值表示没有限制) 默认 8
  6. spring.redis.lettuce.pool.max-active=100
  7. # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
  8. spring.redis.lettuce.pool.max-wait=PT10S
  9. # 连接池中的最大空闲连接 默认 8
  10. spring.redis.lettuce.pool.max-idle=30
  11. # 连接池中的最小空闲连接 默认 0
  12. spring.redis.lettuce.pool.min-idle=1
  13. #链接超时时间
  14. spring.redis.timeout=PT10S

在平常的开发中我们可以中通过spring的注入注解获取了RedisTemplate对象的引用。

  1. @Autowired
  2. private RedisTemplate<String,Object> redisTemplate;
  3. 或者
  4. @Resource
  5. private RedisTemplate<String,Object> redisTemplate;

StringRedisTemplate && RedisTemplate 介绍

  • 两者的关系是StringRedisTemplate继承RedisTemplate。
  • RedisTemplate是一个泛型类,而StringRedisTemplate则不是。
  • StringRedisTemplate只能对key=String,value=String的键值对进行操作,RedisTemplate可以对任何类型的key-value键值对
    操作。
  • 他们各自序列化的方式不同,但最终都是得到了一个字节数组,殊途同归,StringRedisTemplate使用的是
    StringRedisSerializer类;RedisTemplate使用的是JdkSerializationRedisSerializer类。反序列化,则是一个得到String,一个
    得到Object

    另外针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)

    • JdkSerializationRedisSerializer:这个序列化方法就是Jdk提供的了。首先要求我们要被序列化的类继承自Serializeable
      接口,然后通过,然后通过Jdk对象序列化的方法保存。(注:这个序列化保存的对象,即使是个String类型的,在redis
      控制台,也是看不出来的,因为它保存了一些对象的类型什么的额外信息)。是目前最常用的序列化策略。
    • StringRedisSerializer:就是通过String.getBytes()来实现的。而且在Redis中,所有存储的值都是字符串类型的。所以这
      种方法保存后,通过Redis-cli控制台,是可以清楚的查看到我们保存了什么key,value是什么。是最轻量级和高效的策
    • JacksonJsonRedisSerializer:jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json
      格式存储在redis中,也可以将json格式的数据转换成pojo实例。因为jackson工具在序列化和反序列化时,需要明确指定
      Class类型,因此策略封装起来稍微复杂。

RedisTemplate在操作数据的时候,存入数据会将数据先序列化成字节数组然后在存入Redis数据库(默认
JdkSerializationRedisSerializer:这个序列化方法就是Jdk提供的了,首先要求我们要被序列化的类继承自Serializeable接口,然后通过
Jdk对象序列化的方法保存),这个时候打开Redis查看的时候,你会看到你的数据不是以可读的形式,展现的,而是以字节数组显示,

而StringRedisSerializer在操作数据的时候就是通过String.getBytes()来实现的。而且在Redis中,所有存储的值都是字符串类型的。所
以这种方法保存后,通过Redis-cli控制台或者Redis-Desktop-Manager图形化工具,是可以清楚的查看到我们保存了什么key,value是
什么。

这样就会出现问题,如当我们从以前的项目升级为RedisTemplate在不指定序列化方式的时候取不到原来的值。

  1. @Autowired
  2. private RedisTemplate redisTemplate;
  3. @Test
  4. public void testRedisTemplate() {
  5. System.out.println(redisTemplate.opsForValue().get("username"));
  6. }
  7. // 输出:null

3. 自定义 redis 序列化工具类
  1. /**
  2. * @TODO:类文件简单描述
  3. * @Version: 0.0.1
  4. */
  5. public class MyStringRedisSerializer implements RedisSerializer<Object> {
  6. private final Charset charset;
  7. public MyStringRedisSerializer() {
  8. this(StandardCharsets.UTF_8);
  9. }
  10. public MyStringRedisSerializer(Charset charset) {
  11. Assert.notNull(charset, "Charset must not be null!");
  12. this.charset = charset;
  13. }
  14. @Override
  15. public String deserialize(byte[] bytes) {
  16. return (bytes == null ? null : new String(bytes, charset));
  17. }
  18. @Override
  19. public byte[] serialize(Object object) {
  20. if (object == null) {
  21. return new byte[0];
  22. }
  23. if (object instanceof String) {
  24. return object.toString().getBytes(charset);
  25. } else {
  26. String string = JSON.toJSONString(object);
  27. return string.getBytes(charset);
  28. }
  29. }
  30. }

4. 创建 RedisConfig 配置类
  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.data.redis.connection.RedisConnectionFactory;
  4. import org.springframework.data.redis.core.RedisTemplate;
  5. import org.springframework.data.redis.serializer.StringRedisSerializer;
  6. import java.net.UnknownHostException;
  7. /**
  8. * @TODO:类文件简单描述
  9. * @Version: 0.0.1
  10. */
  11. @SuppressWarnings("all")
  12. @Configuration
  13. public class RedisConfig {
  14. @Bean
  15. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
  16. RedisTemplate<String, Object> template = new RedisTemplate();
  17. template.setConnectionFactory(redisConnectionFactory);
  18. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  19. MyStringRedisSerializer myStringRedisSerializer = new MyStringRedisSerializer();
  20. template.setKeySerializer(stringRedisSerializer);
  21. template.setValueSerializer(myStringRedisSerializer);
  22. template.setHashKeySerializer(stringRedisSerializer);
  23. template.setHashValueSerializer(myStringRedisSerializer);
  24. return template;
  25. }
  26. }

5. 创建 RedisService 类
  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.data.redis.core.RedisTemplate;
  3. /**
  4. * @TODO:类文件简单描述
  5. * @Author: 小霍
  6. * @Version: 0.0.1
  7. */
  8. public class RedisService {
  9. @Autowired
  10. private RedisTemplate<String, Object> redisTemplate;
  11. }

6. 创建自定义运行时异常
  1. /**
  2. * @TODO:类文件简单描述
  3. * @Version: 0.0.1
  4. */
  5. @SuppressWarnings("all")
  6. public class BusinessException extends RuntimeException {
  7. /**
  8. * 异常编号
  9. */
  10. private final int messageCode;
  11. /**
  12. * 对messageCode 异常信息进行补充说明
  13. */
  14. private final String detailMessage;
  15. public BusinessException(int messageCode, String message) {
  16. super(message);
  17. this.messageCode = messageCode;
  18. this.detailMessage = message;
  19. }
  20. public int getMessageCode() {
  21. return messageCode;
  22. }
  23. public String getDetailMessage() {
  24. return detailMessage;
  25. }
  26. }

7. RedisTemplate工具类封装自定义序列化方式

修改RedisService 加入如下代码

  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.data.redis.core.RedisTemplate;
  3. import org.springframework.stereotype.Service;
  4. import com.example.demo.exception.BusinessException;
  5. import java.util.*;
  6. import java.util.concurrent.TimeUnit;
  7. /**
  8. * @TODO:类文件简单描述
  9. * @Version: 0.0.1
  10. */
  11. @Service
  12. public class RedisService {
  13. @Autowired
  14. private RedisTemplate<String, Object> redisTemplate;
  15. /** -------------------key相关操作--------------------- */
  16. /**
  17. * @TODO : 是否存在key
  18. * @Author: 小霍
  19. * @UpdateUser:
  20. * @Version: 0.0.1
  21. * @param key
  22. * @return java.lang.Boolean
  23. *
  24. */
  25. public Boolean hasKey(String key) {
  26. if (null == key) {
  27. return false;
  28. }
  29. return redisTemplate.hasKey(key);
  30. }
  31. /**
  32. * @TODO : 删除key
  33. * @Author: 小霍
  34. * @UpdateUser:
  35. * @Version: 0.0.1
  36. * @param key
  37. * @return Boolean 成功返回true 失败返回false
  38. *
  39. */
  40. public Boolean delete(String key) {
  41. if (null == key) {
  42. return false;
  43. }
  44. return redisTemplate.delete(key);
  45. }
  46. /**
  47. * @TODO : 批量删除key
  48. * @Author: 小霍
  49. * @CreateDate: 2019/8/27 20:27
  50. * @UpdateUser:
  51. * @UpdateDate: 2019/8/27 20:27
  52. * @Version: 0.0.1
  53. * @param keys
  54. * @return Long 返回成功删除key的数量
  55. *
  56. */
  57. public Long delete(Collection<String> keys) {
  58. return redisTemplate.delete(keys);
  59. }
  60. /**
  61. * @TODO : 设置过期时间
  62. * @Author: 小霍
  63. * @UpdateUser:
  64. * @Version: 0.0.1
  65. * @param key
  66. * @param timeout
  67. * @param unit
  68. * @return java.lang.Boolean
  69. *
  70. */
  71. public Boolean expire(String key, long timeout, TimeUnit unit) {
  72. if (null == key || null == unit) {
  73. return false;
  74. }
  75. return redisTemplate.expire(key, timeout, unit);
  76. }
  77. /**
  78. * @TODO : 查找匹配的key
  79. * @Author: 小霍
  80. * @UpdateUser:
  81. * @Version: 0.0.1
  82. * @param pattern
  83. * @return java.util.Set<java.lang.String>
  84. *
  85. */
  86. public Set<String> keys(String pattern) {
  87. if (null == pattern) {
  88. return null;
  89. }
  90. return redisTemplate.keys(pattern);
  91. }
  92. /**
  93. * @TODO : 移除 key 的过期时间,key 将持久保持
  94. * @Author: 小霍
  95. * @UpdateUser:
  96. * @Version: 0.0.1
  97. * @param key
  98. * @return java.lang.Boolean
  99. *
  100. */
  101. public Boolean persist(String key) {
  102. if (null == key) {
  103. return false;
  104. }
  105. return redisTemplate.persist(key);
  106. }
  107. /**
  108. * @TODO : 返回 key 的剩余的过期时间
  109. * @Author: 小霍
  110. * @UpdateUser:
  111. * @Version: 0.0.1
  112. * @param key
  113. * @param unit
  114. * @return java.lang.Long 当 key 不存在时,返回 -2 。当 key 存在但没有设置剩余生存时间时,返回 -1
  115. * 。否则,以秒为单位,返回 key的剩余生存时间
  116. *
  117. */
  118. public Long getExpire(String key, TimeUnit unit) {
  119. if (null == key || null == unit) {
  120. throw new BusinessException(4001004, "key or TomeUnit 不能为空");
  121. }
  122. return redisTemplate.getExpire(key, unit);
  123. }
  124. // *************String相关数据类型***************************
  125. /**
  126. * @TODO : 设置指定 key 的值
  127. * @Author: 小霍
  128. * @UpdateUser:
  129. * @Version: 0.0.1
  130. * @param key
  131. * @param value
  132. * @return void
  133. *
  134. */
  135. public void set(String key, Object value) {
  136. if (null == key || null == value) {
  137. return;
  138. }
  139. redisTemplate.opsForValue().set(key, value);
  140. }
  141. /**
  142. * @TODO : 设置key 的值 并设置过期时间
  143. * @Author: 小霍
  144. * @UpdateUser:
  145. * @Version: 0.0.1
  146. * @param key
  147. * @param value
  148. * @param time
  149. * @param unit
  150. * @return void
  151. *
  152. */
  153. public void set(String key, Object value, long time, TimeUnit unit) {
  154. if (null == key || null == value || null == unit) {
  155. return;
  156. }
  157. redisTemplate.opsForValue().set(key, value, time, unit);
  158. }
  159. /**
  160. * @TODO : 设置key 的值 并设置过期时间 key存在 不做操作返回false key不存在设置值返回true
  161. * @Author: 小霍
  162. * @UpdateUser:
  163. * @Version: 0.0.1
  164. * @param key
  165. * @param value
  166. * @param time
  167. * @param unit
  168. * @return java.lang.Boolean
  169. *
  170. */
  171. public Boolean setifAbsen(String key, Object value, long time, TimeUnit unit) {
  172. if (null == key || null == value || null == unit) {
  173. throw new BusinessException(4001004, "kkey、value、unit都不能为空");
  174. }
  175. return redisTemplate.opsForValue().setIfAbsent(key, value, time, unit);
  176. }
  177. /**
  178. * @TODO : 获取指定Key的Value。如果与该Key关联的Value不是string类型,Redis将抛出异常,
  179. * 因为GET命令只能用于获取string Value,如果该Key不存在,返回null
  180. * @Author: 小霍
  181. * @UpdateUser:
  182. * @Version: 0.0.1
  183. * @param key
  184. * @return java.lang.Object
  185. *
  186. */
  187. public Object get(String key) {
  188. if (null == key) {
  189. return null;
  190. }
  191. return redisTemplate.opsForValue().get(key);
  192. }
  193. /**
  194. * @TODO : 很明显先get再set就说先获取key值对应的value然后再set 新的value 值。
  195. * @Author: 小霍
  196. * @UpdateUser:
  197. * @Version: 0.0.1
  198. * @param key
  199. * @param value
  200. * @return java.lang.Object
  201. *
  202. */
  203. public Object getSet(String key, Object value) {
  204. if (null == key) {
  205. return null;
  206. }
  207. return redisTemplate.opsForValue().getAndSet(key, value);
  208. }
  209. /**
  210. * @TODO : 通过批量的key获取批量的value
  211. * @Author: 小霍
  212. * @UpdateUser:
  213. * @Version: 0.0.1
  214. * @param keys
  215. * @return java.util.List<java.lang.Object>
  216. *
  217. */
  218. public List<Object> mget(Collection<String> keys) {
  219. if (null == keys) {
  220. return Collections.emptyList();
  221. }
  222. return redisTemplate.opsForValue().multiGet(keys);
  223. }
  224. /**
  225. * @TODO : 将指定Key的Value原子性的增加increment。如果该Key不存在,其初始值为0,在incrby之后其值为increment。
  226. * 如果Value的值不能转换为整型值,如Hi,该操作将执行失败并抛出相应异常。操作成功则返回增加后的value值。
  227. * @Author: 小霍
  228. * @UpdateUser:
  229. * @Version: 0.0.1
  230. * @param key
  231. * @param increment
  232. * @return long
  233. *
  234. */
  235. public long incrby(String key, long increment) {
  236. if (null == key) {
  237. throw new BusinessException(4001004, "key不能为空");
  238. }
  239. return redisTemplate.opsForValue().increment(key, increment);
  240. }
  241. /**
  242. *
  243. * @TODO : 将指定Key的Value原子性的减少decrement。如果该Key不存在,其初始值为0,
  244. * 在decrby之后其值为-decrement。如果Value的值不能转换为整型值,
  245. * 如Hi,该操作将执行失败并抛出相应的异常。操作成功则返回减少后的value值。
  246. * @Author: 小霍
  247. * @UpdateUser:
  248. * @Version: 0.0.1
  249. * @param key
  250. * @param decrement
  251. * @return java.lang.Long
  252. *
  253. */
  254. public Long decrby(String key, long decrement) {
  255. if (null == key) {
  256. throw new BusinessException(4001004, "key不能为空");
  257. }
  258. return redisTemplate.opsForValue().decrement(key, decrement);
  259. }
  260. /**
  261. * @TODO : 如果该Key已经存在,APPEND命令将参数Value的数据追加到已存在Value的末尾。如果该Key不存在,
  262. * APPEND命令将会创建一个新的Key/Value。返回追加后Value的字符串长度。
  263. * @Author: 小霍
  264. * @UpdateUser:
  265. * @Version: 0.0.1
  266. * @param key
  267. * @param value
  268. * @return java.lang.Integer
  269. *
  270. */
  271. public Integer append(String key, String value) {
  272. if (key == null) {
  273. throw new BusinessException(4001004, "key不能为空");
  274. }
  275. return redisTemplate.opsForValue().append(key, value);
  276. }
  277. //******************hash数据类型*********************
  278. /**
  279. * @TODO : 通过key 和 field 获取指定的 value
  280. * @Author: 小霍
  281. * @UpdateUser:
  282. * @Version: 0.0.1
  283. * @param key
  284. * @param field
  285. * @return java.lang.Object
  286. *
  287. */
  288. public Object hget(String key, Object field) {
  289. if (null == key || null == field) {
  290. return null;
  291. }
  292. return redisTemplate.opsForHash().get(key, field);
  293. }
  294. /**
  295. * @TODO : 为指定的Key设定Field/Value对,如果Key不存在,该命令将创建新Key以用于存储参数中的Field/Value对,
  296. * 如果参数中的Field在该Key中已经存在,则用新值覆盖其原有值。
  297. * 返回1表示新的Field被设置了新值,0表示Field已经存在,用新值覆盖原有值。
  298. * @Author: 小霍
  299. * @UpdateUser:
  300. * @Version: 0.0.1
  301. * @param key
  302. * @param field
  303. * @param value
  304. * @return
  305. *
  306. */
  307. public void hset(String key, Object field, Object value) {
  308. if (null == key || null == field) {
  309. return;
  310. }
  311. redisTemplate.opsForHash().put(key, field, value);
  312. }
  313. /**
  314. * @TODO : 判断指定Key中的指定Field是否存在,返回true表示存在,false表示参数中的Field或Key不存在。
  315. * @Author: 小霍
  316. * @UpdateUser:
  317. * @Version: 0.0.1
  318. * @param key
  319. * @param field
  320. * @return java.lang.Boolean
  321. *
  322. */
  323. public Boolean hexists(String key, Object field) {
  324. if (null == key || null == field) {
  325. return false;
  326. }
  327. return redisTemplate.opsForHash().hasKey(key, field);
  328. }
  329. /**
  330. * @TODO : 从指定Key的Hashes Value中删除参数中指定的多个字段,如果不存在的字段将被忽略,
  331. * 返回实际删除的Field数量。如果Key不存在,则将其视为空Hashes,并返回0。
  332. * @Author: 小霍
  333. * @UpdateUser:
  334. * @Version: 0.0.1
  335. * @param key
  336. * @param fields
  337. * @return java.lang.Long
  338. *
  339. */
  340. public Long hdel(String key, Object... fields) {
  341. if (null == key || null == fields || fields.length == 0) {
  342. return 0L;
  343. }
  344. return redisTemplate.opsForHash().delete(key, fields);
  345. }
  346. /**
  347. * @TODO : 通过key获取所有的field和value
  348. * @Author: 小霍
  349. * @UpdateUser:
  350. * @Version: 0.0.1
  351. * @param key
  352. * @return java.util.Map<java.lang.Object,java.lang.Object>
  353. *
  354. */
  355. public Map<Object, Object> hgetall(String key) {
  356. if (key == null) {
  357. return null;
  358. }
  359. return redisTemplate.opsForHash().entries(key);
  360. }
  361. /**
  362. * @TODO : 逐对依次设置参数中给出的Field/Value对。如果其中某个Field已经存在,则用新值覆盖原有值。
  363. * 如果Key不存在,则创建新Key,同时设定参数中的Field/Value。
  364. * @Author: 小霍
  365. * @UpdateUser:
  366. * @Version: 0.0.1
  367. * @param key
  368. * @param hash
  369. * @return
  370. *
  371. */
  372. public void hmset(String key, Map<String, Object> hash) {
  373. if (null == key || null == hash) {
  374. return;
  375. }
  376. redisTemplate.opsForHash().putAll(key, hash);
  377. }
  378. /**
  379. * @TODO : 获取和参数中指定Fields关联的一组Values,其返回顺序等同于Fields的请求顺序。
  380. * 如果请求的Field不存在,其值对应的value为null。
  381. * @Author: 小霍
  382. * @UpdateUser:
  383. * @Version: 0.0.1
  384. * @param key
  385. * @param fields
  386. * @return java.util.List<java.lang.Object>
  387. *
  388. */
  389. public List<Object> hmget(String key, Collection<Object> fields) {
  390. if (null == key || null == fields) {
  391. return null;
  392. }
  393. return redisTemplate.opsForHash().multiGet(key, fields);
  394. }
  395. /**
  396. * @TODO : 对应key的字段自增相应的值
  397. * @Author: 小霍
  398. * @UpdateUser:
  399. * @Version: 0.0.1
  400. * @param key
  401. * @param field
  402. * @param increment
  403. * @return java.lang.Long
  404. *
  405. */
  406. public Long hIncrBy(String key, Object field, long increment) {
  407. if (null == key || null == field) {
  408. throw new BusinessException(4001004, "key or field 不能为空");
  409. }
  410. return redisTemplate.opsForHash().increment(key, field, increment);
  411. }
  412. // ***************List数据类型***************
  413. /**
  414. * @TODO : 向列表左边添加元素。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的头部插入。
  415. * 如果该键的Value不是链表类型,该命令将将会抛出相关异常。操作成功则返回插入后链表中元素的数量。
  416. * @Author: 小霍
  417. * @UpdateUser:
  418. * @Version: 0.0.1
  419. * @param key
  420. * @param strs 可以使一个string 也可以使string数组
  421. * @return java.lang.Long 返回操作的value个数
  422. *
  423. */
  424. public Long lpush(String key, Object... strs) {
  425. if (null == key) {
  426. return 0L;
  427. }
  428. return redisTemplate.opsForList().leftPushAll(key, strs);
  429. }
  430. /**
  431. * @TODO : 向列表右边添加元素。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的尾部插入。
  432. * 如果该键的Value不是链表类型,该命令将将会抛出相关异常。操作成功则返回插入后链表中元素的数量。
  433. * @Author: 小霍
  434. * @UpdateUser:
  435. * @Version: 0.0.1
  436. * @param key
  437. * @param strs 可以使一个string 也可以使string数组
  438. * @return java.lang.Long 返回操作的value个数
  439. *
  440. */
  441. public Long rpush(String key, Object... strs) {
  442. if (null == key) {
  443. return 0L;
  444. }
  445. return redisTemplate.opsForList().rightPushAll(key, strs);
  446. }
  447. /**
  448. * @TODO : 返回并弹出指定Key关联的链表中的第一个元素,即头部元素。如果该Key不存在,
  449. * 返回nil。LPOP命令执行两步操作:第一步是将列表左边的元素从列表中移除,第二步是返回被移除的元素值。
  450. * @Author: 小霍
  451. * @UpdateUser:
  452. * @Version: 0.0.1
  453. * @param key
  454. * @return java.lang.Object
  455. *
  456. */
  457. public Object lpop(String key) {
  458. if (null == key) {
  459. return null;
  460. }
  461. return redisTemplate.opsForList().leftPop(key);
  462. }
  463. /**
  464. * @TODO : 返回并弹出指定Key关联的链表中的最后一个元素,即头部元素。如果该Key不存在,返回nil。
  465. * RPOP命令执行两步操作:第一步是将列表右边的元素从列表中移除,第二步是返回被移除的元素值。
  466. * @Author: 小霍
  467. * @UpdateUser:
  468. * @Version: 0.0.1
  469. * @param key
  470. * @return java.lang.Object
  471. *
  472. */
  473. public Object rpop(String key) {
  474. if (null == key) {
  475. return null;
  476. }
  477. return redisTemplate.opsForList().rightPop(key);
  478. }
  479. /**
  480. * @TODO : 该命令的参数start和end都是0-based。即0表示链表头部(leftmost)的第一个元素。
  481. * 其中start的值也可以为负值,-1将表示链表中的最后一个元素,即尾部元素,-2表示倒数第二个并以此类推。
  482. * 该命令在获取元素时,start和end位置上的元素也会被取出。如果start的值大于链表中元素的数量,
  483. * 空链表将会被返回。如果end的值大于元素的数量,该命令则获取从start(包括start)开始,链表中剩余的所有元素。
  484. * 注:Redis的列表起始索引为0。显然,LRANGE numbers 0 -1 可以获取列表中的所有元素。返回指定范围内元素的列表。
  485. * @Author: 小霍
  486. * @UpdateUser:
  487. * @Version: 0.0.1
  488. * @param key
  489. * @param start
  490. * @param end
  491. * @return java.util.List<java.lang.Object>
  492. *
  493. */
  494. public List<Object> lrange(String key, long start, long end) {
  495. if (null == key) {
  496. return null;
  497. }
  498. return redisTemplate.opsForList().range(key, start, end);
  499. }
  500. /**
  501. * @TODO : 让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
  502. * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
  503. * @Author: 小霍
  504. * @UpdateUser:
  505. * @Version: 0.0.1
  506. * @param key
  507. * @param start
  508. * @param end
  509. * @return
  510. *
  511. */
  512. public void ltrim(String key, long start, long end) {
  513. if (null == key) {
  514. return;
  515. }
  516. redisTemplate.opsForList().trim(key, start, end);
  517. }
  518. /**
  519. * @TODO : 该命令将返回链表中指定位置(index)的元素,index是0-based,表示从头部位置开始第index的元素,
  520. * 如果index为-1,表示尾部元素。如果与该Key关联的不是链表,该命令将返回相关的错误信息。 如果超出index返回这返回nil。
  521. * @Author: 小霍
  522. * @UpdateUser:
  523. * @Version: 0.0.1
  524. * @param key
  525. * @param index
  526. * @return java.lang.Object
  527. *
  528. */
  529. public Object lindex(String key, long index) {
  530. if (null == key) {
  531. return null;
  532. }
  533. return redisTemplate.opsForList().index(key, index);
  534. }
  535. /**
  536. * @TODO : 返回指定Key关联的链表中元素的数量,如果该Key不存在,则返回0。如果与该Key关联的Value的类型不是链表,则抛出相关的异常。
  537. * @Author: 小霍
  538. * @UpdateUser:
  539. * @Version: 0.0.1
  540. * @param key
  541. * @return java.lang.Long
  542. *
  543. */
  544. public Long llen(String key) {
  545. if (null == key) {
  546. return 0L;
  547. }
  548. return redisTemplate.opsForList().size(key);
  549. }
  550. // ***************Set数据类型*************
  551. /**
  552. * @TODO : 如果在插入的过程用,参数中有的成员在Set中已经存在,该成员将被忽略,而其它成员仍将会被正常插入。
  553. * 如果执行该命令之前,该Key并不存在,该命令将会创建一个新的Set,此后再将参数中的成员陆续插入。返回实际插入的成员数量。
  554. * @Author: 小霍
  555. * @UpdateUser:
  556. * @Version: 0.0.1
  557. * @param key
  558. * @param members 可以是一个String 也可以是一个String数组
  559. * @return java.lang.Long 添加成功的个数
  560. *
  561. */
  562. public Long sadd(String key, Object... members) {
  563. if (null == key) {
  564. return 0L;
  565. }
  566. return redisTemplate.opsForSet().add(key, members);
  567. }
  568. /**
  569. * @TODO : 返回Set中成员的数量,如果该Key并不存在,返回0。
  570. * @Author: 小霍
  571. * @UpdateUser:
  572. * @Version: 0.0.1
  573. * @param key
  574. * @return java.lang.Long
  575. *
  576. */
  577. public Long scard(String key) {
  578. if (null == key) {
  579. return 0L;
  580. }
  581. return redisTemplate.opsForSet().size(key);
  582. }
  583. /**
  584. * @TODO : 判断参数中指定成员是否已经存在于与Key相关联的Set集合中。返回true表示已经存在,false表示不存在,或该Key本身并不存在。
  585. * @Author: 小霍
  586. * @UpdateUser:
  587. * @Version: 0.0.1
  588. * @param key
  589. * @param member
  590. * @return java.lang.Boolean
  591. *
  592. */
  593. public Boolean sismember(String key, Object member) {
  594. if (null == key) {
  595. return false;
  596. }
  597. return redisTemplate.opsForSet().isMember(key, member);
  598. }
  599. /**
  600. * @TODO : 和SPOP一样,随机的返回Set中的一个成员,不同的是该命令并不会删除返回的成员。
  601. * @Author: 小霍
  602. * @UpdateUser:
  603. * @Version: 0.0.1
  604. * @param key
  605. * @return java.lang.String
  606. *
  607. */
  608. public Object srandmember(String key) {
  609. if (null == key) {
  610. return null;
  611. }
  612. return redisTemplate.opsForSet().randomMember(key);
  613. }
  614. /**
  615. * @TODO : 和SPOP一样,随机的返回Set中的一个成员,不同的是该命令并不会删除返回的成员。
  616. * 还可以传递count参数来一次随机获得多个元素,根据count的正负不同,具体表现也不同。 当count 为正数时,SRANDMEMBER
  617. * 会随机从集合里获得count个不重复的元素。 如果count的值大于集合中的元素个数,则SRANDMEMBER 会返回集合中的全部元素。
  618. * 当count为负数时,SRANDMEMBER 会随机从集合里获得|count|个的元素,如果|count|大与集合中的元素,
  619. * 就会返回全部元素不够的以重复元素补齐,如果key不存在则返回nil。
  620. * @Author: 小霍
  621. * @UpdateUser:
  622. * @Version: 0.0.1
  623. * @param key
  624. * @param count
  625. * @return java.util.List<java.lang.String>
  626. *
  627. */
  628. public List<Object> srandmember(String key, int count) {
  629. if (null == key) {
  630. return null;
  631. }
  632. return redisTemplate.opsForSet().randomMembers(key, count);
  633. }
  634. /**
  635. * @TODO : 通过key随机删除一个set中的value并返回该值
  636. * @Author: 小霍
  637. * @UpdateUser:
  638. * @Version: 0.0.1
  639. * @param key
  640. * @return java.lang.String
  641. *
  642. */
  643. public Object spop(String key) {
  644. if (null == key) {
  645. return null;
  646. }
  647. return redisTemplate.opsForSet().pop(key);
  648. }
  649. /**
  650. * @TODO : 通过key获取set中所有的value
  651. * @Author: 小霍
  652. * @UpdateUser:
  653. * @Version: 0.0.1
  654. * @param key
  655. * @return java.util.Set<java.lang.String>
  656. *
  657. */
  658. public Set<Object> smembers(String key) {
  659. if (null == key) {
  660. return null;
  661. }
  662. return redisTemplate.opsForSet().members(key);
  663. }
  664. /**
  665. * @TODO : 从与Key关联的Set中删除参数中指定的成员,不存在的参数成员将被忽略,
  666. * 如果该Key并不存在,将视为空Set处理。返回从Set中实际移除的成员数量,如果没有则返回0。
  667. * @Author: 小霍
  668. * @UpdateUser:
  669. * @Version: 0.0.1
  670. * @param key
  671. * @param members
  672. * @return java.lang.Long
  673. *
  674. */
  675. public Long srem(String key, Object... members) {
  676. if (null == key) {
  677. return 0L;
  678. }
  679. return redisTemplate.opsForSet().remove(key, members);
  680. }
  681. /**
  682. * @TODO : 将元素value从一个集合移到另一个集合
  683. * @Author: 小霍
  684. * @UpdateUser:
  685. * @Version: 0.0.1
  686. * @param srckey
  687. * @param dstkey
  688. * @param member
  689. * @return java.lang.Long
  690. *
  691. */
  692. public Boolean smove(String srckey, String dstkey, Object member) {
  693. if (null == srckey || null == dstkey) {
  694. return false;
  695. }
  696. return redisTemplate.opsForSet().move(srckey, member, dstkey);
  697. }
  698. /**
  699. * @TODO : 获取两个集合的并集
  700. * @Author: 小霍
  701. * @UpdateUser:
  702. * @Version: 0.0.1
  703. * @param key
  704. * @param otherKeys
  705. * @return java.util.Set<java.lang.Object> 返回两个集合合并值
  706. *
  707. */
  708. public Set<Object> sUnion(String key, String otherKeys) {
  709. if (null == key || otherKeys == null) {
  710. return null;
  711. }
  712. return redisTemplate.opsForSet().union(key, otherKeys);
  713. }
  714. // **********Sorted Set 数据类型********************
  715. /**
  716. * @TODO : 添加参数中指定的所有成员及其分数到指定key的Sorted Set中,在该命令中我们可以指定多组score/member作为参数。
  717. * 如果在添加时参数中的某一成员已经存在,该命令将更新此成员的分数为新值,同时再将该成员基于新值重新排序。
  718. * 如果键不存在,该命令将为该键创建一个新的Sorted Set Value,并将score/member对插入其中。
  719. * @Author: 小霍
  720. * @UpdateUser:
  721. * @Version: 0.0.1
  722. * @param key
  723. * @param score
  724. * @param member
  725. * @return java.lang.Long
  726. *
  727. */
  728. public Boolean zadd(String key, double score, Object member) {
  729. if (null == key) {
  730. return false;
  731. }
  732. return redisTemplate.opsForZSet().add(key, member, score);
  733. }
  734. /**
  735. * @TODO : 该命令将移除参数中指定的成员,其中不存在的成员将被忽略。 如果与该Key关联的Value不是Sorted Set,相应的错误信息将被返回。
  736. * 如果操作成功则返回实际被删除的成员数量。
  737. * @Author: 小霍
  738. * @UpdateUser:
  739. * @Version: 0.0.1
  740. * @param key
  741. * @param members 可以使一个string 也可以是一个string数组
  742. * @return java.lang.Long
  743. *
  744. */
  745. public Long zrem(String key, Object... members) {
  746. if (null == key || null == members) {
  747. return 0L;
  748. }
  749. return redisTemplate.opsForZSet().remove(key, members);
  750. }
  751. /**
  752. * @TODO : 返回Sorted Set中的成员数量,如果该Key不存在,返回0。
  753. * @Author: 小霍
  754. * @UpdateUser:
  755. * @Version: 0.0.1
  756. * @param key
  757. * @return java.lang.Long
  758. *
  759. */
  760. public Long zcard(String key) {
  761. if (null == key) {
  762. return 0L;
  763. }
  764. return redisTemplate.opsForZSet().size(key);
  765. }
  766. /**
  767. * @TODO : 该命令将为指定Key中的指定成员增加指定的分数。如果成员不存在,该命令将添加该成员并假设其初始分数为0,
  768. * 此后再将其分数加上increment。如果Key不存在,该命令将创建该Key及其关联的Sorted Set,
  769. * 并包含参数指定的成员,其分数为increment参数。如果与该Key关联的不是Sorted Set类型,
  770. * 相关的错误信息将被返回。如果不报错则以串形式表示的新分数。
  771. * @Author: 小霍
  772. * @UpdateUser:
  773. * @Version: 0.0.1
  774. * @param key
  775. * @param score
  776. * @param member
  777. * @return java.lang.Double
  778. *
  779. */
  780. public Double zincrby(String key, double score, Object member) {
  781. if (null == key) {
  782. throw new BusinessException(4001004, "key 不能为空");
  783. }
  784. return redisTemplate.opsForZSet().incrementScore(key, member, score);
  785. }
  786. /**
  787. * @TODO : 该命令用于获取分数(score)在min和max之间的成员数量。
  788. * (min=<score<=max)如果加上了“(”着表明是开区间例如zcount key (min max 则
  789. * 表示(min<score=<max) 同理zcount key min (max 则表明(min=<score<max) 返回指定返回数量。
  790. * @Author: 小霍
  791. * @UpdateUser:
  792. * @Version: 0.0.1
  793. * @param key
  794. * @param min
  795. * @param max
  796. * @return java.lang.Long
  797. *
  798. */
  799. public Long zcount(String key, double min, double max) {
  800. if (null == key) {
  801. return 0L;
  802. }
  803. return redisTemplate.opsForZSet().count(key, min, max);
  804. }
  805. /**
  806. * @TODO : Sorted Set中的成员都是按照分数从低到高的顺序存储,该命令将返回参数中指定成员的位置值, 其中0表示第一个成员,它是Sorted
  807. * Set中分数最低的成员。 如果该成员存在,则返回它的位置索引值。否则返回nil。
  808. * @Author: 小霍
  809. * @UpdateUser:
  810. * @Version: 0.0.1
  811. * @param key
  812. * @param member
  813. * @return java.lang.Long
  814. *
  815. */
  816. public Long zrank(String key, Object member) {
  817. if (null == key) {
  818. return null;
  819. }
  820. return redisTemplate.opsForZSet().rank(key, member);
  821. }
  822. /**
  823. * @TODO : 如果该成员存在,以字符串的形式返回其分数,否则返回null
  824. * @Author: 小霍
  825. * @UpdateUser:
  826. * @Version: 0.0.1
  827. * @param key
  828. * @param member
  829. * @return java.lang.Double
  830. *
  831. */
  832. public Double zscore(String key, Object member) {
  833. if (null == key) {
  834. return null;
  835. }
  836. return redisTemplate.opsForZSet().score(key, member);
  837. }
  838. /**
  839. * @TODO :
  840. * 该命令返回顺序在参数start和stop指定范围内的成员,这里start和stop参数都是0-based,即0表示第一个成员,-1表示最后一个成员。如果start大于该Sorted
  841. * Set中的最大索引值,或start > stop,此时一个空集合将被返回。如果stop大于最大索引值,
  842. * 该命令将返回从start到集合的最后一个成员。如果命令中带有可选参数WITHSCORES选项,
  843. * 该命令在返回的结果中将包含每个成员的分数值,如value1,score1,value2,score2...。
  844. * @Author: 小霍
  845. * @UpdateUser:
  846. * @Version: 0.0.1
  847. * @param key
  848. * @param min
  849. * @param max
  850. * @return java.util.Set<java.lang.String> 指定区间内的有序集成员的列表。
  851. *
  852. */
  853. public Set<Object> zrange(String key, long min, long max) {
  854. if (null == key) {
  855. return null;
  856. }
  857. return redisTemplate.opsForZSet().range(key, min, max);
  858. }
  859. /**
  860. * @TODO : 该命令的功能和ZRANGE基本相同,唯一的差别在于该命令是通过反向排序获取指定位置的成员,
  861. * 即从高到低的顺序。如果成员具有相同的分数,则按降序字典顺序排序。
  862. * @Author: 小霍
  863. * @UpdateUser:
  864. * @Version: 0.0.1
  865. * @param key
  866. * @param start
  867. * @param end
  868. * @return java.util.Set<java.lang.String>
  869. *
  870. */
  871. public Set<Object> zReverseRange(String key, long start, long end) {
  872. if (null == key) {
  873. return null;
  874. }
  875. return redisTemplate.opsForZSet().reverseRange(key, start, end);
  876. }
  877. /**
  878. * @TODO : 该命令将返回分数在min和max之间的所有成员,即满足表达式min <= score <= max的成员,
  879. * 其中返回的成员是按照其分数从低到高的顺序返回,如果成员具有相同的分数, 则按成员的字典顺序返回。可选参数LIMIT用于限制返回成员的数量范围。
  880. * 可选参数offset表示从符合条件的第offset个成员开始返回,同时返回count个成员。
  881. * 可选参数WITHSCORES的含义参照ZRANGE中该选项的说明。*最后需要说明的是参数中min和max的规则可参照命令ZCOUNT。
  882. * @Author: 小霍
  883. * @UpdateUser:
  884. * @Version: 0.0.1
  885. * @param key
  886. * @param max
  887. * @param min
  888. * @return java.util.Set<java.lang.String>
  889. *
  890. */
  891. public Set<Object> zrangebyscore(String key, double min, double max) {
  892. if (null == key) {
  893. return null;
  894. }
  895. return redisTemplate.opsForZSet().rangeByScore(key, min, max);
  896. }
  897. /**
  898. * @TODO : 该命令除了排序方式是基于从高到低的分数排序之外,其它功能和参数含义均与ZRANGEBYSCORE相同。
  899. * 需要注意的是该命令中的min和max参数的顺序和ZRANGEBYSCORE命令是相反的。
  900. * @Author: 小霍
  901. * @UpdateUser:
  902. * @Version: 0.0.1
  903. * @param key
  904. * @param max
  905. * @param min
  906. * @return java.util.Set<java.lang.String>
  907. *
  908. */
  909. public Set<Object> zrevrangeByScore(String key, double min, double max) {
  910. if (null == key) {
  911. return null;
  912. }
  913. return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
  914. }
  915. }

5. redis 实战-分布式 Session 共享

以往我们的项目都是部署在单台服务器运行,因为客户的所有请求都是由唯一服务器来处理,sessionId 保存在这台服务器上是没有问
题的。但是当项目同时部署在多台服务器上时,就会出现 sessionId 共享问题。
现在有两台服务器同时运行,分别是 Server A 和 Server B,通过 nginx 配置了负载均衡,客户端的请求会被随机分配到两台服务器上
进行处理。假设客户现在有第一次请求(登录请求)被分配到 Server A 进行处理,Server A 接受到请求之后会生成 sessionId 并且保
存到内存当中,然后返回给客户(浏览器),浏览器会把 sessionId 保存到 cookie 中,第一次请求完成。如果之后每一次请求还是由
Server A 来处理当然一切正常,但是一旦出现意外(比如 Server A 宕机)或者nginx 采用了轮询、weight方式负载均衡,请求被分配
到 Server B进行处理,这时候 Server B 拿到客户请求的 sessionId 是由 Server A 生成的,两边对不上啊 ! 于是客户会发现,本来还
用的好好的,怎么会突然跳到登录页需要重新登录了呢 ?
那我们该怎么去解决呢?
既然问题的根源出在 sessionId 无法共享上面,那么是不是让 sessionId 可以在多台服务器之间共享就可以了?换个思路,即把
sessionId 保存到数据库即可(最终选择redis,因为会比较快!),验证时不再从当前服务器获取 sessionId 改从 redis 中获取即可!

实现思路:

redis 中 session 管理有两种方案,1. redis 直接接管所有的session,2. 将以往存储在session中的数据存储在redis中,不在使用到session。

  1. 登录页面提交用户名密码。
  2. 登录成功后生成token。Token相当于原来的sessionid,字符串,可以使用uuid。
  3. 把用户信息保存到redis。Key就是token,value就是userId。
  4. 设置key的过期时间。模拟Session的过期时间。一般一个小时。
  5. 拦截器拦截请求校验 sessionId。

1. redis 全面接管 session
  1. 添加依赖
  1. <dependency>
  2. <groupId>org.springframework.session</groupId>
  3. <artifactId>spring-session-data-redis</artifactId>
  4. </dependency>
  1. 编写配置类(RedisSessionManagerApplication)
  1. @Configuration
  2. @EnableRedisHttpSession // 开启全局 redis session 会话管理
  3. public class RedisSessionManagerApplication {
  4. // 完成以上两步后session就统一移交给redis管理,
  5. // 注意: 每次修改数据后都要重新保存(session.setAttribute()),否则redis中保存的数据将不会同步
  6. }

注意 每次修改session中的数据后都要重新保存(session.setAttribute()),否则redis中保存的数据将不会同步

2. 将数据存储在redis中
  1. 编写登录代码
  1. ......
  2. // 凭证存入redis 60分钟失效
  3. redisService.set(respVO.getToken(), respVO.getId(), 60, TimeUnit.MINUTES);
  4. ......

6. redis 实战-异地登录提醒下线

一个账号同时只能在一个地方登录,如果在其他地方登录则提示已在别处登录,同时,同一浏览器同
时只能登录一个用户。

思路

  1. 登录页面提交用户名密码。
  2. 登录成功后生成token。Token相当于原来的 sessionid,字符串,可以使用uuid。
  3. 把用户信息保存到redis。Key就是token,value就是userId。
  4. 设置key的过期时间。模拟Session的过期时间。一般一个小时。
  5. 标记 Token把Token存入redis,key为 userId,value 就是 Token过期时间和 key 为 Token 的过期时间一致
  6. 拦截器拦截请求校验 token。
  7. 获取 userId 后再去比较 header 携带的token和redis标记的token是否一致,不一致则提示用户已经异地登录

7. redis 实战-注册短信验证码

8. redis 实战-计数器(订单号/特殊有规律编码/点赞数)

9. redis 实战-购物车