课程链接:黑马Redis快速入门,一套搞定Redis,常见数据结构及命令,包含jedis应用与优化、springdataRedis应用与优化_哔哩哔哩_bilibili
声明:该文章为本人的学习笔记,非专业教程
目录
Redis图形化界面 RedisDesktopManager(RDM)
Redis快速入门
认识NoSQL
差异
SQL(关系型数据库) | NoSQL(非关系型数据库) | |
---|---|---|
数据结构 | 结构化(structured) | 非结构化 |
数据关联 | 关联的(Relational) | 无关联的 |
查询方式 | SQL查询 | 非SQL |
事务特性 | ACID(事务) | BASE(基本一致,无事务) |
存储方式 | 磁盘 | 内存 |
扩展性 | 垂直 | 水平 |
使用场景 | 1)数据结构固定 2)相关业务对数据安全性、一致性要求较高 | 1)数据结构不固定 2)对一致性、安全性要求不高 3)对性能要求低 |
认识Redis
Redis诞生于2009年全称Remote Dictionary Server,远程词典服务器,是一个基于内存的键值型NoSQL数据库。
特征:
-
键值(key-value)型,value支持多种不同的数据结构,功能丰富
-
单线程,每个命令具有原子性
-
低延迟,速度快(基于内存、IO多路复用、良好的编码)
-
支持数据持久化(解决断电问题)
-
支持主从集群、分片集群
-
支持多语言客户端
安装Redis(Linux)
-
配置EPEL仓库
EPEL的全称是 Extra Packages for Enterprise Linux。EPEL是由Fedora 社区打造,为RHEL及其衍生发行版如CentOS、Scientific Linux等提供高质量软件包的项目。装上了EPEL后,相当于添加了一个第三方源。EPEL则为服务器版本提供大量的rpm包(yum程序所使用的程序安装包,类似Windows的exe),而且大多数rpm包在官方repository中是找不到的
# root执行 yum install -y epel-release
-
安装Redis
# root执行 yum install -y redis
-
配置Redis
# vim 打开 /etc/redis.conf文件(需root) # 找到 bind 127.0.0.1 改为 bind 0.0.0.0 # 为了方便连接redis,改为任意IP都可以连接Redis # 找到 #requirepass foobared requirepass 设置你的密码 # 本机测试可选
-
启动Redis
# root执行 # 使用systemctl管控,服务名:redis systemctl enable redis # 开机自启 建议设置 systemctl disable redis # 关闭开机自启 systemctl start redis # 启动 systemctl stop redis # 关闭 systemctl status redis # 查看状态
-
放行防护墙,redis使用端口6379
# 方式1(推荐),关闭防火墙 systemctl stop firewalld # 关闭 systemctl disable firewalld # 关闭开机自启 #方式2,放行6379端口 firewall-cmd --add-port=6379/tcp --permanent #放行tcp规则下的6379端口,永久生效 firewall-cmd --reload
-
进入redis服务(命令行客户端)
# 执行redis-cli [womeng@centos ~]$ redis-cli 127.0.0.1:6379>AUTH 你的密码 # 没有设置密码请跳过 OK 127.0.0.1:6379> ping PONG
Redis图形化界面 RedisDesktopManager(RDM)
下载链接:https://github.com/lework/RedisDesktopManager-Windows/releases/download/2022.5/resp-2022.5.zip
安装:双击解压后的文件,除了要修改安装位置,其他一路点击下一步即可
连接Redis
-
双击运行RDM
-
在弹出的窗口点击 连接到Redis服务器
-
点击连接设置
-
连接名随意,地址填写Linux虚拟机的地址(Linux终端输入ifconfig可知),端口不变,密码跟配置文件的一样
-
测试连接
-
成功 —> 点击确定,完成连接
-
失败 —> 检查安装步骤是否有漏
Redis常见命令
Redis数据结构
Redis是一个key-value的数据库,key一般是String类型,不过value的类型多种多样
数据类型 | 举例 |
---|---|
String | hello world |
Hash | {name: "Jack" , age : 21} |
List | [A -> B -> C -> D] |
Set | {A , B , C} |
SortedSet | {A : 1 , B : 2 , C : 3} |
GEO | {A : (120.3 , 30.5)} |
BitMap | 0110110101110101011 |
HyperLog | 0110110101110101011 |
基本类型:
-
String:字符串
-
Hash:哈希表
-
List:有序集合,本质是链表
-
Set:无序集合,不可重复
-
SortedSet:有序集合,不可重复
特殊类型:
-
GEO:用于存储地理位置信息
-
BitMap:基于字符串类型,可以将字符串看作是一个由二进制位组成的数组
-
HyperLog:一种概率数据结构,用于估算集合的基数(即集合中不同元素的数量)
不同数据类型有不同的指令
Redis通用命令
通用指令是部分数据类型都可以使用的指令,常见的有
官方文档:Commands | Docs (redis.io)
命令行查看:
127.0.0.1:6379> help @generic
PS:使用指令前可以输入 help [command]
来查看指令的具体用法,按Tab键可以快速匹配指令
-
KEYS
:查看符合模板的所有key(类似MySQL模糊查询),不建议在生产环境设备上使用127.0.0.1:6379> help KEYS KEYS pattern summary: Find all keys matching the given pattern since: 1.0.0 group: generic
127.0.0.1:6379> KEYS *
1) "mykey"
2) "name"
127.0.0.1:6379> KEYS n*
1) "name"
-
DEL
:删除一个或多个指定的key,返回成功删除的键的个数127.0.0.1:6379> help DEL DEL key [key ...] summary: Delete a key since: 1.0.0 group: generic
127.0.0.1:6379> KEYS * 1) "k1" 2) "k2" 3) "k3" 4) "name" 127.0.0.1:6379> DEL k1 k2 k3 k4 (integer) 3
-
EXISTS
:判断一个或多个key是否存在,返回存在的key的个数127.0.0.1:6379> help EXISTS EXISTS key [key ...] summary: Determine if a key exists since: 1.0.0 group: generic
127.0.0.1:6379> KEYS * 1) "name" 127.0.0.1:6379> EXISTS name age (integer) 1
-
EXPIRE
:给一个key设置有效期,有效期到期时该key会被自动删除。如果不设置,表示该key永久有效 -
TTL
:查看一个key的剩余有效期
之所以有
EXPIRE
这个命令,是为了节省内存空间的,如果长时间不清理,内存就会被占满,影响Redis的效率
127.0.0.1:6379> KEYS *
1) "name"
127.0.0.1:6379> ttl name
(integer) -1 # -1 表示永久有效
127.0.0.1:6379> EXPIRE name 15
(integer) 1
127.0.0.1:6379> ttl name
(integer) 14
127.0.0.1:6379> ttl name
(integer) 11
127.0.0.1:6379> ttl name
(integer) 6
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) -2 # -2表示不存在
127.0.0.1:6379> KEYS *
(empty list or set)
String类型
String类型,也就是字符串类型,是Redis中最简单的存储类型。
其value是字符串,不过根据字符串的格式不同,又分为3类:
-
String:普通字符串,
-
int:整数类型,可以自增、自减操作
-
float:浮点类型,可以自增、自减操作
不管是那种格式,底层都是字节数组形式存储,只不过是编码方式不同。
数值类型的字符串,在存储时会转换成二进制形式的数据,这样一个字节就可以存储很大的数值,可以更节省空间;
普通字符串只能是将字符转为对应的字节码,然后进行存储,相对来讲会更加花费空间。
字符类型的最大空间不能超过512m。
KEY | VALUE |
---|---|
msg | hello world |
num | 10 |
score | 92.5 |
String类型常见命令
增改查:删用 DEL
-
SET:添加或这修改已经存在的一个String类型的键值对
-
GET:根据key获取String类型的value
-
MSET:批量添加多个String类型的键值对
-
MGET:根据多个key获取多个String类型的value
数值操作:
-
INCR:让一个整型的key自增1
-
INCRBY:让一个整型的key自增并指定步长(可以指定负值,下同),如:incrby num 2 让num值自增2
-
INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
组合命令:
-
SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行。等同于
set key value nx
(推荐使用) -
SETEX:添加一个String类型的键值对,并指定有效期。等同于
set key value ex [time]
Key的层级结构
Redis中没有MySQL中的Table的概念,不过Redis允许有多个单词形成层级结构来区分不同的数据(如,用户,订单),多个单词之间用 :
隔开,示例格式;
项目名:业务名:类型:id
例如,项目名为test,有user和product两种不同的数据,可以这样定义
-
user相关的key:
test:user:1
-
product相关的key:
test:product:1
如果Value是一个Java对象,例如一个User对象,则可以将对象序列化为JSON格式的字符串后存储:
KEY | VALUE |
---|---|
test:user:1 | {"id":1,"name":"jack","age":21} |
test:product:1 | {"id":1,"name":"小米","price":4999} |
演示:
127.0.0.1:6379> MSET test:user:1 '{"id":1,"name":"jack","age":21}' test:product:1 '{"id":1,"name":"小米","price":4999}'
OK
127.0.0.1:6379> MSET test:user:2 '{"id":1,"name":"jack","age":21}' test:product:2 '{"id":1,"name":"小米","price":4999}'
OK
127.0.0.1:6379> KEYS *
1) "k1"
2) "test:user:2"
3) "k2"
4) "test:product:2"
5) "k3"
6) "test:product:1"
7) "test:user:1"
PS:在RDM中可以更好的查看存储效果
Hash类型
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构
String结构是将对象序列化为JSON字符串后存储,当需要修改对某个字段时很不方便:要么重新赋值,要么删了重建
KEY | VALUE |
---|---|
test:user:1 | {"id":1,"name":"Jack","age":21} |
test:user:2 | {"id":2,"name":"Mike","age":18} |
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段作CRUD:
Hash类型常用命令
增改查:删用 DEL
-
HSET key field value :添加或修改hash类型key的field值
-
HGET key field:获取一个hash类型key的field值
-
HMSET:批量添加多个hash类型key的field值
-
HMGET:批量获取多个hash类型key的field值
-
HGETALL:获取一个hash类型的key中所有的field和value
-
HKEYS:获取一个hash类型的key中所有的field
-
HVALS:获取一个hash类型的key中所有的value
数值操作:
-
HINCRBY:让一个hash类型的key的字段值自增并指定步长
组合命令:
-
HSETNX:添加hash类型key的field值,前提是这个field不存在,否则不执行。
演示:
127.0.0.1:6379> HSET test:user:3 name Tom
(integer) 1
127.0.0.1:6379> HSET test:user:3 age 22
(integer) 1
127.0.0.1:6379> HMSET test:user:4 name Jonh age 21 gender male
OK
127.0.0.1:6379> KEYS *
1) "test:user:3"
2) "k3"
3) "test:product:2"
4) "test:user:4"
5) "k1"
6) "k2"
7) "test:user:2"
8) "test:product:1"
9) "test:user:1"
List类型
Redis中的List类型于Java中的LinkedList类似,可以看作是一个双向链表结构。既支持正向检索也反向检索
特征也与LinkedList类似:
-
有序
-
元素可重复
-
插入和删除快
-
查询速度一般
常用来存储一些有序数据,例如:朋友圈点赞列表,评论列表等
List常用命令
-
LPUSH key element ... :向列表左侧插入一个或多个元素
-
LPOP key:移除并返回列表左侧的第一个元素,没有则返回
nil
(没错,就是nil) -
RPUSH key element ... :向列表右侧插入一个或多个元素
-
RPOP key:移除并返回列表右侧的第一个元素,没有则返回
nil
-
LRANGE key start end:返回一段角标范围内的所有元素
-
BLPOP和BRPOP:与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回
nil
演示:
127.0.0.1:6379> LPUSH users 1 2 3
(integer) 3
127.0.0.1:6379> RPUSH users 4 5 6
(integer) 6
127.0.0.1:6379> LPOP users
"3"
127.0.0.1:6379> RPOP users
"6"
127.0.0.1:6379> LRANGE users 1 3
1) "1"
2) "4"
3) "5"
Set类型
Redis的Set结构与Java中的HashSet类似,可以看作是一个value为null的额HashMap。因为也是一个hash表,因此也具备与HashSet类似的特征:
-
无序
-
元素不可重复
-
查找快
-
支持交集、并集、差集等功能
Set类型常见命令
单集合操作:
-
SADD key member ...:向set中添加一个或多个元素
-
SREM key member ...:移除set中的在指定元素
-
SCARD key:返回set中元素的个数
-
SISMENBER key member:判断一个元素是否存在于set中
-
SMEMBERS:获取set中的所有元素
多集合操作:
-
SINTER key1 key2 ...:求key1于key2的交集
-
SDIFF key1 key2 ...:求key1于key2的差集
-
SUNION key1 key2 ...:求key1合key2的并集
Set命令练习
将下列数据用Redis的Set集合来存储:
-
张三的好友有:李四、王五、赵六
SADD ZSfs lisi wangwu zhaoliu
-
李四的好友有:王五、麻子、二狗
SADD LSfs wangwu mazi ergou
利用Set的命令实现以下功能:
-
计算张三的好友有几个
127.0.0.1:6379> SCARD ZSfs (integer) 3
-
计算张三和李四有哪些共同好友
127.0.0.1:6379> SINTER ZSfs LSfs 1) "wangwu"
-
查询哪些是张三的好友却不是李四的好友
127.0.0.1:6379> SDIFF ZSfs LSfs 1) "zhaoliu" 2) "lisi"
-
查询张三和李四的好友总共有哪些人
127.0.0.1:6379> SUNION ZSfs LSfs 1) "ergou" 2) "lisi" 3) "wangwu" 4) "zhaoliu" 5) "mazi"
-
判断李四是否是张三的好友
127.0.0.1:6379> SISMEMBER ZSfs lisi (integer) 1
-
判断张三是否是李四的好友
127.0.0.1:6379> SISMEMBER LSfs zhangsan (integer) 0
-
将李四从张三的好友列表中移除
127.0.0.1:6379> SREM ZSfs lisi (integer) 1
SortedSet类型
Redis的SortedSet是一个可排序的set集合,于Java中的TreeSet类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加hash表。
SortedSet具备下列特性:
-
可排序
-
元素不重复
-
查询速度快
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
SortedSet的常见命令
-
ZADD key score member:添加一个或多个元素到sorted set,如果已经存在则更新其score值
-
ZREM key member:删除sorted set中的一个指定元素
-
ZSCORE key member:获取sorted set中指定元素的score值
-
ZRANK key member:获取sorted set中指定元素的排名(升序,降序为
ZREV
前缀) -
ZCARD key:获取sorted set中的元素个数
-
ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
-
ZINCRBY key increment member:让sorted set中的指定元素自增,步长为指定的increment值
-
ZRANGE key min max:按照score排序后,获取指定排名范围内的元素(升序,降序为
ZREV
前缀) -
ZRANGEBYSCORE key min max:按照score排序后,获取指定score范围内的元素(升序,降序为
ZREV
前缀) -
ZDIFF、ZINTER、ZUNION:求差集、交集、并集
SortedSet命令练习
将班级的下列学生得分存入到Redis的SortedSet中
Jack 85 ,Lucy 89 ,Rose 95 ,Tom 78 , Amy 82 ,Miles 76
127.0.0.1:6379> ZADD class1 85 Jack 89 Lucy 95 Rose 78 Tom 82 Amy 76 Miles
(integer) 6
实现下列功能:
-
删除Tom同学
127.0.0.1:6379> ZREM class1 Tom (integer) 1
-
获取Amy同学的分数
127.0.0.1:6379> ZSCORE class1 Amy "82"
-
获取Rose同学的排名
127.0.0.1:6379> ZRANK class1 Rose # 升序排名 从 0 开始 (integer) 4 127.0.0.1:6379> ZREVRANK class1 Rose # 降序排名 (integer) 0
-
查询80分以下有几个同学
127.0.0.1:6379> ZCOUNT class1 0 80 (integer) 1
-
给Amy同学加2分
127.0.0.1:6379> ZINCRBY class1 2 Amy "84"
-
查出成绩前三名的同学
127.0.0.1:6379> ZREVRANGE class1 0 2 # 降序排序 1) "Rose" 2) "Lucy" 3) "Jack"
-
查出成绩80分以下的所有同学
127.0.0.1:6379> ZRANGEBYSCORE class1 0 80 1) "Miles"
Redis的Java客户端
各种客户端
在Redis官网中提供了各种语言的额客户端,地址:Connect with Redis clients | Docs
客户端 | 描述 |
---|---|
jedis | 优点:以Redis命令作为方法名称,学习成本低,简单实用。 缺点:jedis实例是线程不安全的,多线程环境下需要基于连接池来使用 |
lettuce | lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且式线程安全的。支持Redis的哨兵模式、集群模式和管道模式 |
Redisson | Redisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合。包含了诸如Map、Queue、Lock、Semaphore、AtomicLong等强大功能 |
本教程会侧重于jedis和lettuce的学习,以及整合了这两种客户端的Spring Data Redis。
Jedis
快速入门
官网地址:redis/jedis: Redis Java client (github.com)
-
新建maven项目,在pom.xml引入以下依赖:
<dependencies> <!--jedis:Redis的Java客户端--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>5.0.0</version> </dependency> <!--单元测试--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency> </dependencies>
-
建立连接
@BeforeEach void setUp(){ //1.建立连接 jedis = new Jedis("你的Redis服务器地址", 6379); //2.设置密码(跟Redis配置文件一致) jedis.auth("密码"); //3.选择库 jedis.select(0); }
-
编写测试用例
//测试字符串类型 @Test void testString(){ //存入数据 String result = jedis.set("name", "makabaka"); System.out.println("result = " + result); //获取数据 String name = jedis.get("name"); System.out.println("name = " + name); }
-
测试完成,断开连接
@AfterEach void tearDown(){ //测试完成后,断开连接 if (jedis != null){ jedis.close(); } }
-
运行测试用例,控制台输出
result = OK name = makabaka
-
完整测试类代码(在路径src/test/java新建 test 包 )
package test; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.Jedis; public class JedisTest { private Jedis jedis; @BeforeEach void setUp(){ //1.建立连接 jedis = new Jedis("你的Redis服务器地址", 6379); //2.设置密码(跟Redis配置文件一致) jedis.auth("你的密码"); //3.选择库 jedis.select(0); } @AfterEach void tearDown(){ //测试完成后,断开连接 if (jedis != null){ jedis.close(); } } //测试字符串类型 @Test void testString(){ //存入数据 String result = jedis.set("name", "makabaka"); System.out.println("result = " + result); //获取数据 String name = jedis.get("name"); System.out.println("name = " + name); } }
Jedis连接池
jedis本身时线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐使用连接池代替jedis的直连方式。
-
在 src/main/java 路径下新建 jedis.util包,创建JedisConnectionFactory工具类
package jedis.util; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisConnectionFactory { private static final JedisPool jedisPool; static { // 配置连接池 JedisPoolConfig poolConfig = new JedisPoolConfig(); // 最大连接数 poolConfig.setMaxTotal(8); // 最大空闲连接 poolConfig.setMaxIdle(8); // 最小空闲连接 poolConfig.setMinIdle(0); // 等待时常 ms poolConfig.setMaxWaitMillis(1000); // 创建连接池对象 jedisPool = new JedisPool(poolConfig,"你的Redis服务器地址",6379,1000,"密码"); } public static Jedis getJedis(){ return jedisPool.getResource(); } }
-
替换jedis的获取方式
@BeforeEach void setUp(){ //1.从JedisConnectionFactory中获取jedis jedis = JedisConnectionFactory.getJedis(); //2.选择库 jedis.select(0); }
SpringDataRedis
SpringData是Spring中数据操作的模板,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis
SpringDataRedis的特点:
-
提供了对不同Redis客户端的整合(Lettuce和jedis)
-
提供了RedisTemplate统一API来操作Redis
-
支持Redis的发布定于模型
-
支持Redis哨兵和Redis集群
-
支持基于Lettuce的响应式编程
-
支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
-
支持基于Redis的JDKCollection实现
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装在不同的类型中:
API | 返回值类型 | 说明 |
---|---|---|
redisTemplate.opsForValue() | ValueOperations | 操作String类型数据 |
redisTemplate.opsForHash() | HashOperations | 操作Hash类型数据 |
redisTemplate.opsForList() | ListOperation | 操作List类型数据 |
redisTemplate.opsForSet() | SetOperation | 操作Set类型数据 |
redisTemplate.opsForZSet() | ZSetOperation | 操作SortedSet类型数据 |
redisTemplate | 通用命令 |
SpringDataRedis快速入门
SpringBoot以及提供了对SpringDataRedis的支持,使用非常简单:
-
新建SpringBoot项目,引入依赖
<!--redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--common-pool--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
-
在application.yml(默认为.properties后缀,可自行重命名)中配置Redis的信息
spring: # Redis配置 data: redis: host: Redis服务器地址 password: 密码 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: 100ms
-
在要使用的地方注入RedisTemplate
@Autowired private RedisTemplate redisTemplate;
-
测试类完整代码
package com.womeng.springbootredisdemo; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; @SpringBootTest class SpringBootRedisDemoApplicationTests { @Autowired private RedisTemplate redisTemplate; @Test void testString() { //写入一条String数据 redisTemplate.opsForValue().set("name","卧梦"); //获取String数据 Object name = redisTemplate.opsForValue().get("name"); System.out.println("name = " + name); } }
SpringDataRedis的序列化方式
RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认采用JDK序列化,得到的结果是这样的:
127.0.0.1:6379> set name Jack
OK
127.0.0.1:6379> get name
"Jack"
127.0.0.1:6379> KEYS *
1) "test:user:3"
2) "test:product:2"
3) "k3"
4) "test:user:2"
5) "k2"
6) "users"
7) "class1"
8) "ZSfs"
9) "test:product:1"
10) "LSfs"
11) "name"
12) "s1"
13) "\xac\xed\x00\x05t\x00\x04name" # 新建的name在这
14) "test:user:4"
15) "k1"
16) "test:user:1"
它没有覆盖原有的name
,而是新建了另外一个带前缀的name
缺点:
-
可读性差
-
内存占用大
我们可以自定义RedisTemplate的序列化方式,新建配置类,代码如下:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
// 创建RedisTemplate
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置连接工厂
template.setConnectionFactory(connectionFactory);
// 创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 设置key的序列化
template.setKeySerializer(RedisSerializer.string()); // 一般key都是String类型
template.setHashKeySerializer(RedisSerializer.string()); // 所以设置为专门处理String的序列化工具
// 设置Value的序列化
template.setValueSerializer(jsonRedisSerializer); // value可以是自定义对象
template.setHashValueSerializer(jsonRedisSerializer); // 设置为专门json的更合适
// 返回
return template;
}
}
修改注入代码:
@Autowired
private RedisTemplate<String,Object> redisTemplate;
重新运行测试用例,修改成功
PS:如果报错,引入以下依赖:
<!--Jackson处理依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
这是因为新建SpringBoot项目时,没有引入SpringMVC依赖,缺少了json的处理工具,就需要手动引入json的处理依赖。
另外,也可以自定义实体类来测试,RedisTemplate可以自动完成对象的序列化和反序列化。代码如下:
User.java:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private Integer age;
}
在测试类新建测试用例:
@Test
void testSaveUser(){
// 写入数据
redisTemplate.opsForValue().set("user:100",new User("womeng",21));
// 获取数据
User user = (User) redisTemplate.opsForValue().get("user:100");
System.out.println("user = " + user);
}
运行测试用例,控制台输出
user = User(name=womeng, age=21)
Redis数据存储:
StringRedisTemplate
在存储实体类的时候,自带json的序列化工具会多存入一条数据,用于自动反序列化,即
"@class": "com.womeng.springbootredisdemo.pojo.User"
这条数据甚至比本身要存数据占用的空间还大,在大量存储数据的时候会造成严重的内存浪费。因此不建议使用默认的JSON序列化工具,推荐统一作String类型处理,手动实现实体类的序列化和反序列化。
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String。省去了我们自定义RedisTemplate的过程:
@SpringBootTest
class RedisStringTests {
//到如Spring提供的StringRedisTemplate 无需自定义
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void testString() {
//写入一条String数据
stringRedisTemplate.opsForValue().set("name","卧梦");
//获取String数据
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
// 写入数据
User user = new User("makabaka", 21);
// 手动序列化
String json = mapper.writeValueAsString(user);
stringRedisTemplate.opsForValue().set("user:200",json );
// 获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
// 手动反序列化
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}
}
存储结果