Programing

Redis를 사용하여 패턴과 일치하는 키를 원자 적으로 삭제하는 방법

lottogame 2020. 2. 10. 22:01
반응형

Redis를 사용하여 패턴과 일치하는 키를 원자 적으로 삭제하는 방법


내 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에 새 데이터를 씁니다. 그런 다음 접두사 : 2 : 1 및 접두사 : 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 시작 당신은 정말 사용하려는 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를 매우 신중하게 프로덕션 환경에서만 사용해야하는 명령으로 고려하십시오. 큰 데이터베이스에 대해 실행될 때 성능이 저하 될 수 있습니다. 이 명령은 키 공간 레이아웃 변경과 같은 디버깅 및 특수 작업을위한 것입니다. 일반 응용 프로그램 코드에서 키를 사용하지 마십시오. 키 공간의 하위 세트에서 키를 찾는 방법을 찾고 있다면 세트 사용을 고려하십시오.

업데이트 : 동일한 기본 효과에 대한 하나의 라이너-

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

다른 답변을 파싱하는 데 어려움이있는 사람들을 위해 :

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 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*

참고 : '접두사'를 키 접두사로 바꿔야합니다.


이 명령을 사용하여 키를 삭제할 수도 있습니다.

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'

Exyz ' 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 '를 사용하십시오 .

당신이 할 경우 모든 삭제레디 스 , 다음이 Commands-에 따라

redis-cli로 :

  1. FLUSHDB- 연결의 CURRENT 데이터베이스에서 데이터를 제거합니다.
  2. FLUSHALL- 모든 데이터베이스에서 데이터를 제거합니다.

예를 들면 :-쉘에서 :

redis-cli flushall
redis-cli flushdb

키 이름에 공백이 있으면 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

이것은 또한 생산에서 KEYS보다 선호하지만 원자 적이 지 않은 SCAN을 사용합니다.


방금 같은 문제가있었습니다. 사용자의 세션 데이터를 다음 형식으로 저장했습니다.

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-x, key-y, key-z그 해시 (순서는 나에게 중요하지 않았다) 나는 해시 더 이상 난 그냥 할 것을 요구 해달라고하면 DEL session:sessionid그 해시 ID와 관련된 모든 데이터가 사라합니다. DEL원자 적이며 데이터에 액세스 / 해시 데이터 쓰기는 O (1)입니다.


참고로

  • 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

나는 당신을 도울 수있는 것이 MULTI / EXEC / DISCARD 라고 생각합니다 . 트랜잭션과 100 % 동일 하지는 않지만 삭제를 다른 업데이트와 분리 할 수 ​​있어야합니다.


FastoRedis의 "브랜치 제거"기능을 통해 간단하게 구현됩니다. 제거 하려는 브랜치를 선택하기 만하면 됩니다.

여기에 이미지 설명을 입력하십시오


이 명령을 사용하고 시도하십시오 :

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

프로덕션 서버에 권장되는 KEYS --pipe대신 xargs 대신 SCAN을 사용하는 버전 입니다.

나는 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

이것은 질문에 대한 직접적인 대답은 아니지만 내 답변을 검색 할 때 여기에 왔으므로 여기에서 공유 할 것입니다.

일치해야하는 수천 또는 수억 건의 키가있는 경우 여기에 제공된 답변으로 인해 Redis가 상당한 시간 (분) 동안 응답하지 않고 메모리 소비로 인해 충돌이 발생할 수 있습니다 (백그라운드 저장은 작업 중간에 시작하십시오).

다음 접근 방식은 명백히 추악하지만 더 나은 방법을 찾지 못했습니다. Atomicity는 여기서 의문의 여지가 없습니다.이 경우 주된 목표는 Redis를 100 % 응답하고 유지하는 것입니다. 데이터베이스 중 하나에 모든 키가 있고 어떤 패턴과도 일치하지 않아도 완벽하게 작동하지만 차단 특성 때문에 http://redis.io/commands/FLUSHDB를 사용할 수 없습니다 .

아이디어는 간단합니다. 루프에서 실행되고 http://redis.io/commands/SCAN 또는 http://redis.io/commands/RANDOMKEY 와 같은 O (1) 연산 을 사용하여 키를 가져 오는 스크립트를 작성하십시오. 패턴 (필요한 경우)과 http://redis.io/commands/DEL 을 하나씩 일치시킵니다 .

더 좋은 방법이 있다면 알려주세요. 답변을 업데이트하겠습니다.

Ruby에서 randomkey를 사용하여 레이크 작업으로 예제 구현 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

위에서 언급 한 대부분의 방법을 시도했지만 일부 검색 후이 점을 발견 한 후에는 효과가 없었습니다.

  • redis에 둘 이상의 db가있는 경우 다음을 사용하여 데이터베이스를 결정해야합니다 -n [number]
  • 몇 가지 키를 사용 del하지만 수천 또는 수백만 개의 키가있는 경우 del이 차단되는 동안 unlink는 비 차단unlink 이기 때문에 사용하는 것이 좋습니다. 자세한 내용은이 페이지를 방문하십시오 unlink vs del
  • 또한 keys델과 같고 차단 중입니다.

그래서이 코드를 사용하여 패턴별로 키를 삭제했습니다.

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

도구를 사용하거나 Lua 식을 실행하는 것과 관련된 모든 답변을 지원합니다.

내 편에서 한 가지 더 옵션 :

프로덕션 및 프로덕션 전 데이터베이스에는 수천 개의 키가 있습니다. 때때로 우리는 일부 키 (일부 마스크로)를 삭제하고 일부 기준에 따라 수정해야합니다. 물론, 특히 샤딩 (각 물리적마다 512 개의 논리적 DB)이있는 CLI에서 수동으로 수행 할 수있는 방법이 없습니다.

이를 위해이 모든 작업을 수행하는 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();
    }

}

아래 명령이 나를 위해 일했습니다.

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

지금은 redis 클라이언트를 사용하여 먼저 SCAN (패턴 일치 지원)을 수행 한 다음 각 키를 개별적으로 DEL 할 수 있습니다.

그러나 공식 레디 스에 대한 문제는 델 후두둑 - 상응를 만들 수있다 GitHub에있다 여기 , 당신이 유용한 경우 약간의 사랑을 보여 간다!


가난한 사람의 원자 대량 삭제?

아마도 몇 분 후에 같은 시간에 모두 같은 시간에 만료되도록 설정 한 다음 그 시간까지 기다렸다가 모두 "자체 파괴"를 볼 수있을 것입니다.

그러나 나는 그것이 얼마나 원자 적인지 잘 모르겠습니다.


Spring RedisTemplate 자체가 기능을 제공합니다. 최신 버전의 RedissonClient는 "deleteByPattern"기능을 더 이상 사용하지 않습니다.

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

참고 URL : https://stackoverflow.com/questions/4006324/how-to-atomically-delete-keys-matching-a-pattern-using-redis



반응형