如何使用Redis以原子方式删除与模式匹配的键

时间:2010-10-23 22:04:27

标签: redis

在我的Redis数据库中,我有许多prefix:<numeric_id>个哈希值。

有时我想以原子方式清除它们。如何在不使用分布式锁定机制的情况下执行此操作?

27 个答案:

答案 0 :(得分:662)

在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),这样您的应用程序就会将新数据写入密钥前缀:3:1和前缀:3:2。然后,您可以安全地从前缀:2:1和前缀:2:2中取旧值并清除旧密钥。

答案 1 :(得分:395)

从redis 2.6.0开始,您可以运行原子执行的lua脚本。我从来没有写过,但我认为它看起来像这样

EVAL "return redis.call('del', unpack(redis.call('keys', ARGV[1])))" 0 prefix:[YOUR_PREFIX e.g delete_me_*]

请参阅EVAL documentation

答案 2 :(得分:68)

这是Lua中实现的通配符删除的完全工作和原子版本。它的运行速度比xargs版本快得多,因为它来回的网络要少得多,并且完全是原子的,阻止任何其他的redis请求,直到它完成。如果您想以原子方式删除Redis 2.6.0或更高版本上的密钥,这绝对是可行的方法:

redis-cli -n [some_db] -h [some_host_name] EVAL "return redis.call('DEL', unpack(redis.call('KEYS', ARGV[1] .. '*')))" 0 prefix:

这是@ mcdizzle在回答这个问题时的想法的工作版本。这个想法100%归功于他。

编辑:根据下面的Kikito评论,如果你的Redis服务器中有更多的密钥要删除而不是空闲内存,那么你将遇到"too many elements to unpack" error。在这种情况下,请执行:

for _,k in ipairs(redis.call('keys', ARGV[1])) do 
    redis.call('del', k) 
end

正如Kikito建议的那样。

答案 3 :(得分:60)

免责声明:以下解决方案不会提供原子性。

从v2.8开始,你真的想要使用SCAN命令而不是KEYS [1]。以下Bash脚本演示了按模式删除键:

#!/bin/bash

if [ $# -ne 3 ] 
then
  echo "Delete keys from Redis matching a pattern using SCAN & DEL"
  echo "Usage: $0 <host> <port> <pattern>"
  exit 1
fi

cursor=-1
keys=""

while [ $cursor -ne 0 ]; do
  if [ $cursor -eq -1 ]
  then
    cursor=0
  fi

  reply=`redis-cli -h $1 -p $2 SCAN $cursor MATCH $3`
  cursor=`expr "$reply" : '\([0-9]*[0-9 ]\)'`
  keys=${reply##[0-9]*[0-9 ]}
  redis-cli -h $1 -p $2 DEL $keys
done

[1] KEYS是一个危险的命令,可能会导致DoS。以下是其文档页面中的引用:

  

警告:将KEYS视为一项只能在生产环境中使用的命令。在针对大型数据库执行时可能会破坏性能。此命令用于调试和特殊操作,例如更改键空间布局。不要在常规应用程序代码中使用KEYS。如果您正在寻找在密钥空间子集中查找密钥的方法,请考虑使用集合。

UPDATE:一个衬垫,具有相同的基本效果 -

$ redis-cli --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli DEL

答案 4 :(得分:34)

对于那些在解析其他答案时遇到问题的人:

eval "for _,k in ipairs(redis.call('keys','key:*:pattern')) do redis.call('del',k) end" 0

key:*:pattern替换为您自己的模式并将其输入redis-cli,您就可以了。

来自:http://redis.io/commands/del

的信用lisco

答案 5 :(得分:17)

您也可以使用此命令删除密钥: -

假设您的redis中有许多类型的键,如 -

  1. 'xyz_category_fpc_12'
  2. 'xyz_category_fpc_245'
  3. 'xyz_category_fpc_321'
  4. 'xyz_product_fpc_876'
  5. 'xyz_product_fpc_302'
  6. 'xyz_product_fpc_01232'
  7. 前 - ' xyz_category_fpc '此处 xyz 网站名称,这些密钥与电子商务网站的产品和类别相关并由FPC生成。

    如果您使用以下命令 -

    _config

    OR

    /_node/couchdb@localhost/_config

    删除所有键,例如“ xyz_category_fpc ”(删除1,2和3键)。要删除其他4,5和6个数字键,请在上面的命令中使用“ xyz_product_fpc ”。

    如果您想在 Redis 删除所有内容,请按照以下命令进行操作 -

    使用redis-cli:

    1. FLUSHDB - 从连接的CURRENT数据库中删除数据。
    2. FLUSHALL - 从所有数据库中删除数据。
    3. 例如: - 在你的shell中:

      couchdb@localhost

答案 6 :(得分:16)

@ mcdizle的解决方案不起作用它只适用于一个条目。

这个适用于具有相同前缀的所有键

EVAL "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" 0 prefix*

注意:您应该将'prefix'替换为您的密钥前缀...

答案 7 :(得分:12)

如果密钥名称中有空格,则可以在bash中使用:

redis-cli keys "pattern: *" | xargs -L1 -I '$' echo '"$"' | xargs redis-cli del

答案 8 :(得分:10)

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

public class AgentSkills 
{
  public List<string> agentSkillsNameList=new List<string>();
  public List<string> agentSkillsLvlList=new List<string>();
}

这也使用SCAN,它在生产中优于KEYS,但不是原子的。

答案 9 :(得分:8)

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

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

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

我做了什么:将会话数据存储在hash内。我只是创建一个散列ID为session:sessionid的哈希,然后我在该哈希中推送key-xkey-ykey-z(顺序对我来说无关紧要)如果我不再需要那个哈希我只做DEL session:sessionid并且与该哈希id相关的所有数据都消失了。 DEL是原子的,访问数据/写入哈希的数据是O(1)。

答案 10 :(得分:5)

我认为可能对你有帮助的是MULTI/EXEC/DISCARD。虽然不是100% equivalent of transactions,但您应该能够将删除与其他更新隔离开来。

答案 11 :(得分:4)

请使用此命令并尝试:

redis-cli --raw keys "$PATTERN" | xargs redis-cli del

答案 12 :(得分:3)

通过&#34;删除分支&#34;简单实现FastoRedis中的功能,只需选择要删除的分支。

enter image description here

答案 13 :(得分:2)

这不是问题的直接答案,但是因为我在搜索自己的答案时来到这里,我会在这里分享。

如果你必须匹配数十或数亿个密钥,这里给出的答案将导致Redis在大量时间内没有响应(几分钟?),并且可能因内存消耗而崩溃(当然,后台保存将在您的操作过程中启动。)

以下方法无疑是丑陋的,但我找不到更好的方法。原子性在这里是不可能的,在这种情况下,主要目标是使Redis保持正常运行并在100%的时间内做出响应。如果您将所有密钥都放在一个数据库中并且不需要匹配任何模式,那么它将完美地工作,但由于它具有阻塞性,因此无法使用http://redis.io/commands/FLUSHDB

想法很简单:编写一个在循环中运行的脚本并使用O {1)操作(如http://redis.io/commands/SCANhttp://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

答案 14 :(得分:2)

使用SCAN而不是KEYS(对于生产服务器推荐)和--pipe而不是xargs的版本。

我更喜欢管道覆盖xargs,因为它更高效,当你的密钥包含引号或其他特殊字符时,你的shell会尝试和解释。此示例中的正则表达式替换将键包装在双引号中,并转义内部的任何双引号。

export REDIS_HOST=your.hostname.com
redis-cli -h "$REDIS_HOST" --scan --pattern "YourPattern*" > /tmp/keys
time cat /tmp/keys | perl -pe 's/"/\\"/g;s/^/DEL "/;s/$/"/;'  | redis-cli -h "$REDIS_HOST" --pipe

答案 15 :(得分:1)

这个对我有用:

calcManifest()

答案 16 :(得分:1)

如果您的密钥名称中有空格,这将适用于 MacOS

/tmp/prefix2

答案 17 :(得分:1)

// TODO

您有时认为DEL之类的Redis命令无法正常工作,因此该命令没有任何意义,

redis-cli KEYS "*" | xargs -i redis-cli EXPIRE {} 1这是生活骇客

答案 18 :(得分:0)

我用最简单的 EVAL 命令变体成功了:

EVAL "return redis.call('del', unpack(redis.call('keys', my_pattern_here*)))" 0

我用我的值替换了 my_pattern_here

答案 19 :(得分:0)

如果您使用Windows环境,请按照以下步骤操作,它绝对可以使用:

  1. 从此处下载GOW-https://github.com/bmatzelle/gow/wiki(因为xargs命令在Windows中不起作用)

  2. 下载适用于Windows的redis-cli(详细说明在这里-https://medium.com/@binary10111010/redis-cli-installation-on-windows-684fb6b6ac6b

  3. 运行cmd并打开存储redis-cli的目录(例如:D:\ Redis \ Redis-x64-3.2.100)

  4. 如果要删除所有以“ Global:ProviderInfo”开头的密钥,请执行此查询(需要更改粗体参数(主机,端口,密码,密钥)并编写您的参数,因为这仅是示例):

    redis-cli -h redis.test.com -p 6379 -a redispassword -原始键“ 全局: ProviderInfo *” | xargs redis-cli -h redis.test.com -p 6379 -a redispassword del

答案 20 :(得分:0)

现在,您可以使用redis客户端并先执行SCAN(支持模式匹配),然后分别删除每个密钥。

但是,官方redis github上存在一个创建模式匹配项here的问题,如果发现有用的话就去爱上它吧!

答案 21 :(得分:0)

我尝试了上面提到的大多数方法,但是它们对我不起作用,经过一番搜索,我发现了这些要点:

  • 如果redis上有多个数据库,则应使用-n [number]
  • 确定数据库
  • 如果您有几个键,请使用del,但是如果有成千上万个键,则最好使用unlink,因为 unlink是非阻止的,而del是阻止的,有关更多信息,请访问此页面unlink vs del
  • keys也很像del,并且正在阻止

所以我用这段代码按模式删除密钥:

 redis-cli -n 2 --scan --pattern '[your pattern]' | xargs redis-cli -n 2 unlink 

答案 22 :(得分:0)

我支持所有与使用某个工具或执行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();
    }

}

答案 23 :(得分:-1)

如果您的密钥包含特殊字符-例如Guide$CLASSMETADATA][1],其他答案可能不起作用。将每个键括在引号中将确保将其正确删除:

redis-cli --scan --pattern sf_* | awk '{print $1}' | sed "s/^/'/;s/$/'/" | xargs redis-cli del

答案 24 :(得分:-1)

下面的命令对我有用。

redis-cli -h redis_host_url KEYS "*abcd*" | xargs redis-cli -h redis_host_url DEL

答案 25 :(得分:-1)

穷人的原子质量删除?

也许你可以将它们全部设置为EXPIREAT相同的秒 - 比如将来的几分钟 - 然后等到那个时候再看到它们全部“自毁”。

但我不确定那将是多么原子。

答案 26 :(得分:-2)

Spring RedisTemplate本身提供了这些功能。最新版本中的RedissonClient已弃用&#34; deleteByPattern&#34;功能。

Set<String> keys = redisTemplate.keys("geotag|*");
redisTemplate.delete(keys);