支持的数据结构
确实是字符串,list/set,map,都支持。
然后也提供了对应数据结构的写和读的命令。
但是key/value的存储,不管是什么数据结构,都是字节数组。也就是说,在redis里面存储的都是字节数组。那序列化和反序列化是在哪里完成?客户端。
下面分别以几个数据结构举例子,并且加上代码,就比较容易理解。
字符串
key始终是字符串类型,因为key只有字符串类型,没有其他类型。
重点看value是什么类型,到底是字符串?还是对象?
而且,更重要的是,这里说的value到底是什么类型,是站在java客户端的角度去说的,在redis服务器端根本不存在什么对象类型,只有字符串类型。
value是字符串
value的写和读,都是字符串,没有什么特殊的。
value是对象
需要先在客户端序列化(本质其实是把对象编码为字节数组)为字符串(从编码之后的字节数组得到字符串)。
有点拗口。
字节数组和字符串,本质是一回事。可以互相转换。
但是序列化和反序列化是需要编码和解码的,编码的目的就是为了在存储的时候把对象序列化为字节数组——本质是为了方便通信和存储,解码的目的是为了能够把字节数组还原为java对象。
看代码就知道了:redis工具类
public void set(String key, Object obj) {
executeSync(commands -> {
commands.set(key, obj instanceof String ? (String) obj : JsonUtil.writeJson(obj));
return null;
});
}
如果是字符串类型,那么不需要序列化。如果是对象,就需要先序列化为字符串——然后再写入redis。
再来看下序列化的工具类
自定义工具类:com.itranswarp.util.JsonUtil#writeJson(java.lang.Object)
public static String writeJson(Object obj) {
try {
return OBJECT_MAPPER.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
底层其实是jackson框架:com.fasterxml.jackson.databind.ObjectMapper#writeValueAsString
public String writeValueAsString(Object value) throws JsonProcessingException {
SegmentedStringWriter sw = new SegmentedStringWriter(this._jsonFactory._getBufferRecycler());
try {
this._writeValueAndClose(this.createGenerator((Writer)sw), value);
} catch (JsonProcessingException var4) {
throw var4;
} catch (IOException var5) {
throw JsonMappingException.fromUnexpectedIOE(var5);
}
return sw.getAndClear();
}
list
其实和字符串数据结构一样,只不过现在的value是集合。但是集合里的元素仍然是只有字符串类型。也就是说,如果客户端侧的元素是字符串类型,那么不需要序列化;如果元素是对象,就需要先序列化。
代码:redis客户端-lettuce
io.lettuce.core.api.sync.RedisListCommands#lpush
/**
* Prepend one or multiple values to a list.
*
* @param key the key.
* @param values the value.
* @return Long integer-reply the length of the list after the push operations.
*/
Long lpush(K key, V... values);
map
也差不多,主要来看一下元素的数据类型。也是站在客户端侧来看元素的数据类型到底是字符串还是对象。
代码:redis工具类
public void hset(String key, Object field, Object obj) {
executeSync(commands -> {
commands.hset(key, field.toString(), obj instanceof String ? (String) obj : JsonUtil.writeJson(obj));
return null;
});
}
因为是map数据结构,所以value是map。而map的每个元素都是key/value,和list一样,元素的value可以是字符串或者对象,和list不一样的是,每个元素还多了一个key,元素的key也可以是字符串或者对象——虽然key可以是对象,但是一般情况下的应用场景都是字符串。
所以,每个元素的key/value都可以是字符串类型或者对象类型,如果是字符串类型,就不需要序列化,如果是对象类型,就需要先序列化。
总结
除了数据结构这个维度,另外要注意的一个维度就是,客户端侧需要区分元素的数据类型是字符串还是对象,如果是字符串,就不需要序列化,如果是对象,就需要先序列化为字符串。
为什么字符串不需要序列化/编码?因为字符串本身就是字节数组,字符串和字节数组本来就可以直接互相转换。但是对象是需要先序列化/编码的,因为对象是java代码里的概念,redis并没有这个东西,redis只能存储字节数组,要想存储到redis,就需要在客户端侧先序列化,然后从redis读出来的数据也是字节数组,所以客户端侧读出来之后就需要反序列化为java对象。