在我的Redis DB中,我有一些前缀:<numeric_id>哈希值。

有时我想把它们都原子地清除掉。如何在不使用分布式锁定机制的情况下做到这一点呢?


当前回答

这个对我有用,但可能不是原子:

redis-cli keys "stats.*" | cut -d ' ' -f2 | xargs -d '\n' redis-cli DEL

其他回答

我也遇到了同样的问题。我为用户存储会话数据的格式为:

session:sessionid:key-x - value of x
session:sessionid:key-y - value of y
session:sessionid:key-z - value of z

因此,每个条目都是一个单独的键值对。当会话被销毁时,我想通过使用模式session:sessionid:* -删除键来删除所有会话数据,但redis没有这样的功能。

我所做的是:将会话数据存储在散列中。我只是用session:sessionid的哈希id创建了一个哈希,然后我在那个哈希中推key-x, key-y, key-z(顺序对我来说并不重要),如果我不再需要那个哈希,我只是做一个DEL session:sessionid,所有与那个哈希id相关的数据都消失了。DEL是原子的,访问数据/向哈希写入数据是O(1)。

在bash执行:

redis-cli KEYS "prefix:*" | xargs redis-cli DEL

更新

好的,我明白了。这种方式怎么样:存储当前额外的增量前缀,并将其添加到所有的键。例如:

你的价值观是这样的:

prefix_prefix_actuall = 2
prefix:2:1 = 4
prefix:2:2 = 10

当您需要清除数据时,首先更改prefix_actuall(例如set prefix_prefix_actuall = 3),因此您的应用程序将把新数据写入关键字prefix:3:1和prefix:3:2。然后,您可以安全地从prefix:2:1和prefix:2:2中获取旧值并清除旧键。

这并不是对这个问题的直接回答,但因为我是在寻找自己的答案时来到这里的,所以我将在这里分享。

如果你有数千万或数亿个键需要匹配,这里给出的答案将导致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

@itamar的回答很棒,但对回复的解析对我来说并不管用,特别是在给定扫描中没有找到键的情况下。一个可能更简单的解决方案,直接从控制台:

redis-cli -h HOST -p PORT  --scan --pattern "prefix:*" | xargs -n 100 redis-cli DEL

这也使用了SCAN,它在生产中比KEYS更可取,但不是原子的。

我支持所有与有一些工具或执行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();
    }

}