public void multiSet() {
long start = System.currentTimeMillis();
//定义key对应的value
Consumer consumer = new Consumer();
consumer.setId(11111L);
consumer.setName("multiSet-consumer-");
consumer.setPhone("13011112222");
consumer.setBirthday(new Date());
byte[] trueValue = RedisSerializer.json().serialize(consumer);
//start
for (long k = 1; k <= 100; k++) {
long kk = k * 10000;
System.out.println(String.format("第【%s】次批量添加", k));
redisTemplate.executePipelined((RedisCallback
运行结果:
集群状态:
public void deleteOne() {
Boolean result = redisTemplate.delete("multiSet-Key-*");
System.out.println("***********" + result);
}
运行结果:
此方法无效,说明redis不支持del xxx*
public void deleteTwo() {
Long result = redisTemplate.delete(redisTemplate.keys("multiSet-Key-*"));
System.out.println("***********" + result);
}
运行结果:命令执行超时异常,redis中所有key依然健在
集群状态:
注意:
1、如果key很少,是不会报异常的,而且key会被删除
2、但是无论删除的KEY少还是多,方法都会一直阻塞住无法结束,就是keys()方法导致的
3、同时redis集群性能严重下降,get set耗时较长
此做法不OK
public void deleteThree() {
long start = System.currentTimeMillis();
String luaScript =
"while(true) " +
"do local x = redis.call('scan','0','match',KEYS[1]) " +
"for i,k in ipairs(x[2]) " +
"do redis.call('del',k) " +
"end " +
"if(x[1] == '0') then break end " +
"end";
DefaultRedisScript defaultRedisScript = new DefaultRedisScript(luaScript);
ScriptExecutor scriptExecutor = new DefaultScriptExecutor(redisTemplate);
scriptExecutor.execute(defaultRedisScript, Arrays.asList("multiSet-Key-*"));
long end = System.currentTimeMillis();
System.out.println(String.format("总计耗时:【%s】秒", (end - start) / 1000));
}
运行结果:命令执行超时异常,redis集群状态异常
Exception in thread "main" org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: Command timed out after 1 minute(s)
有个redis master已经不可用了
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
192.168.8.132已挂
集群状态:出问题了
注意:如果key较少
1、是不会报异常的,而且key会被删除
2、但是只能删除一个master上的数据,其他master上的匹配KEY没有被删
3、被删的那个master执行脚本期间,不可读写
此做法不OK
集群恢复解决方法:
1、在出问题的master执行redis命令script kill
2、在出问题的master执行redis命令shutdown nosave
3、集群所有机器执行killall redis-server
4、集群所有机器重新启动redis
public void deleteFour() {
long start = System.currentTimeMillis();
//获取所有KEY怼到set中
RedisCallback> redisCallback = connection -> {
Set keySet = new HashSet<>();
ScanOptions.ScanOptionsBuilder scanOptionsBuilder = ScanOptions.scanOptions();
scanOptionsBuilder.match("multiSet-Key-*");
ScanOptions scanOptions = scanOptionsBuilder.build();
Cursor cursor = connection.scan(scanOptions);
int count = 0;
while (cursor.hasNext()) {
keySet.add(RedisSerializer.string().deserialize(cursor.next()));
count++;
}
System.out.println("获取到的KEY总量为:" + count);
return keySet;
};
Set keySet = (Set) redisTemplate.execute(redisCallback);
//遍历set中的KEY,进行分批次删除,一次10000条
List list = new ArrayList<>();
for(String s : keySet){
if(list.size() == 10000){
redisTemplate.executePipelined((RedisCallback
运行结果:
TIPS1、如果所有redis服务当前不是很忙,同时管道批量操作的数量不是很大,则一切OK;其他线程 set get del等都正常;
TIPS2、删除期间,当有其他线程在get值的时候,有一定几率产生异常;同时其他线程操作也会发生异常
此方法虽然效率高,但是还是存在一定风险.
public void deleteFive() {
long start = System.currentTimeMillis();
RedisCallback> redisCallback = connection -> {
Set keySet = new HashSet<>();
ScanOptions.ScanOptionsBuilder scanOptionsBuilder = ScanOptions.scanOptions();
scanOptionsBuilder.match("multiSet-Key-*");
ScanOptions scanOptions = scanOptionsBuilder.build();
Cursor cursor = connection.scan(scanOptions);
int count = 0;
while (cursor.hasNext()) {
keySet.add(RedisSerializer.string().deserialize(cursor.next()));
count++;
}
System.out.println("获取到的KEY总量为:" + count);
return keySet;
};
Set keySet = (Set) redisTemplate.execute(redisCallback);
Iterator it = keySet.iterator();
while (it.hasNext()) {
redisTemplate.delete(it.next());
}
long end = System.currentTimeMillis();
System.out.println(String.format("总计耗时:【%s】秒", (end - start) / 1000));
System.out.println("清除所有指定前缀KEY执行完毕--------------------");
}
运行结果:
集群状态:
确实清除了所有KEY
redis集群性能未受到任何影响,set get del 等操作都正常
但是KEY怼在一个set里面还是不太合适
public void deleteSix() {
long start = System.currentTimeMillis();
RedisCallback redisCallback = connection -> {
ScanOptions.ScanOptionsBuilder scanOptionsBuilder = ScanOptions.scanOptions();
scanOptionsBuilder.match("multiSet-Key-*");
ScanOptions scanOptions = scanOptionsBuilder.build();
Cursor cursor = connection.scan(scanOptions);
long count = 0;
while (cursor.hasNext()) {
count += connection.del(cursor.next()); //count为所有删除成功次数的总和
System.out.println("******" + count + "******");
}
return count;
};
Long count = (Long) redisTemplate.execute(redisCallback);
long end = System.currentTimeMillis();
System.out.println("被删除KEY的数量为:" + count);
System.out.println(String.format("总计耗时:【%s】秒", (end - start) / 1000));
}
运行结果:
集群状态:
发现KEY全部清除了
优势:
虽然执行时间很长,但是这种缓和处理占用资源很少,对redis集群不会造成压力。
方法重新整理一下:
public boolean deleteSix(String pattern) {
RedisCallback redisCallback = connection -> {
try{
ScanOptions.ScanOptionsBuilder scanOptionsBuilder = ScanOptions.scanOptions();
scanOptionsBuilder.match(pattern);
ScanOptions scanOptions = scanOptionsBuilder.build();
Cursor cursor = connection.scan(scanOptions);
while (cursor.hasNext()) {
connection.del(cursor.next());
}
return true;
}catch (Exception e){
return false;
}
};
return (boolean) redisTemplate.execute(redisCallback);
}
1、如果想迅速进行批量删除,可采用方案4(redisTemplate scan + pipeline),云服务器的性能强悍,
应该不会那么容易卡顿吧...SO 管道流操作可以发挥它的优势。
2、对于缓存,可能有批量清除的需求,此时可以使用方案6,让他删个几十分钟也不过分。
And,缓存我推荐使用自定义缓存,非永久化的数据每个KEY都设置过期时间。这样就用不着批量删除了。