在我的Redis数据库中,我有很prefix:<numeric_id>
多哈希值。
有时我想原子地清除它们。如何在不使用某些分布式锁定机制的情况下执行此操作?
在我的Redis数据库中,我有很prefix:<numeric_id>
多哈希值。
有时我想原子地清除它们。如何在不使用某些分布式锁定机制的情况下执行此操作?
Answers:
从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文档。
EVAL "local keys = redis.call('keys', ARGV[1]) \n for i=1,#keys,5000 do \n redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) \n end \n return keys" 0 prefix:*
del prefix:*
应该是一项基本操作:/
EVAL "return redis.call('del', 'defaultKey', unpack(redis.call('keys', ARGV[1])))" 0 prefix:*
以bash执行:
redis-cli KEYS "prefix:*" | xargs redis-cli DEL
更新
好,我明白了。这样的方法是:存储当前的其他增量前缀并将其添加到所有密钥中。例如:
您具有以下值:
prefix_prefix_actuall = 2
prefix:2:1 = 4
prefix:2:2 = 10
当需要清除数据时,请先更改prefix_actuall(例如,设置prefix_prefix_actuall = 3),以便您的应用程序将新数据写入键prefix:3:1和prefix:3:2。然后,您可以安全地从前缀:2:1和前缀:2:2中获取旧值,并清除旧键。
redis-cli KEYS "prefix:*" | xargs --delim='\n' redis-cli DEL
redis-cli -n 3 KEYS "prefix:*" | xargs redis-cli -n 3 DEL
这是在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服务器中的可用内存,则会遇到“要分解的元素太多”错误。在这种情况下,请执行以下操作:
for _,k in ipairs(redis.call('keys', ARGV[1])) do
redis.call('del', k)
end
正如Kikito建议的那样。
for _,k in ipairs(redis.call('keys', KEYS[1])) do redis.call('del', k) end
unpack
转换了“独立变量列表”中的表(其他语言称为explode
),但是最大数量不依赖于系统内存。它通过LUAI_MAXSTACK
常量固定在lua中。在Lua 5.1和LuaJIT中,它是8000;在Lua 5.2中,它是100000。for IMO推荐使用for循环选项。
EVAL
因为它没有预先指定将要操作的键。它应该在单个实例上工作,但是不要期望它与Redis Cluster一起工作。
免责声明:以下解决方案不提供原子性。
从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。如果您正在寻找一种在键空间的子集中查找键的方法,请考虑使用集合。
更新:具有相同基本效果的一根衬板-
$ redis-cli --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli DEL
-n 1
到每个redis-cli
调用中:redis-cli -n 1 --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli -n 1 DEL
我在Redis 3.2.8中使用以下命令
redis-cli KEYS *YOUR_KEY_PREFIX* | xargs redis-cli DEL
您可以从以下网站获得与按键模式搜索有关的更多帮助:-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);
}
}
谢谢 :)
@mcdizle的解决方案不起作用,它仅适用于一个条目。
这个适用于所有具有相同前缀的键
EVAL "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" 0 prefix*
注意:您应该将“ prefix”替换为您的密钥前缀...
您也可以使用此命令删除密钥:
假设您的Redis中有很多类型的键,例如-
EX-“ xyz_category_fpc ”这里的xyz是一个网站名称,以及这些键都涉及到产品和电子商务网站的类别,并通过FPC产生。
如果您按以下方式使用此命令-
redis-cli --scan --pattern 'key*' | xargs redis-cli del
要么
redis-cli --scan --pattern 'xyz_category_fpc*' | xargs redis-cli del
它将删除所有键,例如' xyz_category_fpc ”(删除键1、2和3)。要删除其他4、5和6数字键,请使用' xyz_product_fpc在上述命令中 '。
如果你想删除一切在 Redis的,然后按照这些命令-
使用redis-cli:
例如:-在您的shell中:
redis-cli flushall
redis-cli flushdb
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没有这种功能。
我所做的:将会话数据存储在hash中。我刚刚创建的散列ID的哈希值session:sessionid
,然后我推key-x
,key-y
,key-z
在哈希(为了没事情我),如果我不需要散列了我只是做了DEL session:sessionid
,并与哈希ID相关联的所有数据都将消失。DEL
是原子的,访问数据/将数据写入哈希是O(1)。
我认为MULTI / EXEC / DISCARD可能对您有所帮助。虽然不是100%等价的事务,但是您应该能够将删除与其他更新隔离。
仅供参考。
redis-cli
keys
(使用scan
)也许您只需要修改大写字母。
扫描匹配
#!/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
清除-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
在bash提示符下运行
$ ./clear-redis-key.sh key_head_pattern
例如,如果您的密钥包含特殊字符,则其他答案可能不起作用Guide$CLASSMETADATA][1]
。将每个键括在引号中将确保将其正确删除:
redis-cli --scan --pattern sf_* | awk '{print $1}' | sed "s/^/'/;s/$/'/" | xargs redis-cli del
使用SCAN而非KEYS的版本(建议用于生产服务器),并且 --pipe
xargs的版本。
与xargs相比,我更喜欢使用管道,因为它效率更高,并且当您的键包含带引号或其他特殊字符的shell可以使用try和解释时,它更有效。此示例中的正则表达式替换将键括在双引号中,并在其中转义任何双引号。
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
这不是问题的直接答案,但是由于我是在搜索自己的答案时到达这里的,所以我将在这里分享。
如果您必须匹配数千万或数亿个键,则此处给出的答案将导致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
我尝试了上面提到的大多数方法,但是它们对我不起作用,经过一些搜索,我发现了以下几点:
-n [number]
del
但是如果有成千上万个键,则最好使用,unlink
因为在del 阻止时unlink是非阻塞的,有关更多信息,请访问此页面unlink vs delkeys
像del并且正在阻止所以我用下面的代码按模式删除密钥:
redis-cli -n 2 --scan --pattern '[your pattern]' | xargs redis-cli -n 2 unlink
我支持与拥有某些工具或执行Lua表达式有关的所有答案。
我这边还有一个选择:
在我们的生产和生产前数据库中,有成千上万的密钥。我们有时需要删除一些键(通过某些掩码),通过某些条件进行修改等。当然,无法从CLI手动进行操作,尤其是具有分片(每个物理分区中有512个逻辑数据库)的情况。
为此,我编写了完成所有这些工作的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();
}
}
Spring RedisTemplate本身提供了功能。最新版本的RedissonClient已弃用“ deleteByPattern”功能。
Set<String> keys = redisTemplate.keys("geotag|*");
redisTemplate.delete(keys);
keys
和delete
方法调用之间可能会出现新的键。