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

Ale*_*ysh 544 redis

在我的Redis DB中,我有许多prefix:<numeric_id>哈希值.

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

Cas*_*sey 679

在bash中执行:

redis-cli KEYS "prefix:*" | xargs redis-cli DEL
Run Code Online (Sandbox Code Playgroud)

UPDATE

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

你有这样的价值观:

prefix_prefix_actuall = 2
prefix:2:1 = 4
prefix:2:2 = 10
Run Code Online (Sandbox Code Playgroud)

当您需要清除数据时,首先更改prefix_actuall(例如set prefix_prefix_actuall = 3),这样您的应用程序就会将新数据写入密钥前缀:3:1和前缀:3:2.然后,您可以安全地从前缀:2:1和前缀:2:2中取旧值并清除旧密钥.

  • 将不会删除将在KEYS命令之后创建的键. (35认同)
  • 第一个答案也帮助了我.另一个变种,如果你的redis键包含引号或其他字符搞乱xargs:`redis-cli KEYS"前缀:*"| xargs --delim ='\n'redis-cli DEL` (18认同)
  • 如果你有多个数据库(键空间),那么这就是诀窍:让我们说你需要删除db3中的键:`redis-cli -n 3 KEYS"前缀:*"| xargs redis-cli -n 3 DEL` (17认同)
  • 对不起,这不是原子删除.有人可能会在KEYS和DEL之间添加新密钥.我不想删除那些. (13认同)
  • 我只需要清除一些坏键,所以Casey的第一个答案就是现场,除了我不得不在引号之外移动键:redis-cli KEYS"prefix:*"| xargs redis-cli DEL (5认同)
  • 我有嵌入空间的键.我能够使用sed来引用键:`redis-cli KEYS"前缀:*"| sed's /\(.*)/"\ 1"/'| xargs redis-cli DEL` (3认同)
  • 对于选择了db的其他主机:```redis-cli -h host -n db KEYS"prefix1:prefix2:*"| xargs redis-cli -h host -n db DEL``` (3认同)
  • 是否有相当于 `redis-cli KEYS "prefix:*" | 的等效项 xargs redis-cli DEL` 用于 Windows cmd? (2认同)

小智 412

从redis 2.6.0开始,您可以运行以原子方式执行的lua脚本.我从未写过一篇,但我认为它看起来像这样

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

请参阅EVAL文档.

  • 哎呀... redis被用作简单的密钥/商店缓存.这似乎是`del前缀:*`应该是一个基本的操作:/ (166认同)
  • 这个用于大量的键:`EVAL'本地键= redis.call('keys',ARGV [1])\n对于i = 1,#keys,5000 do \n redis.call('del' ,解包(keys,i,math.min(i + 4999,#keys)))\n end \n返回键"0前缀:*` (91认同)
  • 重要说明:如果您有超过几千个与前缀匹配的密钥,则会失败. (21认同)
  • 是的,如果没有键匹配模式,它会失败.为了解决这个问题,我添加了一个默认密钥:`EVAL'返回redis.call('del','defaultKey',unpack(redis.call('keys',ARGV [1])))"0前缀:*` (7认同)
  • @Ray坦率地说,如果你需要这个功能,你应该简单地用数字数据库或服务器对数据进行分区,并使用flush/flushdb (3认同)
  • @MarcGravell我听说单个redis部署中的多个数据库被弃用了.要在没有功能的情况下进行分区,您需要运行多个redis服务器.不确定添加基础架构真实或虚拟维护是否比存在有效的`del前缀:*`更好. (3认同)
  • 另一种选择:将应一起删除的键添加到哈希/列表/集中,并立即将它们作为一个对象删除。 (2认同)
  • 为了防止空匹配错误:`EVAL'本地键= redis.call(“ keys”,ARGV [1]); 返回#keys&gt; 0和redis.call(“ del”,unpack(keys))或0'0前缀:*` (2认同)
  • 但是这使用了KEYS?文档说我们不应该在生产中使用它. (2认同)

Eli*_*Eli 71

这是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:
Run Code Online (Sandbox Code Playgroud)

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

编辑: Per Kikito的评论如下,如果您有更多的键要删除Redis服务器中的可用内存,您将遇到"太多要解压的元素"错误.在这种情况下,做:

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

正如Kikito建议的那样.

  • 如果您有大量的键(错误是"解压缩的元素太多"),上面的代码将显示为tank.我建议在Lua部分使用一个循环:`for _,k in ipairs(redis.call('keys',KEYS [1]))do redis.call('del',k)end` (10认同)
  • 任何基于 Lua 的解决方案都将违反 `EVAL` 的语义,因为它没有预先指定它将操作的键。它应该在单个实例上工作,但不要指望它与 Redis Cluster 一起工作。 (3认同)
  • Lua 的 `unpack` 将表转换为“自变量列表”(其他语言称之为 `explode`),但最大数量不依赖于系统内存;它通过`LUAI_MAXSTACK`常量在lua中修复。在 Lua 5.1 和 LuaJIT 中是 8000,在 Lua 5.2 中是 100000。IMO 推荐使用 for 循环选项。 (2认同)

Ita*_*ber 62

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

从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
Run Code Online (Sandbox Code Playgroud)

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

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

更新:一个衬垫具有相同的基本效果 -

$ redis-cli --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli DEL
Run Code Online (Sandbox Code Playgroud)

  • 尽管如此,避免使用KEYS绝对是最佳实践,因此无论非原子删除是否可行,这都是一个很好的解决方案. (9认同)

ran*_*mor 38

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

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

替换key:*:pattern为您自己的模式,并将其输入redis-cli,你很高兴.

来自http://redis.io/commands/del的信用lisco


Yas*_*eja 34

我在redis 3.2.8中使用下面的命令

redis-cli KEYS *YOUR_KEY_PREFIX* | xargs redis-cli DEL
Run Code Online (Sandbox Code Playgroud)

您可以从此处获得与密钥模式搜索相关的更多帮助: - https://redis.io/commands/keys.根据您的要求使用方便的水钻样式,如*YOUR_KEY_PREFIX*YOUR_KEY_PREFIX??或任何其他.

如果您有任何人集成了Redis PHP库,那么下面的功能将对您有所帮助.

flushRedisMultipleHashKeyUsingPattern("*YOUR_KEY_PATTERN*"); //function call

function flushRedisMultipleHashKeyUsingPattern($pattern='')
        {
            if($pattern==''){
                return true;
            }

            $redisObj = $this->redis;
            $getHashes = $redisObj->keys($pattern);
            if(!empty($getHashes)){
                $response = call_user_func_array(array(&$redisObj, 'del'), $getHashes); //setting all keys as parameter of "del" function. Using this we can achieve $redisObj->del("key1","key2);
            }
        }
Run Code Online (Sandbox Code Playgroud)

谢谢 :)


efa*_*ruk 19

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

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

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

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

  • 使用lua比使用xargs快很多,顺序为10 ^ 4。 (2认同)

Adr*_*n B 19

我使用 EVAL 命令的最简单变体成功了这一点:

EVAL "return redis.call('del', unpack(redis.call('keys', 'my_pattern_here*')))" 0
Run Code Online (Sandbox Code Playgroud)

my_pattern_here用我的值替换的地方。

  • 这有效,但我必须使用单引号。示例: `EVAL "redis.call('del', unpack(redis.call('keys', 'my_pattern_here*')))" 0` (3认同)

Vis*_*kur 18

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

假设您的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'

Ex- 'xyz_category_fpc '这里xyz是一个网站名称,这些密钥与电子商务网站的产品和类别相关,并由FPC生成.

如果您使用此命令如下 -

redis-cli --scan --pattern 'key*' | xargs redis-cli del
Run Code Online (Sandbox Code Playgroud)

要么

redis-cli --scan --pattern 'xyz_category_fpc*' | xargs redis-cli del
Run Code Online (Sandbox Code Playgroud)

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

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

使用redis-cli:

  1. FLUSHDB - 从连接的CURRENT数据库中删除数据.
  2. FLUSHALL - 从所有数据库中删除数据.

例如: - 在你的shell中:

redis-cli flushall
redis-cli flushdb
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,但是管道输出到`redis-cli del`并不是原子的。 (2认同)
  • 如果 key 有空格或双引号,则不起作用。 (2认同)

Inc*_*c33 12

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

redis-cli keys "pattern: *" | xargs -L1 -I '$' echo '"$"' | xargs redis-cli del
Run Code Online (Sandbox Code Playgroud)


Que*_* S. 11

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

redis-cli --scan --pattern sf_* | awk '{print $1}' | sed "s/^/'/;s/$/'/" | xargs redis-cli del
Run Code Online (Sandbox Code Playgroud)

  • 该脚本运行完美,经过超过 25000 个键的测试。 (2认同)
  • 您还可以使用这个有趣的表达式 ` awk '{ print "'"'"'" $1 "'"'"'"}'` 在 awk 中添加单引号 (2认同)

小智 9

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

redis-cli -h HOST -p PORT  --scan --pattern "prefix:*" | xargs -n 100 redis-cli DEL
Run Code Online (Sandbox Code Playgroud)

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


Kha*_*ear 9

添加到此答案:

要查找前 1000 个键:

EVAL "return redis.call('scan', 0, 'COUNT', 1000, 'MATCH', ARGV[1])" 0 find_me_*
Run Code Online (Sandbox Code Playgroud)

要删除它们:

EVAL "return redis.call('del', unpack(redis.call('SCAN', 0, 'COUNT', 1000, 'MATCH', ARGV[1])[2]))" 0 delete_me_*
Run Code Online (Sandbox Code Playgroud)


小智 9

这是在不使用任何 xargs 魔法的情况下想到的最简单的方法

纯粹的狂欢!

redis-cli DEL $(redis-cli KEYS *pattern*)
Run Code Online (Sandbox Code Playgroud)


Max*_*Max 8

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

session:sessionid:key-x - value of x
session:sessionid:key-y - value of y
session:sessionid:key-z - value of z
Run Code Online (Sandbox Code Playgroud)

因此,每个条目都是一个单独的键值对.当会话被破坏,我想通过删除与模式键删除所有会话数据session:sessionid:*-但Redis的不具备这样的功能.

我做了什么:将会话数据存储在哈希中.我刚刚创建的散列ID的哈希值session:sessionid,然后我推key-x,key-y,key-z在哈希(顺序没有关系对我来说),如果我不需要散列了我只是做一个DEL session:sessionid与哈希ID消失了相关的所有数据.DEL是原子的并且访问数据/将数据写入散列是O(1).

  • 但是,哈希中的字段缺少过期功能,这有时非常有用. (3认同)

小智 8

// TODO

你认为它的命令没有意义,有时 Redis 命令就像DEL无法正常工作一样,并解决了这个问题

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


小智 5

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

  • 但我无法弄清楚如何在这里使用它们.DEL本身就是原子的(或者我认为).在我执行EXEC之前,我无法从KEYS获取值,因此我不能在同一个MULTI中使用KEYS和DEL. (4认同)

plh*_*lhn 5

仅供参考.

  • 只使用bash和 redis-cli
  • 不使用keys(这个用途scan)
  • 群集模式下运行良好
  • 不是原子的

也许你只需要修改大写字符.

scan-match.sh

#!/bin/bash
rcli=“/YOUR_PATH/redis-cli" 
default_server="YOUR_SERVER"
default_port="YOUR_PORT"
servers=`$rcli -h $default_server -p $default_port cluster nodes | grep master | awk '{print $2}' | sed 's/:.*//'`
if [ x"$1" == "x" ]; then 
    startswith="DEFAULT_PATTERN"
else
    startswith="$1"
fi
MAX_BUFFER_SIZE=1000
for server in $servers; do 
    cursor=0
    while 
        r=`$rcli -h $server -p $default_port scan $cursor match "$startswith*" count $MAX_BUFFER_SIZE `
        cursor=`echo $r | cut -f 1 -d' '`
        nf=`echo $r | awk '{print NF}'`
        if [ $nf -gt 1 ]; then
            for x in `echo $r | cut -f 1 -d' ' --complement`; do 
                echo $x
            done
        fi
        (( cursor != 0 ))
    do
        :
    done
done
Run Code Online (Sandbox Code Playgroud)

clear-redis-key.sh

#!/bin/bash
STARTSWITH="$1"

RCLI=YOUR_PATH/redis-cli
HOST=YOUR_HOST
PORT=6379
RCMD="$RCLI -h $HOST -p $PORT -c "

./scan-match.sh $STARTSWITH | while read -r KEY ; do
    $RCMD del $KEY 
done
Run Code Online (Sandbox Code Playgroud)

在bash提示符下运行

$ ./clear-redis-key.sh key_head_pattern
Run Code Online (Sandbox Code Playgroud)


tek*_*ara 5

使用 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
Run Code Online (Sandbox Code Playgroud)


Suf*_*lek 5

请使用此命令并尝试:

redis-cli --raw keys "$PATTERN" | xargs redis-cli del
Run Code Online (Sandbox Code Playgroud)