文章大纲:
1.官方中文文档
2.linux下安装
-
官网下载安装包 redis-6.0.6.tar.gz
-
放到服务器的usr/local/redis目录下
-
解压:
tar -zxvf redis-6.06.tar.gz
-
在redis目录下进行基本的环境安装:
yum install gcc-c++
-
检查gcc的版本:
gcc -v
-
在redis目录下执行
make
(若报错可能是gcc的版本问题) -
执行
make install
- 进入/usr/local/bin目录,这是redis的默认安装目录。在这个目录创建一个myredisconfig的文件夹。然后将/usr/local/redis/redis-6.0.6下的redis.conf拷贝到这个文件夹下
- 设置为后台自动启动:修改myredisconfig目录下的redis.conf
daemonize no
改为daemonize yes
,然后:wq!
保存退出
- 通过配置文件启动redis(在/usr/local/bin目录下)
redis-server myredisconfig/redis.conf
-
redis客户端测试连接
redis-cli -p 6379
ping
- 退出redis
shutdown
exit
- 查看redis进程
ps -ef|grep redis
3.安装出现的问题
- 在安装redis6以上的版本时make命令会出现错误,原因是gcc版本太低(centos默认版本为4),可以选择安装低版本redis5或者升级gcc到8版本,通过gcc -v命令查看版本.
- 升级gcc版本的方法
4. 压力测试工具
redis-benchmark是官方自带的性能测试工具,可以有效的测试redis服务的性能。在bin目录下。
测试demo:
测试100个并发连接,10000个请求,检测host为localhost 端口为6379的redis服务器性能。
redis-benchmark -h localhost -p 6379 -c 100 -n 10000
相关参数说明:
5. 基本的数据库命令
-
redis命令手册:redis.com.cn/commands.ht…
-
redis默认有16个数据库 0~15,默认使用第0个。
查看redis.conf可以得知:
-
进入redis:
redis-server myredisconfig/redis.conf
redis-cli -p 6379
-
退出redis:
shutdown
exit
-
输入密码:
auth 311046
-
切换数据库:
select n
n表示第几个数据库 -
查看当前数据库的key的数量:
dbsize
-
查看具体的key:
key *
-
清空当前库:
flushdb
-
清空全部的库:
flushall
-
设置key值:
set name coderchen
-
获取key值:
get name
-
查看 key是否存在:
exists keyname
例如 exists name -
设置key生存时间当key过期会被自动删除:
expire key 秒数
-
查看还有多少秒过期:
ttl name
-1表示永不过期,-2表示已经过期。 -
查看key的类型:
type key
-
删除key:
del key
注意:redis命令不区分大小写,但是记录的数据区分大小写
- 为什么redis默认端口是6379?
6. redis单线程快?
- 为什么redis是单线程?
redis是基于内存的操作,cpu不是redis的瓶颈,redis的瓶颈是机器内存大小或者网络带宽。既然单线程容易实现而且cpu不会成为瓶颈,那么就顺理成章的采用单线程的方案了。redis是C语言编写的,官方数据可以达到100000+QPS(每秒查询次数)
- redis为什么这么快?
并不是多线程一定比单线程快,效率高。这与cpu,内存,硬盘的速度有关。
redis单线程快是因为redis是基于内存的操作。单线程效率最高,因为多线程的本质就是cpu模拟多个线程的情况,这其中有一个大的开销就是cpu上下文的切换。而redis单线程对内存中的数据进行读写时,速度比较快。举个例子就是读取1MB的数据时,可能多个进程cpu上下文的切换时间就大于单个进程读取数据的时间
key值加引号和不加的区别?
7.字符串String(单值单value)
- string是redis 的基本类型,一个key对应一个value。string类型是二进制安全的,即redis的string可以包含任何数据,比如jpg图片或者序列化的对象,string类型redis最基本的数据类型,一个redis中字符串value最多可以是512M
常用命令说明:
官方命令手册:redis.com.cn/commands.ht…
string数据结构是简单的key-value类型,value不仅可以是string,也可以是数字。
-
append key1 "hello"
:对不存在的key进行append,等同于set key1 "hello"
-
append key1 ",world"
:对已经存在的key append就是在后面追加。 -
strlen key1
:获取字符串长度
set views 0
:要设置为数字才能用incr,decr命令 ,本质上这是一个字符串操作,因为Redis没有专门的整数类型。存储在 key 中的字符串被转换为十进制有符号整数incr views
:执行一次就将key中存储 的数字值增一decr views
:执行一次就将key中存储的数字值减一incrby views 10
:执行一次key中存储的值+10decrby views 10
:执行一次key中存储的值-10
-
getrange key2 m n
:获取[m,n]区间范围内的字符串。n可以为负数,表示偏移,-1 表示最后一个字符, -2 表示倒数第二个字符,以此类推。 -
setrange key2 n value
:将key2的值从第n个开始替换为指定的value -
setex key2 10 "hello"
:将key2的值设置为hello,并且key2的生存时间设置为10s。这个命令等价于set key2 "hello"
expire key2 10
但区别是setex 命令是一个原子操作,在同一时间内完成设置值和设置过期时间这两个操作。 -
setnx key3 "redis"
:在指定的key不存在时,为key设置指定的值,这种情况下等同于set,当key存在时,什么也不做。返回值:0 被设置了 1 没有被设置。
mset key1 value1 key2 value2 ......keyn valuen
:设置多个key和value,如果这其中已经存在key,则会覆盖掉。mget key1 key2 ...keyn
:返回所有给定的key的值msetnx key1 value1 key2 value2 ...keyn valuen
:设置多个 key value,当且仅当所有给定的健都不存在时,即使有一个健存在值,msetnx也会拒绝执行操作。
getset key value
:将key的值修改为value,并返回在修改之前的值。如果key在执行这个命令前不存在,则返回nil。
- 可以用来缓存对象
mset user:1:name zhangsan user:1:age 2
mget user:1:name user:1:age
应用
- 微博数,粉丝数等
8. 列表List(单值多value)
- redis的列表是简单的字符串列表,按照插入顺序排序,你可以添加一个元素到列表的头部(左边)或者尾部(右边)
- 它的底层就是一个链表
常用命令说明
官方命令手册:redis.com.cn/commands.ht…
lpush list "one"
:将值插入到列表的头部(左边)rpush list "two"
:将值插入到列表的尾部(右边)lrange list n m
:返回指定区间的元素 [n,m] m为-1表示返回n后面所有的元素。
lpop list
:移除并返回列表的第一个元素,左边的第一个rpop list
:移除并返回列表的最后一个元素,右边第一个。
lindex list n
:按照索引下标n获得元素(-1代表最后一个,0表示第一个,以此类推)
llen list
:返回列表的长度
lrem list n value
:移除列表中与value相等的元素,移除n个,顺序是从前往后。n>0 从头到尾删除值为value的元素, n<0,从尾到头删除值为value的元素, n=0,移除所有值为value的元素
ltrim list n m
:对一个列表进行修剪,只保留区间[n,m]内的元素。其他全部删除。
rpoplpush list1 list2
:移除列表list1的最后一个元素,并将该元素添加到list2的前边。这个操作是原子性的。从6.2.0起被废除了。
lset list n value
:将list中第n个元素的值设置为value
linsert list before|after value value1
:将value1插入到value的前边或者后边。
性能总结
- 它是一个字符串列表,left,right都可以插入添加
- 如果键不存在,创建新的链表。
- 如果已经存在,新增内容
- 如果值全部移除,对应的键也就消失了
- 链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就比较低。
应用
- 最新消息排行功能
- 消息对列
9. 集合Set(单值多value)
- redis的集合set是string类型的无序集合,是通过HashTable实现的。
常用命令说明
sadd key member[...]
:将一个或者多个成员元素加入到集合中。smembers key
:返回存储在key中的所有成员sismember key member
:判断元素member是否是集合key中的成员。返回1表示是,0表示不是。
scrad key
:返回集合中元素的数量。
srem key value
:移除集合key中的一个或者多个元素value
srandmember key [count]
:返回集合key中的随机元素,count表示返回几个元素。
spop key [count]
:从集合key中删除一个或者多个元素,并返回删除的元素。(删除是随机的)
smove key1 key2 member
:将指定的元素从集合key1移动到key2 。这是一个原子操作。
sdiff key [key...]
:返回第一个集合key与其他key之间的差异,即第一个集合key独有的元素。sinter key [key...]
:返回所有给定集合key的成员交集sunion key [key...]
:返回所有给定集合key的并集
应用
- 实现共同关注,共同好友等功能。
10. 哈希Hash(单值多value,v是一个键值对)
- Hash是一个键值对集合,类似于java中的map,是一个String类型的field和value的映射表,特别适合存储对象。
常用命令
hset key field value [field value ....]
: 为哈希表key的field字段赋值hget key field
:返回哈希表key中的field字段的值hgetall key
:返回哈希表key中所有的域和值hdel key field [field...]
:删除哈希表key中的一个或者多个指定字段。
hlen key
:获取哈希表key中的字段fields的数量
hexists key field
:查看哈希表的指定字段field是否存在。0表示不存在,1表示存在hkeys key
:返回哈希表key中所有的域fieldhvals key
:返回哈希表中所有域的值
hincrby key field increment
: 为哈希表key中的域field的值加上增量increment.增量为负数表示进行减法操作。
hsetnx key field value
:为哈希表中不存在的字段field赋值为value。0表示设置失败,1表示设置成功。
应用
- Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 存储部分变更的数据,如用户信息等。
11. 有序集合Zset
- 在set基础上,加上一个score。之前set是sadd set1 v1 v2 v3…,现在Zset是zadd zset1 score1 v1 score2 v2。
常用命令
zadd key score1 member1[score2 member2...]
:将一个或者多个member元素及其score值加入到有序集合key中。其中score可以是整数值或者双精度浮点数。可以为正或者负数zrange key n m
:返回有序集合key中的[n,m]区间内的元素。下标从0开始。如果m超出范围也不会报错,只会返回包含的。
zrangebyscore key n m
:返回有序集合key中的成员,按照score的从小到大排序,范围是[n,m],n,m可以为-inf,+inf,这样在不知道有序集合数量的时候获取所有的成员。注意[n,m]这个范围是score的范围,不是成员的下标
zrem key member1[member2...]
:从有序集合key中删除指定的成员member
zcard key
:返回有序集合key中的成员个数
zcount key n m
:返回有序集key中,score在[n,m]之间的成员数量。
zrank key member
:返回有序集key中成员的排名,按照score从低到高排名,从0开始
zrevrank key member
:返回有序集key中成员的排名,按照score从高到低排名,从0开始
应用
- 排行榜应用,取top n操作。
12. 三种特殊的数据类型
12.1 GEO地理位置
简介:
redis的GEO特性在redis3.2版本中推出,这个功能可以将用户给定的地理位置信息存储起来,并对这些信息进行操作。来实现诸如附近位置,摇一摇这类依赖于地理位置信息的功能。GEO的数据类型为Zset。
GEO的数据结构总共有六个常用命令:geoadd ,geopos ,geodist , georadius, georadiusbymember , gethash
官方文档:www.redis.net.cn/order/3685.…
命令:
geoadd
将指定的地理空间位置(经度,纬度,名称)添加到指定的key中。这些数据会存储到有序集合Zset中,目的是为了方便使用georadius,或者georadiusbymember命令对数据进行半径查询等操作。
geoadd china:city 121.48 31.40 shanghai 113.88 22.55 shenzhen 120.21 30.20 hangzhou
注意点:该命令采用标准格式的参数xy,所以经度必须在纬度之前。有效的经度 -180~180度,有效的纬度 -85.05~85.05
geopos
从key里面返回所有给定位置元素的位置(经度和纬度)
geopos key member[member...]
geopos china:city shanghai hangzhou
geodist
返回两个给定位置之间的距离,指定的参数unit必须是以下中的一种。m 米 ,km 千米, mi 英里,ft 英尺,默认是m。
geodist key member1 member2 [unit]
geodist china:city shanghai hangzhou km
georadius
以给定的经纬度为中心,找出某一半径内的元素。
范围的单位:m米,km千米 ,mi英里 ,ft英尺
georadius key longitude(经度) latitude(纬度) radius m|km|ft|mi [withcoord] [withdist][withhash][asc|desc][count count]
withdist:返回位置元素的同时,将位置元素与中心之间的距离也一并返回。距离的单位与用户给定范围单位保持一致。
withcoord:将位置元素的经纬度也返回。
withhash:以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大
asc: 根据中心的位置, 按照从近到远的方式返回位置元素。
desc:根据中心的位置, 按照从远到近的方式返回位置元素。
count:获取前n个匹配的元素,可以减少带宽当数据量大时。
举例:
georadius china:city 120 30 1500 km withdist
georadiusbymember
找出位于指定范围内的元素,中心点是由给定的位置元素决定的。
georadiusbymember key member radius m|km|ft|mi [withcoord][withdist][withhash][asc|desc][count count]
举例:
georadiusbymember china:city shanghai 1500 km
geohash
返回一个或者多个位置元素的geohash表示。
redis使用geohash将二维经纬度转换为一维字符串,字符串越长表示位置更精确,两个字符串越相似表示距离越近。
geohash key member[member...]
举例:
geohash china:city shanghai hangzhou
zrem
geo没有提供删除成员的方法,但是因为geo的底层是zset,所有可以用zrem命令实现对地理位置信息的删除。
zrem china:city shanghai
:移除元素
zrange china:city 0 -1
:查看所有元素
命令演示
12.2 HyperLogLog
简介
redis在2.8.9版本添加了HyperLogLog结构。
redis HyperLogLog是用来做基数统计的算法。HyperLogLog的优点是在输入元素的数量和体积非常大时,计算基数所需的空间总是固定的,并且很小。
基数:比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
在redis里面,每个HyperLogLog键只需要花费12kb内存,就可以计算接近2^64个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
HyperLogLog则是一种算法,提供了不精确的去重基数方案。
举个栗子:假如我要统计网页的UV(浏览用户数量,一天内同一个用户多次访问只能算一次),传统的 解决方案是使用Set来保存用户id,然后统计Set中的元素数量来获取页面UV。但这种方案只能承载少量 用户,一旦用户数量大起来就需要消耗大量的空间来存储用户id。我的目的是统计用户数量而不是保存 用户,这简直是个吃力不讨好的方案!而使用Redis的HyperLogLog最多需要12k就可以统计大量的用户 数,尽管它大概有0.81%的错误率,但对于统计UV这种不需要很精确的数据是可以忽略不计的
基本命令
pfadd key element[element...]
:添加指定元素到HyperLogLog
pfcount key[key...]
:返回给定HyperLogLog的基数估计值。
pfmerge destkey sourcekey [sourcekey...]
:将多个HyperLogLog合并为一个HyperLogLog,并集计算。
12.3 BitMap
简介
在开发中,可能会遇到这种情况:需要统计用户的某些信息,如活跃或不活跃,登录或者不登录;又如 需要记录用户一年的打卡情况,打卡了是1, 没有打卡是0,如果使用普通的 key/value存储,则要记录 365条记录,如果用户量很大,需要的空间也会很大,所以 Redis 提供了 Bitmap 位图这种数据结构, Bitmap 就是通过操作二进制位来进行记录,即为 0 和 1;如果要记录 365 天的打卡情况,使用 Bitmap 表示的形式大概如下:0101000111000111………………………,这样有什么好处呢?当然就是节约内存 了,365 天相当于 365 bit,又 1 字节 = 8 bit , 所以相当于使用 46 个字节即可。
BitMap 就是通过一个 bit 位来表示某个元素对应的值或者状态, 其中的 key 就是对应元素本身,实际上 底层也是通过对字符串的操作来实现。Redis 从 2.2 版本之后新增了setbit, getbit, bitcount 等几个 bitmap 相关命令。
基本命令
setbit key n value
:设置key的第n位的值value是0或1,n是从0开始的。getbit key n
:获取第n位的值。如果没有设置返回0bitcount key[start,end]
:统计key上值为1的个数。
13. redis事务
理论
redis事务的概念:
redis事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有的命令都会被序列化。在事务执行过程中,会按照顺序串行化执行对列中 的命令,其他客户端提交的命令请求不会插入到事务执行的命令序列中。
总结的说:redis事务就是一次性,顺序性,排他性的执行一个对列中的一系列命令。
redis事务没有隔离级别的概念:
批量操作在发送exec命令前被放入对列缓存,并不会被实际执行。
redis不保证原子性:
redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
redis事务的三个阶段:
开始事务
命令入队
执行事务
redis事务的相关命令
watch key1 key2...
:监视一个或者多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断(类似乐观锁)unwatch
:取消watch对所有key的监控。multi
:标记一个事务块的开始(queued)exec
:执行所有事务块的命令(一旦执行exec后,之前加的监控锁都会被取消掉)discard
:取消事务,放弃事务块中所有 的命令
乐观锁和悲观锁?
- 悲观锁(Pessimistic Lock),顾名思义就是很悲观,每次拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿到这个数据就会block知道它拿到锁。传统的关系型数据库里面就用到了很多这种锁机制,比如行锁,表锁等,读锁写锁等,都是在操作之前先上锁。
- 乐观锁(Optimistic Lock)顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制,乐观锁适用于多读的应用类型,这样可以提高吞吐量,乐观锁策略:提交版本必须大于记录当前版本才能执行更新。
实践
-
正常执行
-
放弃事务
-
如果事务对列中存在命令性错误(类似java编译性错误),执行exec命令时,所有命令都不会执行。
-
如果在事务对列中存在语法性错误(类似于java的1/0运行时异常),则执行exec命令时,其他正确命令会被执行,错误命令抛出异常。
-
watch命令的演示(watch 用来监控key在事务执行的前后是否变化)–事务成功执行
-
使用watch–事务执行失败
由于在事务执行过程中,watch监视的值出现了变化,因此导致了错误,这时要放弃监视,然后重来。
**注意:**一旦exec执行事务后,无论事务执行成功还是失败,watch对变量的监控都被取消。因此当事务执行失败后,需要重新执行watch对变量进行监控,并开启新的事务进行操作。
watch指令类似于乐观锁,事务提交时,如果watch监控的多个key中任何key的值被其他客户端更改,则使用exec执行事务时,事务对列不会被执行,同时返回Nullmulti-bulk 通知调用者事务执行失败。
14. springboot整合redis
步骤:
- 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
复制代码
-
在application.properties中配置
spring.redis.host=115.29.232.195 spring.redis.port=6379 spring.redis.database=0 spring.redis.timeout=50000 复制代码
-
测试
@SpringBootTest class SpringbootRedisApplicationTests { @Autowired private RedisTemplate redisTemplate; @Test void contextLoads() { /* * opsForList:操作list,类似string * opsForGeo 操作geo * opsForSet * ....... * 和redis命令行一样。 * */ /*获取redis的连接对象*/ // RedisConnection connection = redisTemplate.getConnectionFactory().getConnection(); // connection.flushDb(); // connection.flushAll(); redisTemplate.opsForValue().set("mykey","kuangshen"); System.out.println(redisTemplate.opsForValue().get("mykey")); redisTemplate.opsForSet().add("k5","v9"); Set k5 = redisTemplate.opsForSet().members("k5"); System.out.println(k5); } } 复制代码
redis要做的配置
- 阿里云设置redis的安全组:端口6379
-
修改redis.conf
(在/usr/local/bin/myredisconfig/redis.conf)
bind 127.0.0.1 改为 bind 0.0.0.0(注释掉也行)
protected-mode yes 改为 protected-mode no: (即该配置项表示是否开启保护模式,默认是开启,开启后Redis只会本地进行访问,拒绝外部访问)
daemonize no 改为 daemonize yes
注意:修改后要重新登录redis
-
关于防火墙的设置
rpm -qa|grep firewalld;rpm -qa|grep firewall-config
:检查系统是否安装了firewalld和firewall-config.CentOS中系统默认安装firewalld, firewall-config要自己安装yum -y update firewalld
:将firewalld更新为最新版yum -y install firewall-config
:安装firewall-configsystemctl start firewalld
:启动firewalld服务systemctl status firewalld
:查看firewalld的状态systemctl stop firewalld
:停止firewalld服务systemctl enable firewalld
:恢复开机自动启动firewalld服务推荐参考文章:CentOS7中firewalld的安装与使用详解 blog.csdn.net/solaraceboy…
-
检查阿里云的防火墙是否开放端口号
firewall-cmd --query-port=6379/tcp
如果是 yes,则就是开放的。如果是no
则永久开放6379端口:
firewall-cmd --zone=public --add-port=6379/tcp --permanent
重新加载:
firewall-cmd --reload
然后再查看端口是否开启:
firewall-cmd --query-port=6379/tcp
-
查看redis服务进程
ps -ef | grep redis 复制代码
-
如果上面的方法都试过了但还是不行,试试重启服务器
推荐一个好用的redis客户端工具
Another Redis Desktop Manager
可以用它来测试和管理redis
redis的序列化配置
- 通过源码可知,springboot自动帮我们在容器中生成了一个redisTemplate和一个StringRedisTemplate,但是这个redisTemplate的泛型是<ooject,object>,写代码不方便,需要类型转换的代码,并且没有设置数据存在redis时,key及value的序列化方式。因此要自定义一个配置类。
- 为什么要序列化:www.jianshu.com/p/cc5a29b06…
package com.kuang.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
import java.net.UnknownHostException;
/**
* Created with Intellij IDEA
* Description:
* user: CoderChen
* Date: 2021-06-06
* Time: 14:05
*/
@Configuration
public class redisConfig {
/*编写自己的redisTemplate----固定模板*/
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
/*为了自己开发方便,一般直接使用<String,object>*/
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
/*json序列化配置*/
Jackson2JsonRedisSerializer Jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
Jackson2JsonRedisSerializer.setObjectMapper(om);
/*string序列化配置*/
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
/*配置具体的序列化方式*/
/*key采用string的序列化方式*/
template.setKeySerializer(stringRedisSerializer);
/*hash的key采用string的序列化方式*/
template.setHashKeySerializer(stringRedisSerializer);
/*value序列化方式采用jackson*/
template.setValueSerializer(Jackson2JsonRedisSerializer);
/*hash的value序列化方式采用jackson*/
template.setHashValueSerializer(Jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
复制代码
redis的工具类
- 直接使用redisTemplate操作redis时,需要很多代码,因此直接封装一个redisUtils,这样写代码方便一点。这个redisUtils交给spring容器实例化,使用时直接注解注入。
package com.kuang.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Created with Intellij IDEA
* Description:
* user: CoderChen
* Date: 2021-06-06
* Time: 14:52
*/
/*在真实开发中,经常使用*/
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time,
TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0
* 时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove (String key,long count, Object value){
try {
Long remove = redisTemplate.opsForList().remove(key, count,value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
复制代码
测试代码
package com.kuang;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kuang.pojo.User;
import com.kuang.utils.RedisUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Set;
@SpringBootTest
class SpringbootRedisApplicationTests {
@Autowired
@Qualifier("redisTemplate")/*避免与源码重合,跳转到自定义redisTempalte*/
private RedisTemplate redisTemplate;
@Autowired
private RedisUtil redisUtil;
@Test
void contextLoads() {
/*
* opsForList:操作list,类似string
* opsForGeo 操作geo
* opsForSet
* .......
* 和redis命令行一样。
* */
/*获取redis的连接对象*/
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// connection.flushDb();
// connection.flushAll();
redisTemplate.opsForValue().set("mykey","kuangshen");
System.out.println(redisTemplate.opsForValue().get("mykey"));
redisTemplate.opsForSet().add("k5","v9");
Set k5 = redisTemplate.opsForSet().members("k5");
System.out.println(k5);
}
@Test
public void test() throws JsonProcessingException {
/*真实开发一般使用json传递数据*/
User user = new User("kuangshen", 3);
// String jsonUser = new ObjectMapper().writeValueAsString(user);
// redisTemplate.opsForValue().set("user", jsonUser);
redisTemplate.opsForValue().set("user", user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
@Test
public void test1() {
redisUtil.set("username", "coderchen");
System.out.println(redisUtil.get("username"));
}
}
复制代码