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.

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. Commented Sep 13, 2018 at 9:16

8 Answers 8

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('*'))
);
Sign up to request clarification or add additional context in comments.

1 Comment

A little improvement to ensure removing the string (prefix) starting to the left: preg_replace( "/^${prefix}/", '', $key ); in favor of str_replace.
4

No - Redis' DELlete does not accept wildcards, you have to name the keys explicitly. See here for possible directions: https://stackoverflow.com/a/23399125/3160475

2 Comments

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.
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)
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);
        }
    }

Comments

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);

3 Comments

A good answer has a better explanation How do I write a good answer?
What's $prefix = $this->client->getOptions($this->OPT_PREFIX); here in this code snippet?
This is a much better solution than iterating over each key. Upvoted.
1
$bash = 'redis-cli --scan --pattern "' . $path . '*" | xargs -L 1000 redis-cli DEL';

$res = @shell_exec($bash);

3 Comments

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.
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.
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!
0

I just use

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

It's works fine for me.

4 Comments

Doesn't this just delete everything since you're matching every key?
this is a dangerous answer
Yeah, but you can use mask like some-namespace/*
Redis::delete is deprecated now. It possible use Redis::del ?
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

Comments

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);

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.