web-dev-qa-db-ja.com

Redisを使ってパターンにマッチするキーを自動的に削除する方法

私のRedis DBには、prefix:<numeric_id>個のハッシュがあります。

時々私はそれらをすべて原子的に一掃したいと思う。分散ロックメカニズムを使用せずにこれを行うにはどうすればよいですか?

516

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の資料 を参照してください。

393
mcdizzle

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から古い値を安全に取り出し、古いキーを削除することができます。

659
Casey

これが、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

キキトが示唆したように。

68
Eli

免責事項:以下の解決策 しない 原子性を提供します。

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
60
Itamar Haber

私は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);
            }
        }

ありがとうございました :)

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

34
randomor

このコマンドを使用してキーを削除することもできます。 -

あなたの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 サイト名 であり、これらのキーは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の場合:

  1. _ flushdb _ - 接続のCURRENTデータベースからデータを削除します。
  2. _ flushall _ - すべてのデータベースからデータを削除します。

例: - あなたのシェルで:

redis-cli flushall
redis-cli flushdb
17
Vishal Thakur

@ mcdizleの解決策は機能していませんそれは1つのエントリに対してのみ機能します。

これは、同じプレフィックスを持つすべてのキーに対して機能します。

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

注: / 'prefix'をあなたのキープレフィックスに置き換えてください。

16
efaruk

キーの名前にスペースがある場合は、これをbashで使用できます。

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

@ itamarの答えは素晴らしいですが、返事の解析は私にとってはうまくいきませんでした、特に。特定のスキャンでキーが見つからない場合コンソールから直接、おそらくもっと簡単な解決策:

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

これにはSCANも使用されますが、これは本番環境ではKEYSよりも望ましいですが、アトミックではありません。

10
Guitan

私はちょうど同じ問題を抱えていました。ユーザーのセッションデータを次の形式で格納しました。

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-xkey-ykey-zをプッシュします(順序は関係ありません)そしてもうそのハッシュが必要でなければDEL session:sessionidとそれに関連するすべてのデータをハッシュIDはなくなりました。 DELはアトミックで、データへのアクセス/ハッシュへのデータの書き込みはO(1)です。

8
Max

MULTI/EXEC/DISCARD が役に立つと思います。 100%トランザクション相当 ではなく、削除を他の更新から分離できるはずです。

5
alexpopescu

ちなみに。

  • 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

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
5
plhn

このコマンドを使用して試してください。

redis-cli --raw keys "$PATTERN" | xargs redis-cli del
4
Sufiyan Malek

これは FastoRedis の "Remove branch"機能を介して簡単に実装され、削除したいブランチを選択するだけです。

enter image description here

3

これは質問に対する直接的な答えではありませんが、私自身の答えを探すときにここに来たので、ここで共有します。

あなたがマッチしなければならない数億または数億の鍵を持っているなら、ここに与えられた答えはかなりの時間(数分)の間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
2
Spajus

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
2
tekumara

上記の方法のほとんどを試しましたが、いくつかの検索の後、これらの点を見つけました:

  • redisに複数のdbがある場合は、-n [number]を使用してデータベースを決定する必要があります
  • いくつかのキーがある場合はdelを使用しますが、数千または数百万のキーがある場合は、unlinkが非ブロッキングであるため、unlinkを使用することをお勧めしますdelがブロックしている間、詳細についてはこのページをご覧ください nlink vs del
  • keysもdelのようなもので、ブロックしています

そのため、このコードを使用して、パターンごとにキーを削除しました。

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

私はいくつかのツールを持っているか、または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();
    }

}
0
Denys