У моїй DB Redis у мене є ряд prefix:<numeric_id>
хешей.
Іноді хочеться очистити їх усі атомно. Як це зробити, не використовуючи механізму розподіленого блокування?
У моїй DB Redis у мене є ряд 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_*]
Попередження : Як йдеться в документі Redis , через те, що питання продуктивності
keys
команду не слід використовувати для регулярних операцій у виробництві, ця команда призначена для налагодження та спеціальних операцій. читати більше
Дивіться документацію 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:*
має бути фундаментальною операцією: /
EVAL "return redis.call('del', 'defaultKey', unpack(redis.call('keys', ARGV[1])))" 0 prefix:*
Виконати в bash:
redis-cli KEYS "prefix:*" | xargs redis-cli DEL
ОНОВЛЕННЯ
Гаразд, я зрозумів. Як щодо цього способу: зберігайте поточний додатковий додатковий префікс і додайте його до всіх своїх ключів. Наприклад:
У вас є такі значення:
prefix_prefix_actuall = 2
prefix:2:1 = 4
prefix:2:2 = 10
Коли вам потрібно очистити дані, спочатку ви зміните prefix_actuall (наприклад, встановіть prefix_prefix_actuall = 3), тому ваша програма запише нові дані в префікс ключів: 3: 1 та префікс: 3: 2. Тоді ви можете сміливо брати старі значення з префікса: 2: 1 та префікса: 2: 2 та очистити старі ключі.
redis-cli KEYS "prefix:*" | xargs --delim='\n' redis-cli DEL
redis-cli -n 3 KEYS "prefix:*" | xargs redis-cli -n 3 DEL
Ось повністю працююча та атомна версія видалення підстановок, реалізована в Луа. Вона запуститься набагато швидше, ніж версія 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% йде йому.
EDIT: За коментарем Кікіто нижче, якщо у вас є більше клавіш для видалення, ніж вільна пам'ять на вашому сервері Redis, ви зіткнетеся з помилкою "занадто багато елементів для розпакування" . У цьому випадку зробіть:
for _,k in ipairs(redis.call('keys', ARGV[1])) do
redis.call('del', k)
end
Як запропонував Кікіто.
for _,k in ipairs(redis.call('keys', KEYS[1])) do redis.call('del', k) end
unpack
перетворює таблицю у "список незалежних змінних" (інші мови називають це explode
), але максимальне число не залежить від пам'яті системи; він фіксується в луа через LUAI_MAXSTACK
постійну. У Lua 5.1 та LuaJIT це 8000, а в Lua 5.2 - 100000. Опція для циклу рекомендується IMO.
EVAL
оскільки не зазначає заздалегідь ключі, над якими воно буде працювати. Він повинен працювати в одному екземплярі, але не сподівайтеся, що він буде працювати з класом Redis.
Відмова: наступне рішення не забезпечує атомності.
Починаючи з 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 як команду, яку слід використовувати лише у виробничих середовищах з особливою обережністю. Це може призвести до погіршення продуктивності, коли воно виконується на великих базах даних. Ця команда призначена для налагодження та спеціальних операцій, таких як зміна макета простору ключів. Не використовуйте KEYS у звичайному коді програми. Якщо ви шукаєте спосіб знайти ключі в підмножині простору клавіш, подумайте про використання наборів.
ОНОВЛЕННЯ: один вкладиш для того ж базового ефекту -
$ redis-cli --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli DEL
-n 1
до кожного redis-cli
виклику:redis-cli -n 1 --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli -n 1 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
Я використовую команду нижче в редакції 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);
}
}
Дякую :)
@ mcdizle не працює, він працює лише для одного запису.
Цей працює для всіх клавіш з однаковим префіксом
EVAL "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" 0 prefix*
Примітка. Ви повинні замінити "префікс" вашим ключовим префіксом ...
Ви також можете скористатися цією командою для видалення ключів: -
Припустимо, у вашому redis є багато типів клавіш,
Напр. " 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 " у наведеній вище команді.
Якщо ви хочете видалити все в Redis , виконайте ці команди,
З Redis-cli:
Наприклад: - у вашій оболонці:
redis-cli flushall
redis-cli flushdb
redis-cli del
не атомний.
@ відповідь itamar чудова, але аналіз відповіді не спрацював для мене, esp. у випадку, коли в даному скануванні не знайдено жодних ключів. Можливо, більш просте рішення безпосередньо з консолі:
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 не має такої функції.
Що я зробив: зберігайте дані сеансу в хеші . Я просто створити хеш з хеш - ідентифікатор , session:sessionid
а потім я штовхаю key-x
, key-y
, key-z
в тому , що хеш (порядок не має для мене) , і якщо мені не потрібно , що хеш більше просто зробити , DEL session:sessionid
і всі дані , пов'язані з цим хеш - ідентифікатор пішов. DEL
є атомним і доступ до даних / запису даних до хешу є O (1).
Я думаю, що може допомогти вам МУЛЬТИ / ВИДАЛЕННЯ / ВІДКРИТТЯ . Хоча це не 100% еквівалент транзакцій , ви можете бути в змозі ізолювати делетів від інших оновлень.
FYI.
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
Запустіть у баш-підказці
$ ./clear-redis-key.sh key_head_pattern
Інші відповіді можуть не працювати, якщо ваш ключ містить спеціальні символи - Guide$CLASSMETADATA][1]
наприклад. Обгортання кожної клавіші в лапки забезпечить їх належне видалення:
redis-cli --scan --pattern sf_* | awk '{print $1}' | sed "s/^/'/;s/$/'/" | xargs redis-cli del
Версія, що використовує SCAN, а не KEYS (як рекомендується для виробничих серверів), --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
Це не пряма відповідь на питання, але оскільки я потрапив сюди під час пошуку власних відповідей, я поділюсь цим тут.
Якщо у вас є десятки або сотні мільйонів ключів, з якими вам доведеться відповідати, відповіді, наведені тут, призведуть до того, що Redis не реагує на значну кількість часу (хвилин?) І може призвести до збою через споживання пам’яті (будьте впевнені, збереження фону буде удар в середині вашої операції).
Наступний підхід, безперечно, некрасивий, але я не знайшов кращого. Атомність тут не підлягає сумніву, в цьому випадку головна мета - тримати Redis вгорі і реагувати на 100% часу. Він буде чудово працювати, якщо у вас є всі ключі в одній із баз даних, і вам не потрібно відповідати жодному шаблону, але ви не можете використовувати http://redis.io/commands/FLUSHDB через його блокування.
Ідея проста: написати сценарій, який працює в циклі і використовує операцію O (1), наприклад http://redis.io/commands/SCAN або http://redis.io/commands/RANDOMKEY, щоб отримати ключі, перевірити, чи вони відповідність шаблону (якщо вам це потрібно) і http://redis.io/commands/DEL їх по черзі.
Якщо є кращий спосіб зробити це, будь ласка, повідомте мене, я оновлю відповідь.
Приклад реалізації з randomkey в Ruby, як завдання граблі, що не блокує заміну чогось типу 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
Це просто реалізується через "Видалити гілку" функціональність у FastoRedis , просто виберіть гілку, яку ви хочете видалити.
Я спробував більшість згаданих вище методів, але вони не спрацювали для мене, після деяких пошуків я виявив ці моменти:
-n [number]
del
але якщо є тисячі або мільйони ключів, краще використовувати, unlink
оскільки від’єднання не блокується, а дель блокується, для отримання додаткової інформації відвідайте цю сторінку від’єднайте від дельkeys
є як del і блокуєтому я використовував цей код для видалення ключів за шаблоном:
redis-cli -n 2 --scan --pattern '[your pattern]' | xargs redis-cli -n 2 unlink
Оголошення тепер, ви можете використовувати клієнт Redis і виконувати спочатку SCAN (підтримує узгодження шаблону), а потім DEL кожну клавішу окремо.
Тим НЕ менше, існує проблема на офіційному Redis GitHub створити скоромовка-сопрягая-дель тут , йде показати йому деяку любов , якщо ви знайдете його корисним!
Я підтримую всі відповіді, пов'язані з тим, щоб мати якийсь інструмент або виконати вираз Lua.
Ще один варіант з мого боку:
У наших виробничих та передвиробничих базах даних є тисячі ключів. Час від часу нам потрібно видалити деякі клавіші (за якоюсь маскою), змінити за якимись критеріями і т.д.
Для цього я пишу інструмент для клієнта 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
Весна RedisTemplate сама забезпечує функціональність. В останній версії RedissonClient застаріла функція "deleteByPattern".
Set<String> keys = redisTemplate.keys("geotag|*");
redisTemplate.delete(keys);
keys
та delete
виклики методів.