W moim DB Redis mam wiele prefix:<numeric_id>
skrótów.
Czasami chcę oczyścić je wszystkie atomowo. Jak to zrobić bez użycia rozproszonego mechanizmu blokującego?
W moim DB Redis mam wiele prefix:<numeric_id>
skrótów.
Czasami chcę oczyścić je wszystkie atomowo. Jak to zrobić bez użycia rozproszonego mechanizmu blokującego?
Odpowiedzi:
Począwszy od wersji redis 2.6.0, można uruchamiać skrypty lua, które wykonują się atomowo. Nigdy go nie napisałem, ale myślę, że wyglądałoby to mniej więcej tak
EVAL "return redis.call('del', unpack(redis.call('keys', ARGV[1])))" 0 prefix:[YOUR_PREFIX e.g delete_me_*]
Ostrzeżenie : jak mówi dokument Redis , ze względu na kwestie wydajności
keys
komenda nie powinna być używana do regularnych operacji produkcyjnych, to polecenie jest przeznaczone do debugowania i operacji specjalnych. Czytaj więcej
Zobacz dokumentację 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:*
powinna to być podstawowa operacja: /
EVAL "return redis.call('del', 'defaultKey', unpack(redis.call('keys', ARGV[1])))" 0 prefix:*
Wykonaj w bash:
redis-cli KEYS "prefix:*" | xargs redis-cli DEL
AKTUALIZACJA
Okej, zrozumiałem. A co z tym sposobem: zapisz bieżący dodatkowy przyrostowy przedrostek i dodaj go do wszystkich swoich kluczy. Na przykład:
Masz takie wartości:
prefix_prefix_actuall = 2
prefix:2:1 = 4
prefix:2:2 = 10
Kiedy musisz wyczyścić dane, najpierw zmieniasz prefix_actuall (na przykład ustaw prefix_prefix_actuall = 3), więc Twoja aplikacja zapisze nowe dane w prefiksie kluczy: 3: 1 i prefiksie: 3: 2. Następnie możesz bezpiecznie pobrać stare wartości z prefiksu: 2: 1 i prefiksu: 2: 2 i wyczyścić stare klucze.
redis-cli KEYS "prefix:*" | xargs --delim='\n' redis-cli DEL
redis-cli -n 3 KEYS "prefix:*" | xargs redis-cli -n 3 DEL
Oto całkowicie działająca i atomowa wersja usuwania symboli wieloznacznych zaimplementowana w Lua. Będzie działał znacznie szybciej niż wersja xargs z powodu znacznie mniejszej liczby połączeń sieciowych w przód i w tył, i jest całkowicie atomowy, blokując wszelkie inne żądania przeciwko redis, dopóki się nie skończy. Jeśli chcesz atomowo usunąć klucze w wersji Redis 2.6.0 lub nowszej, zdecydowanie jest to odpowiedni sposób:
redis-cli -n [some_db] -h [some_host_name] EVAL "return redis.call('DEL', unpack(redis.call('KEYS', ARGV[1] .. '*')))" 0 prefix:
To jest działająca wersja pomysłu @ mcdizzle w jego odpowiedzi na to pytanie. Kredyt za pomysł w 100% jedzie do niego.
EDYCJA: Zgodnie z komentarzem Kikito poniżej, jeśli masz więcej kluczy do usunięcia niż wolna pamięć na serwerze Redis, napotkasz błąd „zbyt wielu elementów do rozpakowania” . W takim przypadku wykonaj:
for _,k in ipairs(redis.call('keys', ARGV[1])) do
redis.call('del', k)
end
Jak sugerował Kikito.
for _,k in ipairs(redis.call('keys', KEYS[1])) do redis.call('del', k) end
unpack
przekształca tabelę w „listę zmiennych niezależnych” (nazywają to inne języki explode
), ale maksymalna liczba nie jest zależna od pamięci syste; jest ustalony w lua poprzez LUAI_MAXSTACK
stałą. W Lua 5.1 i LuaJIT jest to 8000, a w Lua 5.2 to 100000. Zalecana jest opcja dla pętli IMO.
EVAL
ponieważ nie określa z góry klawiszy, na których będzie działał. Powinien działać na jednym wystąpieniu, ale nie należy oczekiwać, że będzie działał z klastrem Redis.
Oświadczenie: następujące rozwiązanie nie zapewnia atomowości.
Począwszy od wersji 2.8 naprawdę chcesz używać polecenia SCAN zamiast KLUCZY [1]. Poniższy skrypt Bash pokazuje usuwanie kluczy według wzoru:
#!/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] KLUCZE to niebezpieczne polecenie, które może potencjalnie doprowadzić do DoS. Oto cytat ze strony dokumentacji:
Ostrzeżenie: rozważ KLUCZE jako polecenie, które powinno być używane w środowisku produkcyjnym z najwyższą ostrożnością. Może to zepsuć wydajność, gdy jest wykonywane na dużych bazach danych. To polecenie jest przeznaczone do debugowania i operacji specjalnych, takich jak zmiana układu przestrzeni klawiszy. Nie używaj KLUCZY w swoim zwykłym kodzie aplikacji. Jeśli szukasz sposobu na znalezienie kluczy w podzestawie przestrzeni kluczy, rozważ użycie zestawów.
AKTUALIZACJA: jedna wkładka dla tego samego podstawowego efektu -
$ redis-cli --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli DEL
-n 1
do każdego redis-cli
wywołania:redis-cli -n 1 --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli -n 1 DEL
Dla tych, którzy mieli problemy z analizowaniem innych odpowiedzi:
eval "for _,k in ipairs(redis.call('keys','key:*:pattern')) do redis.call('del',k) end" 0
Zastąp key:*:pattern
swój własny wzór i wprowadź go redis-cli
i możesz zacząć.
Kredyt lisco z: http://redis.io/commands/del
Korzystam z polecenia poniżej w redis 3.2.8
redis-cli KEYS *YOUR_KEY_PREFIX* | xargs redis-cli DEL
Możesz uzyskać więcej pomocy związanej z wyszukiwaniem wzorów kluczy tutaj: - https://redis.io/commands/keys . Użyj wygodnego globalnego wzoru zgodnie z wymaganiami, takimi jak *YOUR_KEY_PREFIX*
lubYOUR_KEY_PREFIX??
innymi.
A jeśli ktoś z was zintegrował bibliotekę Redis PHP, niż poniższa funkcja pomoże.
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);
}
}
Dziękuję Ci :)
Rozwiązanie @ mcdizle nie działa, działa tylko dla jednego wpisu.
Ten działa dla wszystkich kluczy z tym samym prefiksem
EVAL "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" 0 prefix*
Uwaga: należy zastąpić „prefiks” prefiksem klucza ...
Możesz także użyć tego polecenia, aby usunąć klucze: -
Załóżmy, że w twoich redisach jest wiele rodzajów kluczy, takich jak-
Ex „ xyz_category_fpc ” tu xyz jest nazwa_lokacji , a te klawisze są związane z produktami i kategoriami witryny e-commerce i generowane przez FPC.
Jeśli użyjesz tego polecenia, jak poniżej-
redis-cli --scan --pattern 'key*' | xargs redis-cli del
LUB
redis-cli --scan --pattern 'xyz_category_fpc*' | xargs redis-cli del
Usuwa wszystkie klucze, takie jak „ xyz_category_fpc ” (usuwa klucze 1, 2 i 3). Aby usunąć inne klawisze numeryczne 4, 5 i 6, użyj „ xyz_product_fpc ” w powyższym poleceniu.
Jeśli chcesz usunąć wszystko w Redis , wykonaj następujące polecenia:
Z redis-cli:
Na przykład: - w twojej powłoce:
redis-cli flushall
redis-cli flushdb
redis-cli del
nie jest atomowe.
Odpowiedź @ itamar jest świetna, ale parsowanie odpowiedzi nie działało dla mnie, szczególnie. w przypadku, gdy nie ma kluczy w danym skanie. Prawdopodobnie prostsze rozwiązanie bezpośrednio z konsoli:
redis-cli -h HOST -p PORT --scan --pattern "prefix:*" | xargs -n 100 redis-cli DEL
To również używa SCAN, który jest lepszy niż KLUCZE w produkcji, ale nie jest atomowy.
Właśnie miałem ten sam problem. Zapisałem dane sesji dla użytkownika w formacie:
session:sessionid:key-x - value of x
session:sessionid:key-y - value of y
session:sessionid:key-z - value of z
Tak więc każdy wpis był osobną parą klucz-wartość. Gdy sesja zostanie zniszczona, chciałem usunąć wszystkie dane sesji, usuwając klucze ze wzorem session:sessionid:*
- ale funkcja redis nie ma takiej funkcji.
Co zrobiłem: przechowuję dane sesji w haszu . Po prostu utworzyć skrót z id hash session:sessionid
i następnie wciskam key-x
, key-y
, key-z
w tym hash (kolejność nie miało znaczenia dla mnie), a jeśli ja nie potrzebuję, że hash już po prostu zrobić DEL session:sessionid
, a wszystkie dane powiązane z tym identyfikatorem skrótu nie ma. DEL
jest atomowy, a dostęp do danych / zapisywanie danych do skrótu to O (1).
Myślę, że może ci pomóc MULTI / EXEC / DISCARD . Chociaż nie jest to 100% ekwiwalent transakcji , powinieneś być w stanie odizolować usunięcia od innych aktualizacji.
Do Twojej wiadomości
redis-cli
keys
(używa scan
)Być może wystarczy zmodyfikować wielkie litery.
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
Uruchom po wyświetleniu monitu bash
$ ./clear-redis-key.sh key_head_pattern
Inne odpowiedzi mogą nie działać, jeśli Twój klucz zawiera specjalne znaki - Guide$CLASSMETADATA][1]
na przykład. Zawijanie każdego klucza w cudzysłowy zapewni ich prawidłowe usunięcie:
redis-cli --scan --pattern sf_* | awk '{print $1}' | sed "s/^/'/;s/$/'/" | xargs redis-cli del
Wersja wykorzystująca SCAN zamiast KLUCZY (zalecane dla serwerów produkcyjnych), --pipe
a nie xargs.
Wolę potok zamiast xargs, ponieważ jest bardziej wydajny i działa, gdy twoje klucze zawierają cudzysłowy lub inne znaki specjalne, które twoja powłoka próbuje i interpretuje. Podstawienie wyrażenia regularnego w tym przykładzie otacza klucz podwójnymi cudzysłowami i unika ewentualnych podwójnych cudzysłowów.
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
To nie jest bezpośrednia odpowiedź na pytanie, ale ponieważ znalazłem się tutaj, szukając własnych odpowiedzi, podzielę się tym tutaj.
Jeśli masz dziesiątki lub setki milionów kluczy, które musisz dopasować, podane tutaj odpowiedzi spowodują, że Redis nie będzie reagować przez znaczny czas (minuty?) I potencjalnie ulegnie awarii z powodu zużycia pamięci (pamiętaj, że zapisanie w tle spowoduje w środku operacji).
Poniższe podejście jest niezaprzeczalnie brzydkie, ale nie znalazłem lepszego. Atomowość nie wchodzi w rachubę, w tym przypadku głównym celem jest utrzymanie Redis w gotowości i reagowanie w 100% przypadków. Będzie działać idealnie, jeśli masz wszystkie klucze w jednej z baz danych i nie musisz dopasowywać żadnego wzorca, ale nie możesz go użyć http://redis.io/commands/FLUSHDB ze względu na jego blokujący charakter.
Pomysł jest prosty: napisz skrypt, który działa w pętli i używa operacji O (1), takiej jak http://redis.io/commands/SCAN lub http://redis.io/commands/RANDOMKEY aby uzyskać klucze, sprawdza, czy dopasuj wzór (jeśli go potrzebujesz) i http://redis.io/commands/DEL je jeden po drugim.
Jeśli istnieje lepszy sposób, daj mi znać, zaktualizuję odpowiedź.
Przykładowa implementacja z randomkey w Ruby, jako zadanie rake, nie blokujący substytut czegoś takiego jak 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
Jest to proste zaimplementowane za pomocą funkcji „Usuń gałąź” w FastoRedis , wystarczy wybrać gałąź, którą chcesz usunąć.
Próbowałem większości metod wymienionych powyżej, ale one nie działały dla mnie, po kilku wyszukiwaniach znalazłem następujące punkty:
-n [number]
del
ale jeśli są tysiące lub miliony kluczy, lepiej jest użyć, unlink
ponieważ rozłączanie nie blokuje, podczas gdy del blokuje, aby uzyskać więcej informacji, odwiedź tę stronę rozłącz vs.keys
są jak del i blokująwięc użyłem tego kodu, aby usunąć klucze według wzoru:
redis-cli -n 2 --scan --pattern '[your pattern]' | xargs redis-cli -n 2 unlink
masa atomowa biednego człowieka skasowana?
być może mógłbyś ustawić je wszystkie na WYGASZANIE w tej samej sekundzie - jak kilka minut w przyszłości - a następnie poczekać do tego czasu i zobaczyć je wszystkie w tym samym czasie.
ale nie jestem do końca pewien, jak by to wyglądało atomowo.
Teraz możesz użyć klienta redis i wykonać najpierw SKANOWANIE (obsługuje dopasowanie wzorca), a następnie DEL każdego klucza osobno.
Jednak nie jest to problem na oficjalnych REDiS GitHub stworzyć wzorcu dopasowywania-del tutaj , przejdź pokazać mu trochę miłości, jeśli okaże się to przydatne!
Popieram wszystkie odpowiedzi związane z posiadaniem jakiegoś narzędzia lub wykonania wyrażenia Lua.
Jeszcze jedna opcja z mojej strony:
W naszych bazach danych dotyczących produkcji i przedprodukcji znajdują się tysiące kluczy. Od czasu do czasu musimy usunąć niektóre klucze (przez jakąś maskę), zmodyfikować według niektórych kryteriów itp. Oczywiście nie ma sposobu, aby to zrobić ręcznie z poziomu interfejsu CLI, zwłaszcza mając sharding (512 logicznych dbs w każdym fizycznym).
W tym celu piszę narzędzie klienta Java, które wykonuje całą tę pracę. W przypadku usuwania kluczy narzędzie może być bardzo proste, istnieje tylko jedna klasa:
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();
}
}
Poniższe polecenie działało dla mnie.
redis-cli -h redis_host_url KEYS "*abcd*" | xargs redis-cli -h redis_host_url DEL
Spring RedisTemplate sam zapewnia tę funkcjonalność. RedissonClient w najnowszej wersji wycofał funkcję „deleteByPattern”.
Set<String> keys = redisTemplate.keys("geotag|*");
redisTemplate.delete(keys);
keys
a delete
metodami.