背景
记录在redis中的多语言缓存,突然发现取值无法正常解析,加日志后发现,从redis取出来的值,有些在正常值的前面多了很多 \u0000
,有些值好像是覆盖原有值但没覆盖全的样子 {"key":"new Value"}lue"}
,导致在解析数据是报错。
定位问题
通过记录日志,发现在向redis中添加值时,值还是正常的,再取出来,就会发生这些变化,于是怀疑问题出在set方法上。
排查代码,发现代码中使用了两种set方法。
set(k, v, expireTime);
set(k, v, expireTime,TimeUnit.SECONDS);
这两个方法乍一看,好像是少了个设置超时时间的单位,但实际上这两个方法的功能完全不一样。
void set(K key, V value, long offset)
这个三参的方法de Jdoc如下,用指定的值,追加覆盖原有内容,追加覆盖的位置由offset确定。
Overwrite parts of key starting at the specified offset with given value.
Params:
key – must not be null.
value –
offset –
需要注意的是,如果原有内容长度不够,则会使用\u0000
填充到足够的长度。
比如:现有键值对 {"test":"ABCD"}
, 使用set("test","12",10)
,会有以下操作
ABCD不足10,所以使用 \u0000
填充到长度10,即 ABCD\u0000\u0000\u0000\u0000\u0000\u0000
,
然后追加覆盖12
, 最后的值为ABCD\u0000\u0000\u0000\u0000\u0000\u000012
。
这个在调试时才会看到,控制台打印出来的结果是ABCD12
。
void set(K key, V value, long timeout, TimeUnit unit)
这个方法才是正确的保存键值对,并设置过期时间。
Set the value and expiration timeout for key.
Params:
key – must not be null.
value – must not be null.
timeout – the key expiration timeout.
unit – must not be null.
总结
使用重载方法时,需要确认方法每个参数的含义,确认方法的功能,虽然有些参数类型是一样的,但实现的功能也会千差万别。 切记切记。