如何分段存储Redis键值对
说明:本文介绍针对一个value过长的键值对,如何分段存储;
场景
当我们需要存入一个String类型的键值对到Redis中,如下:
(缓存接口)
public interface CacheService {
/**
* 添加一个字符串键值对
* @param key 键
* @param value 值
*/
void setString(String key, String value);
}
(Redis实现)
import com.hezy.service.CacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class RedisServiceImpl implements CacheService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public void setString(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
}
(使用)
import com.hezy.service.impl.RedisServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class RedisServiceImplTest {
@Autowired
private RedisServiceImpl redisService;
@Test
public void test1() {
String key = "content";
String value = "这是一段分非常大非常大的字符串…………………………非常大";
redisService.setString(key, value);
}
}
(查看Redis)
有时候,我们存入的字符串可能过长,过大,有可能来自于一个大对象的序列化。这时候存入的key-value,会造成value过大,会触发一些预警。
可以采用我下面这种分段存储的方法。
优化
思路:将字符串分段,每一段生成一个key,然后将这些分段key再用Redis的List类型存储;获取时就先获取这些分段key,再循环去get对应的字符串,拼接起来就是完整的字符串。
如下:分段存,增加一个参数,设置每段字符串的长度
(缓存接口)
/**
* 分段存储
* @param key 键
* @param value 值
* @param chunkSize 每个分段大小
*/
void setStrSub(String key, String value, int chunkSize);
(Redis实现)
@Override
public void setStrSub(String key, String value, int chunkSize) {
// 将value,按照length,分成多个部分
int totalChunks = (int) Math.ceil((double) value.length() / chunkSize);
// 定义一个分段数据key集合
List<String> subKeys = new ArrayList<>(totalChunks);
// 将字符串分成多段
for (int i = 0; i < totalChunks; i++) {
// 计算分段起止位置
int startIndex = i * chunkSize;
int endIndex = Math.min(startIndex + chunkSize, value.length());
// 获取对应分段数据
String chunkValue = value.substring(startIndex, endIndex);
// 拼接分段key
String subKey = key + "_" + i;
// 存储分段数据
setString(subKey, chunkValue);
// 将分段key添加到集合
subKeys.add(subKey);
}
// 分段key添加到集合
setList(key, subKeys);
}
(添加一个集合到Redis)
@Override
public void setList(String key, List value) {
redisTemplate.opsForList().rightPushAll(key, value);
}
启动,测试
@Test
public void test2() {
String key = "content";
String value = "这是一段分非常大非常大的字符串…………………………非常大";
redisService.setStrSub(key, value, 5);
}
查看Redis
然后,要取数据,也很简单;
(缓存接口)
/**
* 获取字符串(分段)
* @param key
* @return
*/
String getStrSub(String key);
(Redis实现)
@Override
public String getStrSub(String key) {
// 先把分段key获取出来
List<String> list = getList(key);
// 字符串拼接,用StringBuilder,线程安全
StringBuilder stringBuilder = new StringBuilder();
for (String subKey : list) {
String subValue = getString(subKey);
// 这里要跳过null,不然最后输出会把null转为字符串
if (subValue == null) {
continue;
}
stringBuilder.append(subValue);
}
// 如果没有数据,返回null
return "".contentEquals(stringBuilder) ? null : stringBuilder.toString();
}
(Redis获取一个List)
@Override
public List getList(String key) {
return redisTemplate.opsForList().range(key, 0, -1);
}
(使用)
@Test
public void test3() {
String content = redisService.getStrSub("content");
System.out.println(content);
}
(打印)
总结
本文介绍了Redis分段存储一个大键值对(String)的一种方式,看下来,实现并不复杂。使用上也可以很方便,可以考虑把分段的存取和普通的存取都兼容起来,这样对于使用者,只需要加一个参数(分段大小)。