私のRedis DBには、prefix:<numeric_id>
個のハッシュがあります。
時々私はそれらをすべて原子的に一掃したいと思う。分散ロックメカニズムを使用せずにこれを行うにはどうすればよいですか?
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の資料 を参照してください。
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から古い値を安全に取り出し、古いキーを削除することができます。
これが、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
キキトが示唆したように。
免責事項:以下の解決策 しない 原子性を提供します。
V2.8からは 本当に KEYS [1]の代わりに _ scan _ コマンドを使いたいと思うでしょう。次の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
私は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 library を統合しているなら、以下の機能が役に立ちます。
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);
}
}
ありがとうございました :)
他の答えの解析に問題がある人のために:
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
このコマンドを使用してキーを削除することもできます。 -
あなたのredisにはたくさんの種類のキーがあるとします。
Ex- ' xyz_category_fpc 'ここで xyz は サイト名 であり、これらのキーはEコマースサイトの製品およびカテゴリに関連し、FPCによって生成されます。
以下のようにこのコマンドを使うと
redis-cli --scan --pattern 'key*' | xargs redis-cli del
OR
redis-cli --scan --pattern 'xyz_category_fpc*' | xargs redis-cli del
' xyz_category_fpc 'のようにすべてのキーを削除します(1、2、3のキーを削除)。他の4、5、6の数字キーを削除するには、上記のコマンドで ' xyz_product_fpc 'を使用します。
すべて削除 in Redis を実行する場合は、次のコマンドに従ってください。
redis-cliの場合:
例: - あなたのシェルで:
redis-cli flushall
redis-cli flushdb
@ mcdizleの解決策は機能していませんそれは1つのエントリに対してのみ機能します。
これは、同じプレフィックスを持つすべてのキーに対して機能します。
EVAL "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" 0 prefix*
注: / 'prefix'をあなたのキープレフィックスに置き換えてください。
キーの名前にスペースがある場合は、これをbashで使用できます。
redis-cli keys "pattern: *" | xargs -L1 -I '$' echo '"$"' | xargs redis-cli del
@ itamarの答えは素晴らしいですが、返事の解析は私にとってはうまくいきませんでした、特に。特定のスキャンでキーが見つからない場合コンソールから直接、おそらくもっと簡単な解決策:
redis-cli -h Host -p PORT --scan --pattern "prefix:*" | xargs -n 100 redis-cli DEL
これにはSCANも使用されますが、これは本番環境ではKEYSよりも望ましいですが、アトミックではありません。
私はちょうど同じ問題を抱えていました。ユーザーのセッションデータを次の形式で格納しました。
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 の中にセッションデータを保存する。私はちょうどsession:sessionid
のハッシュIDでハッシュを作成し、そして私はそのハッシュにkey-x
、key-y
、key-z
をプッシュします(順序は関係ありません)そしてもうそのハッシュが必要でなければDEL session:sessionid
とそれに関連するすべてのデータをハッシュIDはなくなりました。 DEL
はアトミックで、データへのアクセス/ハッシュへのデータの書き込みはO(1)です。
MULTI/EXEC/DISCARD が役に立つと思います。 100%トランザクション相当 ではなく、削除を他の更新から分離できるはずです。
ちなみに。
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
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
Bashプロンプトで実行
$ ./clear-redis-key.sh key_head_pattern
このコマンドを使用して試してください。
redis-cli --raw keys "$PATTERN" | xargs redis-cli del
これは FastoRedis の "Remove branch"機能を介して簡単に実装され、削除したいブランチを選択するだけです。
これは質問に対する直接的な答えではありませんが、私自身の答えを探すときにここに来たので、ここで共有します。
あなたがマッチしなければならない数億または数億の鍵を持っているなら、ここに与えられた答えはかなりの時間(数分)の間Redisを無反応にし、潜在的にメモリ消費のためクラッシュするでしょう。操作の途中で開始してください。
次のアプローチは紛れもなく醜いです、しかし私はよりよいものを見つけませんでした。ここでのアトミック性は問題ではありません。この場合の主な目標は、Redisを常に稼働させ、常に100%応答することです。すべてのキーが1つのデータベースにあり、パターンを一致させる必要がない場合は、完全に機能しますが、 http://redis.io/commands/FLUSHDB を使用することはできません。
考えは簡単です。ループで実行され、 http://redis.io/commands/SCAN または http:// redisのようにO(1)操作を使用するスクリプトを書くことです。 io/commands/RANDOMKEY キーを取得し、それらがパターンに一致するかどうかをチェックし(必要なら)、そして http://redis.io/commands/DEL それらは一つずつ。
それを行うより良い方法があるならば、私に答えを更新するつもりです、私に知らせてください。
レーキタスクとしてのredis-cli -n 3 flushdb
のような何かのノンブロッキング代用としての、Rubyのランダムキーを使った実装例
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
KEYSではなくSCANを使用したバージョン(本番サーバーに推奨)およびxargsではなく--pipe
。
Xargsよりもpipeを優先します。なぜなら、それはより効率的で、あなたのキーが引用符やあなたのシェルが試して解釈する他の特殊文字を含んでいるときに働くからです。この例の正規表現の置換は、キーを二重引用符で囲み、その内部の二重引用符をエスケープします。
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
上記の方法のほとんどを試しましたが、いくつかの検索の後、これらの点を見つけました:
-n [number]
を使用してデータベースを決定する必要がありますdel
を使用しますが、数千または数百万のキーがある場合は、unlinkが非ブロッキングであるため、unlink
を使用することをお勧めしますdelがブロックしている間、詳細についてはこのページをご覧ください nlink vs delkeys
もdelのようなもので、ブロックしていますそのため、このコードを使用して、パターンごとにキーを削除しました。
redis-cli -n 2 --scan --pattern '[your pattern]' | xargs redis-cli -n 2 unlink
私はいくつかのツールを持っているか、またはLua表現を実行することに関連するすべての答えを支持します。
私の側からもう一つのオプション:
私たちのプロダクションデータベースとプリプロダクションデータベースには、何千もの鍵があります。時々、いくつかのキーを(いくつかのマスクで)削除し、いくつかの基準などで変更する必要があります。もちろん、特にシャーディング(各物理で512論理DB)があるため、CLIから手動で行う方法はありません。
この目的のために、私はこれらすべての仕事をするJavaクライアントツールを書きます。キーを削除する場合、ユーティリティは非常に単純なものになります。1つのクラスだけが存在します。
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();
}
}