Redisを使用してパターンに一致するキーをアトミックに削除する方法


576

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

時々私はそれらをすべてアトミックにパージしたいです。分散ロックメカニズムを使用せずにこれを行うにはどうすればよいですか?


こんにちはスティーブ、私のウェブサイトに問題があります。他のブログmind-geek.net/nosql/redis/delete-keys-specific-expiry-timeに追加しました。これがお役に立てば幸いです。
Gaurav Tewari

43
これは非常に一般的なシナリオであり、Redisチームがネイティブコマンドを追加することを検討してほしいと思います。
Todd Menier、

現在、Luaでそれを行うことができます。以下を参照してください。
Alexander Gladysh 2013

3
@ToddMenier提案されただけで、なぜそれが起こらないのかという理由を取り戻しました:github.com/antirez/redis/issues/2042
Ray

1
多数のキー、特殊文字のあるキーなどの処理方法について、関連する質問をする人がたくさんいます。この問題が発生しており、この質問に回答が投稿されていないと思われるため、別の質問を作成しました。:ここでは他の質問ですstackoverflow.com/questions/32890648/...
jakejgordon

回答:


431

redis 2.6.0以降では、アトミックに実行されるluaスクリプトを実行できます。書いたことはありませんが、こんな感じになると思います

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

警告:パフォーマンスの問題のため、Redisドキュメントに記載されているように、keys コマンドは本番環境での通常の操作には使用できません。このコマンドは、デバッグと特別な操作を目的としています。続きを読む

EVALのドキュメントを参照してください。


23
重要な注意:プレフィックスと一致するキーが数千以上ある場合、これは失敗します。
Nathan Osman

93
この1つは、キーの大きな数のために働いている: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:*
sheerun

181
痛い... redisは単純なキー/ストアキャッシュとしてよく使用されます。これはdel prefix:* 基本的な操作のようです:/
Ray

5
@Ray率直に言って、その機能が必要な場合は、数値データベースまたはサーバーでデータを分割し、flush / flushdbを使用する必要があります
Marc Gravell

9
はい、パターンに一致するキーがない場合は失敗します。:私は、デフォルトのキーを追加したことを修正するためにEVAL "return redis.call('del', 'defaultKey', unpack(redis.call('keys', ARGV[1])))" 0 prefix:*
manuelmhtr

706

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


14
申し訳ありませんが、これはアトミックな削除ではありません。KEYSとDELの間に誰かが新しいキーを追加する可能性があります。削除したくない。
Alexander Gladysh

36
KEYSコマンドの後に作成されるキーは削除されません。
ケーシー、

6
いくつかの不正なキーをクリアする必要があるだけだったので、引用符の外にキーを移動する必要があったことを除いて、Caseyの最初の答えは正解でした。redis-cliKEYS "prefix:*" | xargs redis-cli DEL
jslatts

19
最初の答えも助けになりました。他の変形は、あなたのRedisのキーは、引用符や他の文字が含まれている場合、混乱xargsのアップその:redis-cli KEYS "prefix:*" | xargs --delim='\n' redis-cli DEL
overthink

18
マルチブルデータベース(キースペース)がある場合、これはトリックです:db3のキーを削除する必要があるとしましょう:redis-cli -n 3 KEYS "prefix:*" | xargs redis-cli -n 3 DEL
Christoffer

73

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

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


10
上記のコードは、キーの数が多い場合に失敗します(エラーは「要素が多すぎてアンパックできません」)。私はLuaの部分にループを使用することをお勧めします:for _,k in ipairs(redis.call('keys', KEYS[1])) do redis.call('del', k) end
kikito

@kikito、はい、luaが削除したいキーの数までスタックを拡張できない場合(おそらくメモリ不足が原因)、forループでそれを行う必要があります。必要がない限り、これを行うことはお勧めしません。
Eli

1
Lua unpackは「独立変数のリスト」(他の言語ではと呼ばれますexplode)のテーブルを変換しますが、最大数はシステムメモリに依存しません。LUAI_MAXSTACK定数を介してluaで固定されています。Lua 5.1およびLuaJITでは8000、Lua 5.2では100000です。forループオプションはIMOをお勧めします。
kikito 2013

1
luaスクリプティングはRedis 2.6以降でのみ利用可能であることに注意する必要があります
Wallacer 2013年

1
Luaベースのソリューションは、EVAL操作対象のキーを事前に指定していないため、のセマンティクスに違反します。単一のインスタンスで動作するはずですが、Redisクラスターで動作するとは期待できません。
Kevin Christopher Henry

67

免責事項:次の解決策原子性を提供しません

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を使用しないでください。キースペースのサブセットでキーを見つける方法を探している場合は、セットの使用を検討してください。

更新:同じ基本的な効果のための1つのライナー-

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

9
それでも、KEYSを回避することは間違いなくベストプラクティスと見なされているため、非アトミックな削除が可能な場合は、これが優れたソリューションです。
fatal_error 2015年

これでうまくいきました。しかし、私のキーは、だから私は追加する必要がありましたデータベース1であることを起こっ-n 1それぞれにredis-cli:呼び出しredis-cli -n 1 --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli -n 1 DEL
ロブ・ヨハンセン

キーに特殊文字が含まれている場合、これは機能しないことに注意してください
mr1031011

興味深い価値のある発見... xargsのことを引用する方法はあるのでしょうか...
Itamar Haber

-L 100は何をしますか?
Aparna

41

他の回答の解析で問題が発生した場合:

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

key:*:pattern独自のパターンに置き換えてこれを入力すると、準備完了redis-cliです。

クレジットliscoから:http ://redis.io/commands/del


37

redis 3.2.8で以下のコマンドを使用しています

redis-cli KEYS *YOUR_KEY_PREFIX* | xargs redis-cli DEL

あなたはここからキーパターン検索に関連するより多くのヘルプを得ることができます:-https : //redis.io/commands/keys。以下のようなあなたの要件ごとに、あなたの便利なglobスタイルのパターンを使用する*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);
            }
        }

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


23

@mcdizleのソリューションは機能していません。1つのエントリに対してのみ機能します。

これは同じ接頭辞を持つすべてのキーで機能します

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

注: 'prefix'をキープレフィックスに置き換えてください...


2
luaを使用すると、xargsを使用するよりも10 ^ 4の順に高速になります。
ディーパック

22

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

あなたの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'

例 ' xyz_category_fpc 'ここでxyzサイト名であり、これらのキーはeコマースサイトの製品とカテゴリに関連し、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、これらCommands-に従ってください

redis-cliを使用:

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

例:-シェルで:

redis-cli flushall
redis-cli flushdb

3
おかげで、出力をパイプすることredis-cli delはアトミックではありません。
Alexander Gladysh 2017年

13

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

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

10

@itamarの答えは素晴らしいですが、返信の解析がうまくいきませんでした。特定のスキャンでキーが見つからない場合。コンソールから直接、おそらくより簡単なソリューション:

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

これは、SCANも使用します。これは、実稼働ではKEYSよりも望ましいですが、アトミックではありません。


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にはそのような機能はありません。

私がしたこと:セッションデータをハッシュ内に保存します。私はちょうどのハッシュIDを持つハッシュを作成session:sessionidし、その後、私がプッシュするkey-xkey-ykey-zそのハッシュに(順序は私には問題ではありませんでした)と私は、ハッシュはもう私はちょうど行うことを必要いけない場合DEL session:sessionid、そのハッシュIDに関連付けられているすべてのデータがなくなっています。DELアトミックであり、データへのアクセス/ハッシュへのデータの書き込みはO(1)です。


良い解決策ですが、私の価値観はハッシュそのものです。また、Redisはハッシュを別のハッシュ内に格納します。
アレクサンダーグラディッシュ2010

3
ただし、ハッシュ内のフィールドには有効期限の機能がないため、非常に便利なことがあります。
Evi Song、

私にとって、これはこれまでで最もクリーンで最も単純な答えです
Sebastien H.

セットはもっと意味をなさないのですか?
Jack Tuck

5

あなたに役立つかもしれないと思うのはMULTI / EXEC / DISCARDです。されていないが、トランザクションの100%と同等、あなたが他の更新プログラムから削除を分離することができるはずです。


4
しかし、ここではそれらの使い方を理解できません。DELはそれ自体がアトミックです(または私はそう思います)。また、EXECを実行するまでKEYSから値を取得できないため、同じMULTIでKEYSとDELを使用できません。
Alexander Gladysh

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

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

Guide$CLASSMETADATA][1]たとえば、キーに特殊文字が含まれていると、他の回答が機能しない場合があります。各キーを引用符にラップすると、適切に削除されます。

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

2
このスクリプトは完璧に動作し、25000以上のキーでテストされています。
ジョーディ

1
この面白い表現 `awk '{print"' "'"' "$ 1" '"'" '"}'`を使用して、awkに一重引用符を追加することもできます
Roberto Congiu

3

KEYS(運用サーバーで推奨)ではなくSCANを使用し--pipe、xargs を使用しないバージョン。

xargsよりもパイプを使用する方が効率的であり、キーに引用符や、シェルが試して解釈するその他の特殊文字が含まれている場合に機能するためです。この例の正規表現の置換では、キーを二重引用符で囲み、内部のすべての二重引用符をエスケープします。

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

このソリューションは、約7mのキーでもうまく機能しました。
ダニー

2

これは質問への直接の回答ではありませんが、自分の回答を探すときにここにたどり着いたので、ここで共有します。

一致する必要があるキーが数千万から数億個ある場合、ここでの回答により、Redisはかなりの時間(分)応答しなくなり、メモリ消費のためにクラッシュする可能性があります(バックグラウンドでの保存は確実に行われます)。操作の途中で開始します)。

次のアプローチは紛れもなく醜いですが、私はより良いものを見つけませんでした。原子性はここでは問題外です。この場合の主な目標は、Redisを常に稼働状態に保ち、100%応答できるようにすることです。いずれかのデータベースにすべてのキーがあり、どのパターンにも一致する必要がない場合は完全に機能しますが、ブロックする性質があるため、http://redis.io/commands/FLUSHDBを使用できません。

アイデアは簡単です:ループで実行され、http://redis.io/commands/SCANhttp://redis.io/commands/RANDOMKEYなどのO(1)操作を使用してキーを取得するスクリプトを記述し、それらがパターンに一致させ(必要な場合)、1つずつ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


2

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

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

アトミックではなく、他の回答を複製します。
マシュー

1

上記のほとんどの方法を試しましたが、うまくいきませんでした。いくつか検索したところ、次の点が見つかりました。

  • redisに複数のdbがある場合は、データベースを使用して決定する必要があります -n [number]
  • あなたは、いくつかのキーを使用している場合delしかし、キーの数千または数百万人が存在する場合、それが使用することをお勧めしますunlinkので、アンリンクは、非ブロッキングであるデルは、より多くの情報訪問のために、このページをブロックしている間、デル対リンク解除を
  • またkeys、デルのようであり、ブロックしています

だから私はこのコードを使ってパターンでキーを削除しました:

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

0

貧しい人の原子の大量削除?

多分あなたはそれらすべてを同じ秒にEXPIREATするように設定できます-将来の数分など-そしてその時間まで待って、それらすべてが同時に「自己破壊」するのを見ることができます。

しかし、それがどれほど原子的であるかは本当にわかりません。


0

今のところ、Redisクライアントを使用して、最初にSCAN(パターンマッチングをサポート)を実行し、次に各キーを個別にDELすることができます。

ただし、ここでパターマッチングデルを作成するための公式redis githubに問題があります。役立つと思われる場合は、愛を示してください。


-1

ツールの使用や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();
    }

}

-1

以下のコマンドは私のために働きました。

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

-3

Spring RedisTemplate自体が機能を提供します。最新バージョンのRedissonClientは、「deleteByPattern」機能を廃止しました。

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

2
Redissonのサンプルコードを更新しました。コードはRedissonのようにアトミックアプローチではありません。keysdeleteメソッドの呼び出しの間に表示される新しいキーがあります。
Nikita Koksharov
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.