在我的Redis DB中,我有一些前缀:<numeric_id>哈希值。
有时我想把它们都原子地清除掉。如何在不使用分布式锁定机制的情况下做到这一点呢?
在我的Redis DB中,我有一些前缀:<numeric_id>哈希值。
有时我想把它们都原子地清除掉。如何在不使用分布式锁定机制的情况下做到这一点呢?
当前回答
如果你的键名中有空格,这将适用于MacOS
redis-cli --scan --pattern "myprefix:*" | tr \\n \\0 | xargs -0 redis-cli unlink
其他回答
@mcdizle的解决方案是行不通的,它只适用于一个条目。
这一项适用于具有相同前缀的所有键
EVAL "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" 0 prefix*
注意:你应该用你的密钥前缀替换'prefix'…
我尝试了上面提到的大部分方法,但对我来说都不奏效,经过一些搜索,我发现了以下几点:
如果你在redis上有多个数据库,你应该使用-n [number]来确定数据库。 如果你有几个键使用del,但如果有成千上万或数百万个键,最好使用unlink,因为unlink是非阻塞的,而del是阻塞的,更多信息请访问这个页面unlink vs del 键也像del和is blocking
所以我用这段代码按模式删除键:
redis-cli -n 2 --scan --pattern '[your pattern]' | xargs redis-cli -n 2 unlink
我支持所有与有一些工具或执行Lua表达式相关的答案。
我还有一个选择:
在我们的生产和预生产数据库中,有数千个键。时不时地,我们需要删除一些键(通过一些掩码),修改一些标准等。当然,没有办法从CLI手动完成,特别是使用分片(每个物理分区中有512个逻辑dbs)。
为此,我编写了一个java客户端工具来完成所有这些工作。在删除键的情况下,实用程序可以非常简单,只有一个类:
public class DataCleaner {
public static void main(String args[]) {
String keyPattern = args[0];
String host = args[1];
int port = Integer.valueOf(args[2]);
int dbIndex = Integer.valueOf(args[3]);
Jedis jedis = new Jedis(host, port);
int deletedKeysNumber = 0;
if(dbIndex >= 0){
deletedKeysNumber += deleteDataFromDB(jedis, keyPattern, dbIndex);
} else {
int dbSize = Integer.valueOf(jedis.configGet("databases").get(1));
for(int i = 0; i < dbSize; i++){
deletedKeysNumber += deleteDataFromDB(jedis, keyPattern, i);
}
}
if(deletedKeysNumber == 0) {
System.out.println("There is no keys with key pattern: " + keyPattern + " was found in database with host: " + host);
}
}
private static int deleteDataFromDB(Jedis jedis, String keyPattern, int dbIndex) {
jedis.select(dbIndex);
Set<String> keys = jedis.keys(keyPattern);
for(String key : keys){
jedis.del(key);
System.out.println("The key: " + key + " has been deleted from database index: " + dbIndex);
}
return keys.size();
}
}
这并不是对这个问题的直接回答,但因为我是在寻找自己的答案时来到这里的,所以我将在这里分享。
如果你有数千万或数亿个键需要匹配,这里给出的答案将导致Redis在很长一段时间内(几分钟?)没有响应,并可能因为内存消耗而崩溃(当然,后台保存将在你的操作过程中启动)。
下面的方法不可否认是丑陋的,但我没有找到更好的方法。原子性在这里是没有问题的,在这种情况下,主要目标是保持Redis的正常运行和100%的响应。如果您将所有的密钥都放在一个数据库中,并且不需要匹配任何模式,那么它将完美地工作,但不能使用http://redis.io/commands/FLUSHDB,因为它具有阻塞特性。
想法很简单:写一个脚本,在循环中运行,使用O(1)操作,如http://redis.io/commands/SCAN或http://redis.io/commands/RANDOMKEY来获取密钥,检查它们是否匹配模式(如果你需要的话),然后逐个http://redis.io/commands/DEL它们。
如果有更好的方法,请告诉我,我会更新答案。
Ruby中使用randomkey的示例实现,作为rake任务,以非阻塞方式代替redis-cli -n 3 flushdb:
desc 'Cleanup redis'
task cleanup_redis: :environment do
redis = Redis.new(...) # connection to target database number which needs to be wiped out
counter = 0
while key = redis.randomkey
puts "Deleting #{counter}: #{key}"
redis.del(key)
counter += 1
end
end
下面的命令对我很有用。
redis-cli -h redis_host_url KEYS "*abcd*" | xargs redis-cli -h redis_host_url DEL