超详细 Redis 入门教程
List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),也可以获取指定范围指定下标的元素等。一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过 40 亿个元素)。
两个特点:
1.列表中的元素是有序的,可以通过索引下标获取某个元素霍某个某个范围内的元素列表
2.列表中的元素可以是重复的
使用场景: 消息队列、栈、文章列表等。
常用指令:
添加操作
lpush/rpush key value1[value2…]: 从左边/右边向列表中 PUSH 值(一个或者多个)
lpushx/rpushx key value: 向已存在的列名中 push 值(一个或者多个),list 不存在 lpushx 失败
linsert key before|after pivot value: 在指定列表元素的前/后 插入 value
查找操作
lindex key index: 通过索引获取列表元素
lrange key start end: 获取 list 起止元素 (索引从左往右 递增)
llen key: 查看列表长度
删除操作
lpop/rpop key: 从最左边/最右边移除值 并返回
lrem key count value: count >0:从头部开始搜索 然后删除指定的 value 至多删除 count 个 count < 0:从尾部开始搜索… count = 0:删除列表中所有的指定 value。
ltrim key start end: 通过下标截取指定范围内的列表
rpoplpush source destination: 将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部
修改操作
lset key index value: 通过索引为元素设值
阻塞操作
blpop/brpop key1[key2] timout: 移出并获取列表的第一个/最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
brpoplpush source destination timeout: 和 rpoplpush 功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
代码示例:
Set(集合)
Redis 的 Set 是 string 类型的无序集合,我们不能通过索引获取元素。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储 40 多亿个成员)。
应用场景: 标签(tag)
常用命令:
集合内操作
sadd key member1[member2…]: 向集合中无序增加一个/多个成员
srem key member1[member2…]: 移除集合中一个/多个成员
scard key: 获取集合的成员数
smembers key: 返回集合中所有的成员
sismember key member: 查询 member 元素是否是集合的成员,若存在返回 1,不存在返回 0
srandmember key [count]: 随机返回集合中 count 个成员,count 缺省值为 1
spop key [count]: 随机移除并返回集合中 count 个成员,count 缺省值为 1
集合间操作
sinter key1 [key2…]: 返回所有集合的交集
sinterstore destination key1[key2…]: 在 SINTER 的基础上,存储结果到集合中。覆盖
sunion key1 [key2…]: 返回所有集合的并集
sunionstore destination key1 [key2…]: 在 SUNION 的基础上,存储结果到及和张。覆盖
sdiff key1[key2…]: 返回所有集合的差集 key1- key2 - …
sdiffstore destination key1[key2…]: 在 SDIFF 的基础上,将结果保存到集合中。覆盖
smove source destination member: 将 source 集合的成员 member 移动到 destination 集合
sscan key [MATCH pattern] [COUNT count]: 在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分
代码示例:
Hash(哈希)
几乎所有的编程语言都提供了哈希(hash)结构,Redis 中 hash 是一个 string 类型的 field 和 value 的映射表 value={{field1,value1},{field2,value2}…},可以将一个 Hash 表作为一个对象进行存储,表中存放对象的信息。
应用场景: 用户信息缓存
常用命令:
hset key field value: 将哈希表 key 中的字段 field 的值设为 value。重复设置同一个 field 会覆盖,返回 0
hmset key field1 value1 [field2 value2…]: 同时将多个 field-value (域-值)对设置到哈希表 key 中。
hsetnx key field value: 只有在字段 field 不存在时,设置哈希表字段的值。
hget key field value: 获取存储在哈希表中指定字段的值
hmget key field1 [field2…]: 获取所有给定字段的值
hexists key field: 查看哈希表 key 中,指定的字段是否存在。
hdel key field1 [field2…]: 删除哈希表 key 中一个/多个 field 字段
hlen key: 获取哈希表中字段的数量
hkeys key: 获取所有字段 field
hvals key: 获取哈希表中所有值 value
hgetall key: 获取在哈希表 key 的所有字段和值
hincrby key field n: 为哈希表 key 中的指定字段的整数值加上增量 n,并返回增量后结果 一样只适用于整数型字段
hincrbyfloat key field n: 为哈希表 key 中的指定字段的浮点数值加上增量 n。
hscan key cursor [MATCH pattern] [COUNT count]: 迭代哈希表中的键值对。
代码示例:
Zset(有序集合)
在有序集合中保留了不能有重复成员的特性,但其中的成员是可以排序的,每一个元素都会关联一个 double 类型的分数(score)作为排序依据,score 相同时按字典顺序排序。redis 正是通过分数来为集合中的成员进行从小到大的排序。
应用场景: 排行榜系统,成绩单,工资表
常用命令:
集合内
zadd key score member1 [score2 member2]: 向有序集合添加一个或多个成员,或者更新已存在成员的分数
zcard key: 获取有序集合的成员数
zscore key member: 返回有序集中,成员的分数值
zcount key min max: 计算在有序集合中指定区间 score 的成员数
zlexcount key min max: 在有序集合中计算指定字典区间内成员数量
zincrby key n member: 有序集合中对指定成员的分数加上增量 n
zscan key cursor [MATCH pattern] [COUNT count]: 迭代有序集合中的元素(包括元素成员和元素分值)
范围查询
zrank key member: 返回有序集合中指定成员的索引
zrevrank key member: 返回有序集合中指定成员的索引,从大到小排序
zrange key start end: 通过索引区间返回有序集合成指定区间内的成员
zrevrange key start end: 通过索引区间返回有序集合成指定区间内的成员,分数从高到底
zrangebylex key min max: 通过字典区间返回有序集合的成员
zrevrangebylex key max min: 按字典顺序倒序返回有序集合的成员
zrangebyscore key min max: 返回有序集中指定分数区间内的成员 -inf 和 +inf 分别表示最小最大值,只支持开区间
zrevrangebyscore key max min: 返回有序集中指定分数区间内的成员,分数从高到低排序
删除操作
zrem key member1 [member2…]: 移除有序集合中一个/多个成员
zremrangebylex key min max: 移除有序集合中给定的字典区间的所有成员
zremrangebyrank key start stop: 移除有序集合中给定的排名区间的所有成员
zremrangebyscore key min max: 移除有序集合中给定的分数区间的所有成员
集合间操作
zinterstore destination numkeyskey1 [key2 …]: 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中,numkeys:表示参与运算的集合数,将 score 相加作为结果的 score
zunionstore destination numkeys key1 [key2…]: 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
代码示例:
Redis 提供了基于“发布/订阅”模式的消息机制,在这种模式下,消息发布者和订阅者不进行直接通信,发布者向指定频道(channel)中发布消息,订阅该频道的客户端都可以接收该消息。
Redis 中提供若干命令支持发布订阅功能:
发布消息给指定频道: publish channel message
从一个或多个频道中订阅消息:subscribe channel [channel …]
取消订阅指定的频道:unsubscribe [channel [channel …]]
按照模式订阅频道:psubscribe pattern [pattern …]
按照模式取消订阅频道:punsubscribe [pattern [pattern …]]
查阅订阅的频道:
查看活跃频道:pubsub channels [pattern]
查看频道订阅数:pubsub numsub [channel …]
查看模式订阅数:pubsub numpat
测试发布订阅:
客户端在执行订阅命令后进入订阅状态,只能接受 subscribe、psubscribe、unsubscribe、punsubscribe 四个命令。新开启的订阅客户端无法收到频道之前的消息。
测试查询订阅:
使用场景:聊天室、公告牌、服务之间利用信息解耦等
Redis 事务的本质是一组命令的集合,一次执行多个指令,事务中所有命令都被序列化,其他客户端提交的命令请求不会插入到事务执行命令序列中,简单来说:要不全执行,要不全不执行。Redis 中提供了简单的事务功能,需要 multi 和 exec 两个命令实现。
事务执行流程:
开启事务(multi)
命令入队(…)
执行事务(exec)| 取消事务(discard)
在 Redis 的事务中的命令出现不同错误时,处理机制也会有所差异:
编译型异常(命令错误),事务中所有命令都不会执行,因为 Redis 没有隔离级别的概念,队列中的命令没有提交之前都不会实际的被执行:
运行时异常(1/0),如果事务队列中存在语法性错误,那么执行命令的时候,其它命令是可以正常执行的,错误命令抛出异常,由此看出 Redis 的单条命令保证原子性,但是事务并不保证原子性,不支持回滚功能。
通过事务实现上锁:
在有些应用场景需要在事务之前,确保事务中的 key 没有被其它客户端修改过,才执行事务,否则不执行,着类似于乐观锁的概念,在 Redis 中也提供了相关实现方法:
监视、加锁(watch)
取消监视、解锁(unwatch)
乐观锁:
假设数据一般情况不会造成冲突(很乐观,认为什么时候都不会出问题),在数据提交更新时正式对数据的冲突与否进行检测,发现了冲突,发出错误信息,让用户决定如何处理,适用于读操作多的场景,可以提高程序的吞吐量。
为了避免数据库的幻读,业务处理时间过长,乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性
实现方法:
当有多个线程在操控 redis 的时候,被 watch 监视的 key 值如果发生改变,正在进行的事务将会失败,每次加锁后都要进行解锁,再加锁去重新获取最新的值
正常流程:
出现线程问题:
Redis 是一个内存数据库,当 redis 服务器重启,获取电脑重启,数据会丢失,我们可以将 redis 内存中的数据持久化保存到硬盘的文件中。目前 Redis 支持的存储机制有 RDB 和 AOF。
RDB 机制
RDB 持久化是把当前进程数据生成快照保存到硬盘的过程,触发 RDB 的过程分为手动触发和自动触发。
触发机制:
1.通过 save 与 bgsave 命令手动触发
save 命令:阻塞当前 Redis 服务器,直到 RDB 过程完成为止,对于内存比较大的实例会造成长时间的阻塞,线上环境不建议使用。
bgsave 命令:Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,一般时间很短(微秒级别)。
2.通过配置文件自动触发
redis 的配置文件 redis.windwos.conf 中有一下默认内容:
#900 秒内,至少有一个 key 进行了修改,就进行持久化操作
save 900 1
#300 秒内,至少有 10 个 key 进行了修改,就进行持久化操作
save 300 10
#60 秒内,至少有 1000 个 key 进行了修改,就进行持久化操作
save 60 10000
流程说明:
1.父进程执行 fork 操作创建子进程
2.子进程创建 RDB 文件,根据父进程内存生成临时快照文件,完成后对原来文件进行原子替换。
3.进程发送信号给父进程表示完成,父进程更新统计信息。
整个过程主进程不进行任何 io 操作,保证了性能,如果进行大规模数据恢复,RDB 和 AOP 都可以进行数据恢复,RDB 数据恢复完整性不敏感,RDB 更加高效,缺点时最后一次持久化后的数据可能丢失,默认使用的就是 RDB,一般情况不需要修改这个配置。
RDB 文件处理:
RDB 文件保存在 dir 配置指定的目录下,文件名通过 dbfilename 配置指定,默认为一个 dump.rdb 文件(在生产环境中最好对 dump.rdb 文件进行备份)。可以通过执行config set dir {newDir}
和config set dbfilename {newFileName}
运行期动态执行,当下次运行时会保存到新目录。
如果 redis 加载的 RDB 文件损坏时拒绝启动,可以使用 Redis 提供的redis-check-dump
工具检测 RDB 文件并获得对应的错误报告。
RDB 优点:
RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据快照。非常适合备份:父进程正常处理用户请求,fork 分支一个子进程进行备份。
适合大规模的数据恢复,如果服务器宕机了,不要删除 rdb 文件,重启自然在目录下,自动会读取,并且数据恢复速度远大于 AOF 方式。
RDB 缺点:
RDB 并不能做到实时持久化,存储需要一定的时间间隔,可以自行修改设置。如果 redis 意外宕机,最后一次的修改数据会丢失。
fork 进程的时候,会占用一定的内存空间。
RDB 使用特定的二进制格式保存,可能存在新老版本不兼容问题。
AOF 机制
AOF(append only file)持久化: 以独立日志的方式记录每次执行的命令(除读操作外),重启时重新执行 AOF 文件中的命令来恢复文件。(aof 默认是文件无限追加,大小会不断扩张,如果是文件过大就需要写很久)
开启 AOF 需要我们设置配置:appendonly yes,默认不打开。文件名通过 appendfilename 配置设置,默认文件名为 appendonly.aof。
执行流程:
命令写入(append):所有命令会追加到 aof_buf(缓冲区)中。
文件同步(sync):AOF 缓冲区根据对应的策略向硬盘做同步操作。
文件重写(rewrite):随着文件越来越大,需要定期对 AOF 文件进行重写,达到压缩目的。
重启加载(load):重启时,可以加载 AOF 文件进行数据恢复
AOF 文件损坏:
加载损坏的 AOF 文件时会拒绝启动,并打印日志。我们可以先备份文件,后采用 redis-check-aof --fix 命令修复,修复后使用 diff -u 对比数据的差异,找出丢失的数据,有些可以进行人工修复。
AOF 可能会出现结尾保存不完整的情况,比如机器断电导致结尾命令没有写全。Redis 提供了 aof-load-truncated 配置来兼容这种情况,默认开启。这样在加载出问题时会继续启动,并打印警告日志。
Jedis
Java 有很多优秀的 Redis 客户端,使用较为广泛的是客户端 Jedis,官方推荐的操作 Redis 的中间件。
使用步骤:
导入 jedis 的 jar 包
(依赖:redis.clients.jedis.3.3.0)
建立连接:
Jedis jedis = new Jedis("localhost",6379);
操作:
jedis.set("key","value");
关闭连接,或者直接就使用 jedis 连接池:
jedis.close();
直接连接与连接池方式的比较:
| | 优点 | 缺点 |
| --- | --- | --- |
| 直连 | 简单方便,适用与少量长期连接的场景 | 存在每次新建/关闭 TCP 连接的开销,资源无法控制,可能出现连接泄露。并且 Jedis 对象处于线程不安全的状态 |
| 连接池 | 无需每次都生成 Jdeis 对象,降低开销,并且连接池的形式可以有效的保护和控制系统资源 | 相对于直连,使用相对麻烦,在资源管理上需要很多参数来保证,一旦规划不合理会出现问题 |
搭建工程:
引入依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
测试连接:
public class Ping {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println("连接成功");
//查看服务是否运行
System.out.println("服务正在运行: "+jedis.ping());
}
}
运行结果:连接成功
Jedis 中的 API 接口名和操作名一样,根据 API 文档使用就行:
基本测试:
public class TestJedis {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
System.out.println("清空数据:"+jedis.flushDB());
System.out.println("判断某个键是否存在:"+jedis.exists("username"));
System.out.println("新增<'username','ly'>的键值对:"+jedis.set("username", "ly"));
System.out.println("新增<'password','123456'>的键值对:"+jedis.set("password", "123456"));
System.out.print("系统中所有的键如下:");
Set<String> keys = jedis.keys("*");
System.out.println(keys);
System.out.println("删除键 password:"+jedis.del("password"));
System.out.println("判断键 password 是否存在:"+jedis.exists("password"));
System.out.println("查看键 username 所存储的值的类型:"+jedis.type("username"));
System.out.println("随机返回 key 空间的一个:"+jedis.randomKey());
System.out.println("重命名 key:"+jedis.rename("username","name"));
System.out.println("取出改后的 name:"+jedis.get("name"));
System.out.println("按索引查询:"+jedis.select(0));
System.out.println("删除当前选择数据库中的所有 key:"+jedis.flushDB());
System.out.println("返回当前数据库中 key 的数目:"+jedis.dbSize());
System.out.println("删除所有数据库中的所有 key:"+jedis.flushAll());
}
}
运行结果:
Jedis 实现事务控制:
import com.alibaba.fastjson.JSONObject;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class TestMulti {
public static void main(String[] args) {
//创建客户端连接服务端,redis 服务端需要被开启
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.flushDB();
JSONObject jsonObject = new JSONObject();
jsonObject.put("user1", "xiaoming");
jsonObject.put("user2", "xiaohong");
//开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
try{
//向 redis 存入一条数据
multi.set("json", result);
//再存入一条数据
multi.set("json2", result);
//这里引发了异常,用 0 作为被除数
//int i = 10/0;
//如果没有引发异常,执行进入队列的命令
multi.exec();
}catch(Exception e){
e.printStackTrace();
//如果出现异常,回滚
multi.discard();
}finally{
System.out.println(jedis.get("json"));
System.out.println(jedis.get("json2"));
//最终关闭客户端
jedis.close();
}
}
}
运行结果:
在开启事务期间添加异常,用 0 作为被除数int i = 10/0;
,再次测试结果:
Jedis 连接池: JedisPool
之前通过直连的方式使用 Jedis,由于 Redis 客户端使用的是 TCP 协议,所有每次都需要我们新建 TCP 连接,使用后在断开连接,对于频繁访问 Redis 的场景显然并不高效,在实际生产环境中一般使用连接池的方式对 Jedis 连接进行管理。
使用步骤:
Jedis 提供了 JedisPool 这个类作为对 Jedis 的连接池,使用时首先需要我们创建一个 JedisPool 连接池对象,之后通过调用方法 getResource()获取 Jedis 连接。
1.Jedis 连接池,通常 JedisPool 是单例的。
//0.创建一个配置对象
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(50);
config.setMaxIdle(10);
//1.创建 Jedis 连接池对象
JedisPool jedisPool = new JedisPool(config,"localhost",6379);
相关参数:
| 参数名 | 含义 | 默认值 |
| --- | --- | --- |
| MaxTotal | 最大连接数 | 8 |
| MaxIdle | 最大空闲连接数 | 8 |
| MinIdle | 最小空闲连接数 | 0 |
| MinEvictableIdleTimeMillis | 逐出连接的最小空闲时间 | 1800000 毫秒(30 分钟) |
| MaxWaitMillis | 获取连接时的最大等待毫秒数如果超时就抛异常, 小于零:阻塞不确定的时间 | -1 |
| TimeBetweenEvictionRunsMillis | 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程 | -1 |
| NumTestsPerEvictionRun | 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n) | 3 |
| Lifo | 是否启用后进先出 | true |
| TestOnBorrow | 在获取连接的时候检查有效性 | false |
| TestWhileIdle | 在空闲时检查有效性 | false |
| JmxEnabled | 是否启用 pool 的 jmx 管理功能 | true |
| EvictionPolicyClassName | 设置的逐出策略类名,默认当连接超过最大空闲时间,或连接数超过最大空闲连接数逐出 | DefaultEvictionPolicy |
| SoftMinEvictableIdleTimeMillis | 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据 MinEvictableIdleTimeMillis 判断 | 逐出策略 |
| BlockWhenExhausted | 连接耗尽时是否阻塞, false 报异常,ture 阻塞直到超时 | true |
2.从连接池获取对象,并使用:
//2.获取连接
Jedis jedis = jedisPool.getResource();
//3. 使用
jedis.set("key1","value1");
//4. 关闭 归还到连接池中
jedis.close();
自定义工具类:
在 resources 目录下新建 jedis.properties 配置文件:
host = 127.0.0.1
port = 6379
maxTotal= 50
maxIdle = 10
编写工具类:
public class JedisPoolUtils {
private static JedisPool jedisPool;
static{
//读取配置文件
InputStream is = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
//创建 Properties 对象
Properties pro = new Properties();
//关联文件
try {
pro.load(is);
} catch (IOException e) {
e.printStackTrace();
}
//获取数据,设置到 JedisPoolConfig 中
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));
//初始化 JedisPool
jedisPool = new JedisPool(config,pro.getProperty("host"),Integer.parseInt(pro.getProperty("port")));
}
/**
获取连接方法
*/
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
SpringBoot 整合 Redis
导入依赖 pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
编写配置文件 applacation.yml:
spring:
redis:
host: 127.0.0.1 # Redis 服务器地址
port: 6379 # Redis 服务器连接端口
password: # Redis 服务器连接密码(默认为空)
database: 0 # Redis 数据库索引(默认为 0)
jedis:
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 500 # 连接池中的最大空闲连接
min-idle: 0 # 连接池中的最小空闲连接
lettuce:
shutdown-timeout: 0ms
timeout: 1000 # 连接超时时间(毫秒)
测试:
@SpringBootTest
public class SpringbootRedisTest {
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("key1","value1");
redisTemplate.opsForValue().set("key2","张三");
System.out.println("key1="+redisTemplate.opsForValue().get("key1"));
System.out.println("key2="+redisTemplate.opsForValue().get("key2"));
}
}
运行结果:
查看 RedisTemplate 类:
测试代码:
@Test
public void test() throws JsonProcessingException {
//真实开发一般有使用 json 传递对象
User user =new User();
user.setName("龙源");
user.setAge(10);
String jsonUser = new ObjectMapper().writeValueAsString(user);
redisTemplate.opsForValue().set("user",jsonUser);
System.out.println(redisTemplate.opsForValue().get("user"));
}
//新建一个 User 类
@Component
public class User implements Serializable {//需要序列化 Serializable
private String name;
private int age;
/**
Get,Set,toString
**/
}
运行结果:
在客户端进行查看:发现 user 对象前面有转义字符
通过序列化解决问题:
编写配置类,自定义 RedisTemplate,修改默认的序列化方式:
@Configuration
public class RedisConfig {
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//为了方便开发,使用<String,Object>
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
//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);
//St
ring 的序列化
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;
}
}
再次测试:
//为了方便识别可以修改:RedisTemplate
评论