How to delete multiple redis keys with the same pattern in PHP using phpredis?
Asked Answered
N

8

11

By using phpredis, I have saved some data in pagination like this:

   review/itemA/1
   review/itemA/2 

where 1 and 2 are page numbers. I read in the document that you can use wildcards to retrieve multiple keys.

$allKeys = $redis->keys('*');   // all keys will match this.
$keyWithUserPrefix = $redis->keys('user*');

But can I delete all the old keys using wildcards as well when someone has posted a new review? Can I do something like:

$redis->delete('review/itemA/*'); // or  $redis->delete('review/itemA*')

It didn't work however.

Naara answered 30/8, 2015 at 10:20 Comment(1)
Never use keys command on production environment as it will block the whole DB until the searched key is found. Instead you can use Scan to search specific key for production env.Berga
D
5

When using phpredis, you can get the prefix (which phpredis automatically prepends everywhere) and delete a pattern of keys that way :

<?php
...

$prefix = $redisClient->getOption(Redis::OPT_PREFIX);
$redisClient->delete(array_map(
    function ($key) use ($prefix) {
        return str_replace($prefix, '', $key);
    }, $redisClient->keys('*'))
);
Darlenadarlene answered 1/9, 2017 at 13:27 Comment(1)
A little improvement to ensure removing the string (prefix) starting to the left: preg_replace( "/^${prefix}/", '', $key ); in favor of str_replace.Immediacy
S
4

No - Redis' DELlete does not accept wildcards, you have to name the keys explicitly. See here for possible directions: https://mcmap.net/q/53904/-how-to-atomically-delete-keys-matching-a-pattern-using-redis

Strapper answered 30/8, 2015 at 11:26 Comment(2)
Thanks. I ended up using $allKeys = $redis->keys('itemA*'); and looped over the results to delete them one by one. Not sure if that is the best way of doing it.Naara
Definitely one of the worst ways ;) KEYS is an evil command that you should, as a rule, refrain from using (unless thou be brave, fool or both)Strapper
M
2

With Predis, I do it like this:

    public function delete($key) {
        $keys = $this->client->keys($key);
        foreach ($keys as $key) {
            $this->client->del($key);
        }
    }
Mainland answered 22/3, 2018 at 13:5 Comment(0)
S
2

Predis (->del) allows to pass a keys array too.
It works here and is faster than the del inside the foreach.

$prefix = $this->client->getOptions($this->OPT_PREFIX);
$keys = $this->client->keys("$key*");
if ($keys) $this->client->del($keys);
Springe answered 5/9, 2018 at 8:57 Comment(3)
A good answer has a better explanation How do I write a good answer?Dispirited
What's $prefix = $this->client->getOptions($this->OPT_PREFIX); here in this code snippet?Japha
This is a much better solution than iterating over each key. Upvoted.Hayne
V
1
$bash = 'redis-cli --scan --pattern "' . $path . '*" | xargs -L 1000 redis-cli DEL';

$res = @shell_exec($bash);
Valladares answered 16/12, 2016 at 9:2 Comment(3)
Please refrain from posting code-only answers as they do not help the community to improve by learning how to solve a problem. Try to explain why your solution would help with the problem with the code instead.Beaufort
It's better to add more context/explanation around code (as opposed to just having a code-only answer) as that makes the answer more useful.Trinity
While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please include an explanation for your code, as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Flaggers / reviewers: For code-only answers such as this one, downvote, don't delete!Harilda
A
0

I just use

$redis->delete($redis->keys('*'));

It's works fine for me.

Airport answered 17/7, 2017 at 9:55 Comment(4)
Doesn't this just delete everything since you're matching every key?Leenaleeper
this is a dangerous answerScandinavian
Yeah, but you can use mask like some-namespace/*Airport
Redis::delete is deprecated now. It possible use Redis::del ?Studious
S
0

There is no wildcard for delete function. workaround as follows,

// returns total number of keys deleted
function delete($key) {
    if (empty($key)) {  // empty key can delete all
      return false;
    }
    $keys = $redis->keys("$key*");  // keys() function returns array of key strings. `*` wild card pattern can be changed as per need
    if(!$keys) {
        return 0;
    }
    $prefix = $redis->getOption(\Redis::OPT_PREFIX); // keys already have prefix, so we clear until delete finish.
    $redis->setOption(\Redis::OPT_PREFIX, '');
    $count = $redis->del($keys);  // del() function delete array of keys and returns number of keys deleted.
    $redis->setOption(\Redis::OPT_PREFIX, $prefix);

    return $count;
}

Note: As @Akash Gangrade commented, keys() not advised to use due to performance. you can consider Tags based cache invalidation like https://symfony.com/doc/current/components/cache/cache_invalidation.html#tag-aware-adapters

Sightly answered 20/1, 2021 at 16:52 Comment(0)
C
0

Using scan and unlink is best practise as they are non blocking commands unlike keys and delete

# The keys set in redis
/*
1) "review/itemA/5"
2) "review/itemA/2"
3) "review/itemA/3"
4) "review/itemA/1"
5) "review/itemA/4"
*/

/**
 * vars used for scan
 */
$i = null;
$result = [];

/**
 * scan redis using desired pattern
 */
while ($result != 0) {
    $result = $redis->scan($i, 'review/itemA/*');
    if (!empty($result)) {
        $all_keys[] = $result;
    }
}

# Use call_user_func_array to flatten multidimensional array into indexed array
## Scan may return duplicate keys, so use array_unique
$unlink_keys = array_unique(call_user_func_array('array_merge', $all_keys));

# As of Redis 4.0 use unlink instead of del to stop blocking
$redis->unlink($unlink_keys);
Chiang answered 23/7, 2022 at 17:32 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Frulla

© 2022 - 2024 — McMap. All rights reserved.