Redis使用StringRedisTemplate保存数据,值为 \x00 \u0000 乱码问题

  • 速览
  • 1.问题描述
  • 2.解决方案
  • 详情
  • 1.背景
  • 2.问题明确
  • 3.问题代码
  • 4.看源码
  • 5.发现问题
  • 6.解决问题


速览

1.问题描述

Redis中的value,看起来有一堆乱码的 \x00\x00\x00\x00\x00(官网介绍的是 \u0000)

2.解决方案

错误代码

public void set(String key, String value, long timeout) {
	stringRedisTemplate.opsForValue().set(key, value, timeout);
}

修正代码

public void set(String key, String value, long timeout) {
    // 换了另一个四个入参的SDK方法
	stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.MILLISECONDS);
}

详情

1.背景

  • 检查一个cache问题,利用redis-cli命令和redis客户端查看数据,发现保存的值都是满屏的\x00\x00,很是费解。
  • 百度了一下,知道 \x00 是空符的意思,但是为什么会有这么多空符出现?
  • 在代码中debug发现可以正常get到值,询问同事,原来他也发现了这个问题,在获取的value后加上了一个trim()就可以正常使用了,项目紧于是就先搁置忽略了
  • 但是在排查问题的时候,发现这样着实不太方便;
  • 另外在排查过程中发现原本设定的30分钟缓存有效期,实际上ttl是-1,也就是永不失效,这样这个问题就不能忽略了

2.问题明确

  • 值为乱码,一堆 \x00\x00 字符
  • 设定的过期时间不生效

3.问题代码

private static final long timeout = 30 * 60 * 1000;
private StringRedisTemplate stringRedisTemplate;

public void set(String key, String value) {
	stringRedisTemplate.opsForValue().set(key, value, timeout);
}

4.看源码

点进 .set(key, value, timeout) 发现源码如下

class DefaultValueOperations<K, V> extends AbstractOperations<K, V> implements ValueOperations<K, V> {
	......
	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.ValueOperations#set(java.lang.Object, java.lang.Object, long)
	 */
	@Override
	public void set(K key, V value, long offset) {

		byte[] rawKey = rawKey(key);
		byte[] rawValue = rawValue(value);

		execute(connection -> {
			connection.setRange(rawKey, rawValue, offset);
			return null;
		}, true);
	}
}

5.发现问题

  • 咦,蹊跷来了,他的第三个参数名称是offset(偏移量),并不是想象中的timeout(过期时间),再看下面调用的执行方法是setRange(…),并不是单纯的set(…)
  • 于是查阅Reids官网关于setrange命令的介绍:https://redis.io/commands/setrange,简单一句话是:按照指定的偏移量覆盖key所存储value的一部分字符串,有兴趣的大家可以自己看看原文,官网给出的示例如下:

redis set 乱码 redis value乱码_java

6.解决问题

  • 原来是我们用错了SDK的方法,就是这个方法导致value前增加了很多空符,而且原本想的过期时间是30*60*1000,等于说在真实的值前面加了180W个空符,怪不得满屏的乱码,而且并没有设置过期时间。
  • 问题找到了就好解决了,下面只要找到那个我们想要的正确设置缓存有效期时间的方法就好了,看一眼源码中关于set的方法有几个
  • 一眼就相中第三个,因为他带有我们想要的time字眼啊,点开瞧瞧
  • 哎呀,卧槽~ 代码好多,写的好复杂,不想看了。 没关系重点就在setExpSetEx,意思都是set一对key value,再给exprie个过期时间,两者差别只是过期时间单位是毫秒还是秒,于是整个问题就解决了。

Redis官网命令文档