Still deleting data with keys fuzzy match? This is a bomb ready to explode!

There is no instruction in Redis to delete a specific prefix key in batch, but we often need to delete according to the prefix, so how to do? You might get the following answer after a search

redis-cli --raw keys "ops-coffee-*" | xargs redis-cli del
Copy the code

Directly match all keys in Linux through redis keys command, and then call system command xargs to delete. It seems very perfect, but actually it is a huge risk

Due to the single-threaded service mode of Redis, command keys will block normal business requests. If you match too many keys at a time or encounter a large key in DEL, it will directly cause service unavailability and even cause the risk of Redis downtime

So we should avoid this approach in production. What’s an elegant way to do it? SCAN!

SCAN introduction and use

Redis supports the scan command from version 2.8. The basic usage of the scan command is as follows:

SCAN cursor [MATCH pattern] [COUNT count]
Copy the code

Cursor: Cursor, SCAN command is a cursor based iterators, SCAN every time command is invoked, a new cursor will be returned to the user, the user need to use the new cursor in next iteration as SCAN cursor command parameter, to the continuation of the previous iteration, until the server returns a value of 0 to the users of the cursor, A complete traversal is completed

MATCH: a matching rule, for example, traversing all keys starting with ops-coffee- can be written as ops-coffee-*, and keys containing -coffee- can be written as *-coffee-*

The COUNT: The COUNT option allows the user to tell the iteration command how many elements should be returned from the dataset in each iteration. COUNT is just a hint to the incremental iteration command, and does not represent the actual number returned. For example, if you set COUNT to 2, you may return 3 elements. But the returned element data is positively correlated with the COUNT setting, which defaults to 10

The following is an example of an iteration of the SCAN command:

127.0.0.1:6379> scan 0 MATCH ops-coffee-* 
1) "38"
2)  1) "ops-coffee-25"
    2) "ops-coffee-19"
    3) "ops-coffee-29"
    4) "ops-coffee-10"
    5) "ops-coffee-23"
    6) "ops-coffee-5"
    7) "ops-coffee-14"
    8) "ops-coffee-16"
    9) "ops-coffee-11"
   10) "ops-coffee-15"
   11) "ops-coffee-7"
   12) "ops-coffee-1"
127.0.0.1:6379> scan 38 MATCH ops-coffee-* COUNT 1000
1) "0"
2)  1) "ops-coffee-13"
    2) "ops-coffee-9"
    3) "ops-coffee-21"
    4) "ops-coffee-6"
    5) "ops-coffee-30"
    6) "ops-coffee-20"
    7) "ops-coffee-2"
    8) "ops-coffee-12"
    9) "ops-coffee-28"
   10) "ops-coffee-3"
   11) "ops-coffee-26"
   12) "ops-coffee-4"
   13) "ops-coffee-31"
   14) "ops-coffee-8"
   15) "ops-coffee-22"
   16) "ops-coffee-27"
   17) "ops-coffee-18"
   18) "ops-coffee-24"
   19) "ops-coffee-17"
Copy the code

The SCAN command returns an array of two elements, the first array element is the new cursor for the next iteration, and the second array element is an array containing all the iterated elements

The above example means scanning all keys prefixed with ops-coffee-

The first iteration uses 0 as a cursor to start a new iteration, matches the key prefixed with OPs-coffee -, and returns the cursor value 38 along with the traversed data

The second iteration uses the cursor returned from the first iteration, which returns the value 38 for the first element, and forces the command to scan more elements for this iteration by setting the COUNT option to 1000

On the second call to SCAN, the command returns a cursor 0, indicating that the iteration has ended and the entire data set has been fully traversed

The time complexity of KEYS command is O(n), while SCAN command will decompose the traversal operation into m operations with time complexity of O(1), so as to solve the server blocking caused by traversal of a large amount of data using KEYS command. The following commands can achieve the purpose of elegant deletion:

redis-cli --scan --pattern "ops-coffee-*" | xargs -L 2000 redis-cli del
Copy the code

The xargs -L command represents the number of lines read by XARgs at a time, that is, the number of keys deleted at a time. If too many lines are read at a time, XARgs will report an error

Elegant deletion of several other data structures

There are several other SSCAN, HSCAN, and ZSCAN commands that are similar to Redis data types:

> sscan ops-coffee 0 MATCH v1*
1) "Seven"
2) 1) "v15"
   2) "v13"
   3) "v12"
   4) "v10"
   5) "v14"
   6) "v1"
Copy the code

Different from the SCAN command, these commands need to add a key parameter, such as the above OPs-coffee

For a large set key, ssCAN provides an elegant batch deletion using the following code:

import redis

def del_big_set_key(key_name):
    r = redis.StrictRedis(host='localhost', port=6379)
    
    # count indicates the number of elements removed at a time, 300 at a time
    for key in r.sscan_iter(name=key_name, count=300):
        r.srem(key_name, key)

del_big_set_key('ops-coffee')
Copy the code

For a large hash key, hsCAN gracefully delete it using the following code:

import redis

def del_big_hash_key(key_name):
    r = redis.StrictRedis(host='localhost', port=6379)
    
    # hscan_iter (); # hscan_iter ()
    for key in r.hscan_iter(name=key_name, count=300):
        r.hdel(key_name, key[0])

del_big_hash_key('ops-coffee')
Copy the code

For large ordered sets of the delete is relatively simple, directly according to the zremrangeByrank rank range delete

import redis

def del_big_sort_key(key_name):
    r = redis.StrictRedis(host='localhost', port=6379)

    while r.zcard(key_name) > 0:
        # check if there are elements in the set, if there are, delete the elements ranked 0-99
        r.zremrangebyrank(key_name, 0, 99)

del_big_sort_key('ops-coffee')
Copy the code

If you want to delete a large list, use the llEN method to determine the number of elements, and then ltrim to remove the range of elements

At this point, the elegant deletion of Redis’s five data structures is all realized, and the production environment is preferred to use ~


Related articles recommended reading:

  • Remember a weird troubleshooting experience
  • Nginx has several security-related configurations