今天有一个小功能点是关于批量删除redis指定的键。我以为几分钟既可以搞定的事情,没想到花了我一个钟的时间。
比如我redis存的键是:
{imei}l:topic
{imei}l:title
{imei}l:tag
{imei}l:url
它们都有共同的前缀:{imei}l
所以我一顿操作直接: delete {imei}l*
没想到redis直接给我返回个0,意思没有删除任何东西。
what?redis竟然不支持这样的模糊匹配删除。
非要我一个个删?倔强的我偏不。
于是我想到了,把这些带{imei}l
前缀都先查询出来,然后再直接删除,我记得操作redis的客户端redisTemplate
可以传可变参数的方法。
扫描redis的key,我想到了 keys
这个命令,但是被我否决了,keys *
这个命令千万别在生产环境乱用。特别是数据庞大的情况下,因为keys会引发Redis锁,并且增加Redis的CPU占用,很多公司的运维都是禁止了这个命令的。
当需要扫描key,匹配出自己需要的key时,可以使用 scan
命令。
代码如下:
private void deleteLongPortrait(String[] imeis) {
List<String> list = new ArrayList<>();
for (String imei : imeis) {
String str = "{" + imei + "}l";
list.add(str);
}
String join = String.join(",", list);
Set<String> scan = redisUtil.scan(join);
String[] array = scan.toArray(new String[scan.size()]);
redisUtil.del(array);
}
public Set<String> scan(String matchKey) {
Set<String> keys = stringRedisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> keysTmp = new HashSet<>();
Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match("*" + matchKey + "*").count(1000).build());
while (cursor.hasNext()) {
keysTmp.add(new String(cursor.next()));
}
return keysTmp;
});
return keys;
}
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
// 传入一个 Collection<String> 集合
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
主要业务:
- 前端传了imeis数组,然后对该imeis拼接成redis里存的键的格式。
- String.join(",", list),让该集合变成String字符串。
- scan操作,模糊匹配得到该模式下所有的key。
- scan.toArray(new String[scan.size()]),将set集合转换成String数组
- del(String… key),String数组作为可变参数传入del 方法签名。
- 成功删除该模式下所有的键。