redis入门一篇就够了!(两万字总结) 上

文章大纲:

image.png

1.官方中文文档

www.redis.cn/

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版本的方法

www.cnblogs.com/jixiaohua/p…

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?

www.zhihu.com/question/20…

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中存储的值+10
  • decrby 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中所有的域field
  • hvals 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位的值。如果没有设置返回0
  • bitcount 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-config

    systemctl 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

下载地址:github.com/qishibo/Ano…

可以用它来测试和管理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"));
    }

}

复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享