redis的key删除的时候,是一个阻塞操作;
为什么会阻塞呢,是在删除key的时候,首先要寻找的key,然后进行删除,然而当key已经过期了,或者被他人删除之后,在删除的时候,就找不到这个key,那么它就一直寻找,新版的redis 有最大重试次数(以前的版本直接死循环),那么就会导致一直阻塞.这对于一线上项目来说,阻塞的这段时间可能是致命的;
如何避免??
1 删除普通key,删除key的时候,可以提前判断是否存在,在的时候再进行删除,这样就可以很大概率减少此类事件发生,例如这样:
if (redisTemplate.hasKey(RedisConstant.CASCADE_CHECK_COUNT + cascadeDownTask.getId())) {
redisTemplate.delete(RedisConstant.CASCADE_CHECK_COUNT + cascadeDownTask.getId());
}
不要直接删除
2 其实还有一种情况,删除的这个key为大 key,什么是大key,不是key有多大,而是key对应的value 很大,例如 redis有五种数据类型,有四个是集合,那么当key对应的集合 非常大的时候,此key 就叫做大key,那么删除的时候,也会导致阻塞问题;
我们可以利用类如 分段处理的方式去处理,例如: 目前redis中对应的数据类型是hash,然后hash中有大量 hk ,我们可以分段去删除,然后每次删除休眠一下,这样可以有效减少redis的压力
@SpringBootTest
@Slf4j
class AreaDemoControllerTest {
// @Autowired
// IAreaDemoService areaDemoService;
//
// @Autowired
// AreaDemoMapper areaDemoMapper;
// @Test
// public void aaa(){
// List<AreaDemo> list = areaDemoService.list();
//
// List<AreaDemo> areaDemos1 = list.subList(0, 10);
// areaDemos1.forEach(demo -> demo.setId(null));
// areaDemoMapper.updateBatch(areaDemos1);
// }
@Autowired
RedisTemplate redisTemplate;
/**
* @Description 模拟批量删除大key
* @Author FL
* @Date 11:08 2022/5/12
* @Param []
**/
@Test
public void tetsts() {
String key = "mapKey";
redisTemplate.opsForHash().put(key, "NAME", "小明");
redisTemplate.opsForHash().put(key, "age", "32");
redisTemplate.opsForHash().put(key, "add", "西湖");
redisTemplate.opsForHash().put(key, "tianqi", "晴朗");
redisTemplate.opsForHash().put(key, "heart", "nice");
redisTemplate.opsForHash().put(key, "shoot", "nike");
redisTemplate.opsForHash().put(key, "sex", "男");
Cursor scan = redisTemplate.opsForHash().scan(key, ScanOptions.NONE);
int i = 0;
while (scan.hasNext()) {
Object next = scan.next();
System.out.println(++i);
}
// redisTemplate.opsForHash().delete(key);
removeBigKey(key, 2, 100);// 每次删除两个 休眠100ms
}
public void removeBigKey(String key, int scanCount, long intervalMills) throws CacheException {
final ScanOptions scanOptions = ScanOptions.scanOptions().count(scanCount).build();
// 避免内存泄漏
// 执行循环删除
List<String> fieldKeyList = new ArrayList<>();
try {
Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, scanOptions);
if (ObjectUtil.isNotNull(cursor)) {
while (cursor.hasNext()) {
String fieldKey = String.valueOf(cursor.next().getKey());
fieldKeyList.add(fieldKey);
if (fieldKeyList.size() >= scanCount) {
// 批量删除
Object[] fields = fieldKeyList.toArray();
redisTemplate.opsForHash().delete(key, fields);
log.info("[Big key] remove key: {}, fields size: {}", key, fields.length);
// 清空列表,重置操作
fieldKeyList.clear();
// 沉睡等待,避免对 redis 压力太大
// DateUtil.sleepInterval(intervalMills, TimeUnit.MILLISECONDS);
Thread.sleep(intervalMills);
}
}
}
// 最后 fieldKeyList 中可能还有剩余,不过一般数量不大,直接删除速度不会很慢
// 执行 key 本身的删除
// redisTemplate.opsForHash().delete(key,fieldKeyList);
redisTemplate.opsForHash().delete(key, fieldKeyList.toArray());
} catch (Exception e) {
// log.error();
}
}
}
代码均在 test中
版权声明:本文为qq_32419139原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。