先讲原因:redisTemplate对象的key的序列化器是JdkSerializationRedisSerializer,导致redis服务器上的key的名字与代码中写的字符串不同了。使用代码中key去删除会失败,查找会找不到,须键序列化器相同。
本例中,代码中的redis Key是"v1:smc:login:info"
Redis服务器上的key的名字变为:
1) "\xac\xed\x00\x05t\x00\x11v1:smc:login:info"
原因知道了,下面来梳理这个问题的排查思路。
uat环境写了个bug,把一个错误的值写到key中。
快速fix后,需要删除这个key。
使用之前的删除接口,提示成功。
再次调试,发现还是旧值,很无语。
直接登录redis服务器删除。
发现没有这个key。
> del v1:smc:login:info
(integer) 0
使用keys命令,返回也是空
> keys v1:smc:login:info
(empty array)
是不是有两个Redis服务器,删除和查找不在同一个? 不是。是同一个。
决定本地调试一下。本地环境和uat环境用的相同的redis,想查看下这个RedisTemplate配置的是哪个Redis服务器,另外看看这个RedisTemplate是不是有特殊之处?发现这个项目中其它地方只用了StringRedisTemplate。
稳稳的复现:值取到了。仍然是第一次写入的错误值。
这个RedisTemplate配置的host,port,database没有不同。
不死心,又试了这个场景:
key是存在的。与之前的情况完全一致。
[9]> keys *smc*
1) "\xac\xed\x00\x05t\x00\x11v1:smc:login:info"
使用两边*号的查一下,居然找到了。
只是前面被加了些莫名其妙的前缀。
问了下DeepSeek:
该字符串是 Java 序列化二进制数据,头部结构为:
魔数(ACED) + 版本号(0005) + 字符串类型标识(74) + 长度(0011) + 内容(v1:smc:login:info)
该字符串的前面部分 \xac\xed\x00\x05t\x00\x11
并不是标准的 Unicode 编码文本,而是 Java 对象序列化协议的二进制数据头。以下是逐字节解析:
\xac\xed
这是 Java 序列化流的魔数(Magic Number),固定为 0xACED
,用于标识这是一个 Java 序列化对象流。\x00\x05
表示序列化协议的版本号(5
),对应 java.io.ObjectStreamConstants.STREAM_VERSION
。t
对应 ASCII 字符 t
(十六进制 0x74
),表示接下来是一个字符串对象(TC_STRING
类型标识符)。\x00\x11
字符串长度字段,十六进制 0x0011
表示十进制 17
,即后续字符串 v1:smc:login:info
的长度(恰好 17 个字符)。至此找到原因。
解决办法就很简单了。
方法1:使用本项目RedisTemplate对象的delete方法来删除:
用魔法打败魔法。能发起致命攻击的,都是最熟悉你的。
方法2:改下redisKey。
譬如,可以把"v1:smc:login:info"改为"v2:smc:login:info"
是不是可以继续测功能了。
删除接口在另一个项目中,与这个项目的RedisTemplate的序列化器不同。
没删除掉,为什么提示“deleteKeyCount 1”呢?
看了下代码:
看到原因了不?
此处需要改下一下:
直接使用redisTemplate.delete(key)的返回值就可以了。
为什么刚开始为什么没想到是Redis序列化器的问题。之前的项目中对RedisTempate进行特殊配置的。没想到这个项目没配,并且老代码中也一直在用。就没有质疑这个部分。
为什么删不掉时,没有想到是这个原因。
项目比较多,有几个项目用了Redis其它的database,也不确定这个是不是也是例外。在这个点上卡的时间有些长。
再加上,删除接口一直反馈“deleteKeyCount 1 ”,然后就卡这了。
心得:代码方面的事,实在没有思路就debug一下,看看具体的数据,没有思路时就先试一试,说不定就找到解决办法了。
所谓:想都是问题,做才可能有答案。
别等万事俱备,才选择开始。
最后,提供一个RedisTempate配置示例,欢迎拍砖:
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setDefaultSerializer(fastJsonRedisSerializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
使用这个配置,写到Redis服务器的key就与代码中的相同了。