A basic Redis primer for beginners

A basic Redis primer for beginners

This post is a basic Redis primer for beginners. I will cover off all the basic CRUD operations to get you started quickly.

Redis has been around for around 11 years now. It has been used extensively throughout the last 10 years as a way to speed up websites. This is a achieved by an in-memory data store which provides blazingly fast response times.

The current version is 6.0.9 and it’s the what I will be using for this post.

Overview

Redis isn’t like a relational database, it does provide abstractions such as databases (name spacing) or schemas. In fact, Redis is schema-less and the data you add into the store will probably change over time.

Terminology

In order to get familiar with Redis, we will need to cover off the terminology it uses to represent it’s data.

Key – A globally unique identifier for a record, think of this as an identity key. It can be up to 512 megabytes in length.

Value – The contents of a key, it can be of various different data types and it’s this data type that determines its maximum length.

Basic Commands

The following section will focus on how to execute some basic commands from the command line (cli).

Inserting data

127.0.0.1:6379> set customer:1000 value-0
OK
127.0.0.1:6379>

In the sample above we created a new key called “customer:1000” with a value of “value-0”.

In order to make the following examples a little more interesting, we will generate some data and bulk load it into Redis using the cli.

def main():
    with open("data.txt", 'w') as fp:
        for k, v in zip(range(0, 100), range(0, 100)):
            fp.write("SET customer:{} value-{}\n".format(1000 + k, v))
    print("done")

if __name__ == "__main__":
    main()

The python script above will generate the 1000 customer records, we can import this data in bulk as follows.

$ cat data.txt | redis-cli --pipe 

We now have 100 records to worth with, this will make the following examples a little more interesting.

Retrieving data

127.0.0.1:6379> get customer:1000
"value-0"
127.0.0.1:6379> 

Similarly, we can retrieve the value of the “customer:1000” key with get command. The return value “value-0” is surrounded in quotes indicating it’s a string.

Searching for data

There are two commands that are using in Redis for searching for data, keys and scan.

Keys

The keys command is useful for debugging, but it should not be used in production due to it’s blocking nature (more on this later).

The syntax for the keys command is, keys <pattern> where pattern is a wildcard string that will be used to match relevant keys.

For example, if we want to retrieve all the keys that match customer:100* we would do the following.

127.0.0.1:6379> keys customer:100*
 1) "customer:1009"
 2) "customer:1001"
 3) "customer:1008"
 4) "customer:1002"
 5) "customer:1005"
 6) "customer:1007"
 7) "customer:1003"
 8) "customer:1006"
 9) "customer:1000"
10) "customer:1004"
127.0.0.1:6379>

An important thing to note here is that the keys command is a blocking operation, meaning that while its searching no other operations can happen until the search is complete (inserts, updates or deletes).

You might not thing of this as being an issue initially, but since Redis is essentially a flat store you are effectively searching for everything when using the keys command. When you have millions of records in the store and you’re also using the store to provide caching data to web clients you want to avoid blocking operations as much as possible.

Scan

An alternative method to search for data is the scan command. It is similar to keys but with one notable difference, it works with a cursor.

127.0.0.1:6379> scan 0 MATCH customer:100*
1) "40"
2) (empty list or set)
127.0.0.1:6379>

We don’t get a very interesting result in this instance, this is because the data we inserted was not ordered and so the records are scattered.

In the returned output the “1)” indicates the next cursor position (in our case the first record that matches our wildcard). The second part of the output “2)” is saying empty because none of the records in the current cursor (from record 0 – 39) met our criteria.

To get some more interesting output, lets update the cursor to “40”.

127.0.0.1:6379> scan 40 MATCH customer:100*
1) "92"
2) 1) "customer:1003"
   2) "customer:1001"
   3) "customer:1005"
127.0.0.1:6379>

As per the previous example, the “1)” is our next cursor location. In this example we found 3 records that matched our criteria. You would need to repeat this process until the cursor returned 0, this would indicate you were done.

While this process works, it can seem a little cumbersome so you can add some additional parameters to the scan command to return more records in a single scan.

127.0.0.1:6379> scan 0 MATCH customer:100* COUNT 101
1) "0"
2)  1) "customer:1003"
    2) "customer:1001"
    3) "customer:1005"
    4) "customer:1009"
    5) "customer:1002"
    6) "customer:1000"
    7) "customer:1007"
    8) "customer:1006"
    9) "customer:1004"
   10) "customer:1008"
127.0.0.1:6379>

Rather than making multiple calls to the scan command, the count is increased to 101 allowing more records to be returned in one hit.

This can impact performance so use the count with care.

Deleting records

To delete records from Redis, there are two commands del and unlink.

The difference between the two is that DEL is a blocking operation, all other operations will wait till del completes. Both the key and the memory allocated for the value are cleared by this command.

Unlink on the other hand is asynchronous, the record is unlinked (as the name suggest) which means the key is no longer available and the memory is later freed by an asynchronous process running in the background.

Let’s add some new keys and then delete them using the two different methods.

127.0.0.1:6379> set something some-value
OK
127.0.0.1:6379> set something2 some-new-value
OK
127.0.0.1:6379> scan 0 MATCH something* COUNT 103
1) "0"
2) 1) "something2"
   2) "something"
127.0.0.1:6379> del something something2
(integer) 2
127.0.0.1:6379> scan 0 MATCH something* COUNT 103
1) "0"
2) (empty list or set)

In this example, I create two new keys “something” and “something2”. Next we delete both of the keys, the del command returns a “2” which is the count of keys that were deleted.

We then validate that the keys don’t exist anymore using the scan command.

127.0.0.1:6379> set something some-value
OK
127.0.0.1:6379> set something2 some-new-value
OK
127.0.0.1:6379> unlink something something2
(integer) 2
127.0.0.1:6379> scan 0 MATCH something* COUNT 103
1) "0"
2) (empty list or set)

The syntax for the unlink command is exactly the same, remembering the key difference here is that unlink is not a blocking operation. Cleanup of the key and associated memory is done by a separate thread.

Wrapping up

This covers the essential aspects when starting off with Redis. There is a lot more to uncover with Redis which I hope to expand on in future posts.

Leave a Reply