1,因业务需要,需要通过通配符删除redis指定的key,逻辑代码很简单,当时以为这个会执行很快,不应该会有问题,因为redis链接的是腾讯云的redis,不支持通配符直接删除,所以自己写了一个客户段,删除每个节点的数据。
@PostMapping("/clearRedis2")
public JsonResVo clearRedis2(){
try {
log.info("redis:" + applicationVo.host +",applicationVo.passwd" + applicationVo.passwd);
JedisUtils jedisUtils = new JedisUtils(applicationVo.host,applicationVo.port,applicationVo.passwd);
jedisUtils.delKeys("wpp_ds_*");
jedisUtils.delKeys("wpp_d_*");
jedisUtils.delKeys("wpp_h_*");
return JsonResVo.buildSuccess("缓存清理完成");
} catch (Exception e) {
return JsonResVo.buildSuccess("缓存清理异常:"+e.getMessage());
}
}
import java.net.SocketException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
public class JedisUtils {
private JedisCluster jedisCluster;
/*private String host = "xx.xx.xx.xx";
private int port = 6379;
private String passwd = "xxx";*/
private String host;
private int port;
private String passwd;
public JedisUtils(String hostip,int port,String passwd) {
this.host=hostip;
this.port=port;
this.passwd=passwd;
String redisString = this.host + ":" + this.port;
String[] hostArray = redisString.split(",");
Set nodes = new HashSet();
// 配置redis集群
for (String host : hostArray) {
String[] detail = host.split(":");
nodes.add(new HostAndPort(detail[0], Integer.parseInt(detail[1])));
}
this.jedisCluster =
new JedisCluster(nodes, 10000, 10000, 5, this.passwd, new GenericObjectPoolConfig());
}
//获取redis中指定key的值,value类型为String的使用此方法
public Set keys(String redisKeyStartWith) throws SocketException {
CommandSenderJedis commandSenderJedis =
new CommandSenderJedis(this.host, this.port, this.passwd);
return commandSenderJedis.keys(redisKeyStartWith);
}
// 获取redis中指定key的值,value类型为String的使用此方法
public void delKeys(String redisKeyStartWith) throws SocketException {
CommandSenderJedis commandSenderJedis =
new CommandSenderJedis(this.host, this.port, this.passwd);
commandSenderJedis.delKeys(redisKeyStartWith);
}
// 获取redis中指定key的值,value类型为String的使用此方法
public String get(String key) {
return this.jedisCluster.get(key);
}
//设置redis中指定key的值,value类型为String的使用此方法
public void set(String key, String value) {
this.jedisCluster.set(key, value);
}
//获取redis中指定key的值,对应的value,value类型为MAP的使用此方法
public Map getMap(String key) {
return this.jedisCluster.hgetAll(key);
}
//删除redis中指定key的值项
public void del(String key) {
this.jedisCluster.del(key);
}
public static void main(String args[]) {
try {
rmr("liqiang_ds_*");
rmr("liqiang_d_*");
rmr("liqiang_h_*");
// rmr("TRIP:GT_TRAIN_KEY_TRAJACTORY:*");
// rmr("TRIP:KEY_TRAJACTORY:*");
// rmr("res:1:*");
// rmr("res:0:*");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void rmr(String redisKeyStartWith) throws Exception {
/*JedisUtils jedisUtils = new JedisUtils();
jedisUtils.delKeys(redisKeyStartWith);*/
// Set keys = jedisUtils.keys(redisKeyStartWith);
// System.out.println("=====================");
// keys.forEach(System.out::println);
// keys.forEach(
// k -> {
// jedisUtils.del(k);
// });
}
}
2,后来通过打点日志,发现耗时确实在redis通配符删除那块代码,日志如下:
3,发现删除执行需要18s,而网关那边做了一次限制,如果请求超过15秒,则网关自动断开链接,报504错误。
4,发现问题后,通过开个线程,异步执行删除操作,问题解决,修复后的代码如下:
@PostMapping("/clearRedis")
public JsonResVo clearRedis(){
try {
log.info("redis:" + applicationVo.host +",applicationVo.passwd" + applicationVo.passwd);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
log.info("start...." +Thread.currentThread().getName());
try {
JedisUtils jedisUtils = new JedisUtils(applicationVo.host,applicationVo.port,applicationVo.passwd);
jedisUtils.delKeys("liqiang_ds_*");
jedisUtils.delKeys("liqiang_d_*");
jedisUtils.delKeys("liqiang_h_*");
} catch (SocketException e) {
e.printStackTrace();
}
log.info("end....");
}
});
t1.setName("clearRedis");
t1.start();
log.info("3333");
return JsonResVo.buildSuccess("缓存清理完成");
} catch (Exception e) {
log.info("error"+ e.getMessage());
return JsonResVo.buildSuccess("缓存清理异常:"+e.getMessage());
}
}
总结:
1,一直认为redis不会有问题,前期排除是不是其他问题,在这个纠结了很久,后来通过日志,发现确实是redis操作超时,所以还是要以日志为准,日志是最准确的,不能凭经验去判断
2,通过这个问题,也发现,刚开始redis数据量比较少是,通过通配符删除数据,del key* 会比较快,随着数据量大的时候,通配符删除命令会指数级耗时增加,所以还是要精确指定key删除