Skip to content

Instantly share code, notes, and snippets.

@justincastilla
Last active October 28, 2021 15:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justincastilla/94a0f0f6450128c7f837340adde6bc4d to your computer and use it in GitHub Desktop.
Save justincastilla/94a0f0f6450128c7f837340adde6bc4d to your computer and use it in GitHub Desktop.
RU101[LIVE] Week 3 Cheat Sheet

RU101[LIVE] Week 3 Cheat Sheet

Table of Contents

Storing objects in Redis with Hashes

For our Hash segment we'll be using the Redis team's pets as examples.

HSET: Sets an individual field/value pair wtihin a hash.

One of Justin's pet chickens is laying eggs! Update the specific hash to contain the new field value pair laying_eggs: True

>HSET user:justin:pet:josie laying_eggs True
(integer) 1

Remember that Redis Hashes - like Redis - are schemaless. We are not bound to maintaining similar field/value pairs among our data types.

HGETALL: Returns all of the field/value pairs of a specified key.

Lets check out the entry for Steve's dog, Honey:

>HGETALL user:steve:pet:honey
 1) "name"
 2) "Honey"
 3) "age"
 4) "5"
 5) "weight"
 6) "71"
 7) "number_of_legs"
 8) "4"
 9) "species"
10) "dog"
11) "breed"
12) "Greyhound"
13) "favourite_toy"
14) "Cucumber Squeaker"

HGETALL is considered a dangerous command as returning all of the field/value pairs of a hash could potentially block the Redis instance if there is a high quantity of data to return. HGET and HSCAN is preferred.

HGET: Returns the value associated with a specified field in the hash stored at a specified key.

Find the favourite_toy field of Rachel's cat named Sputnik:

>HGET user:rachel:pet:sputnik
"Moppy Ball"

HSCAN: Incrementally iterates over the field/value pairs of a Hash

HSCAN will iterate through a specified hash and return the field/value pairs it traverses as well as a cursor position of where it stopped before terminating. You would then call the HSCAN command again with this returned cursor; this then continues scanning where it previously left off. Note that because Redis is memory optimized, smaller data types and aggregations may return the entire data type it is scanning. It is only when a data type reaches a larger threshold (and thus is converted into a hash table) will you need to use a cursor.

HINCRBY: Increments the number stored at a specified field in the hash stored at a sepcified key by an increment value.

Increment the weight of KyleO's dog Denali by 3 pounds:

>HINCRBY user:kyleO:pet:denali weight 3
(integer) 45

Note that the return value is the updated value of the field weight. To decrement a field, use negative numbers

Uploading an image into a Hash Field

Lets store an image in one of our hashes. To accomplish this, we'll store a base64 encoded copy of an image into an image field as a string.

From the command-line, not the redis-cli:

$ redis-cli -x HSET user:simon:pet:spot image < base64 spot.JPG

To fetch and decode our image, we'll again call a redis-cli command from the regular command-line:

$ redis-cli HGET user:simon:pet:spot image > spot_image.base64
$ base64 -d spot_image.base64 > spot_image.jpg

Now lets open spot_image.jpg and see Spot at play:

$ open spot_image.jpg

Spot, the Piranha






MULTI, EXEC, DISCARD and WATCH are the foundation of the transaction structure in Redis. They allow the execution of a group of commands in a single step, with two important guarantees:

  • All the commands in a transaction are serialized and executed sequentially.

  • Either all of the commands or none are processed, so a Redis transaction is also atomic.

This prevents 'in-between' conditions when values are not what they should be when being evaluated by other clients. Lets try an example with our pet Hashes from before.

What happens when two of the pets swap toys, such as when Rachel's cat Sputnik and Justin's chicken Josie swap a Moppy Ball and a Cherry Tomato? Lets evaluate the necessary commands and states of each step:


1. Here are the starting values of the two relevant Hash objects:
key field value
user:rachel:pet:sputnik favourite_toy "Moppy Ball"
user:justin:pet:josie favourite_toy "Cherry Tomato"


2. Update Sputnik's toy to a "Cherry Tomato":
>HSET user:rachel:pet:sputnik favourite_toy "Cherry Tomato"

Resulting values of both keys:

key field value
user:rachel:pet:sputnik favourite_toy "Cherry Tomato"
user:justin:pet:josie favourite_toy "Cherry Tomato"

Notice that at this point, both keys have the value "Cherry Tomato". This isn't quite right, as there is only one toy to swap from Josie the chicken.

Both Sputnik and Josie can't have the same toy at the same time. This applies similarly to our data - we don't want anyone accessing inaccurate data while in the middle of our multiple-command transaction.



3. Set Josie's favorite toy value to `"Moppy Ball"`:
>HSET user:justin:pet:josie favourite_toy "Moppy Ball"

Resulting values of both keys:

key field value
user:rachel:pet:sputnik favourite_toy "Cherry Tomato"
user:justin:pet:josie favourite_toy "Moppy Ball"


The MULTI and EXEC command structure ensure that all commands within will be executed one after another, without any interruption by any other client commands. This ensures that during the moment of duplicate values, there will be no accidental access.

Here's an example using our toy-swap scenario:

> MULTI
OK
TX)> HSET user:rachel:pet:sputnik favourite_toy "Cherry Tomato"
QUEUED
TX)> HSET user:justin:pet:josie favourite_toy "Moppy Ball"
QUEUED
TX)> EXEC
1) (integer) 0
2) (integer) 0

After the MULTI command is called, an OK is returned to verify that all following commands will be added to a multi-command transaction. You can see this after every subsequent command with the return value of QUEUED. To end the transaction, you enter the EXEC command, which will execute every command in succession starting directly after MULTI.

This particular transaction will swap the toy values sequentially with no interruption from any other client request to the Redis instance. Also with transactions, both key's values will be successfully swapped or not at all; that is a guarantee from the transaction structure. If there is an I/O error or server crash, the entire transaction will be cancelled.

WATCH is used to provide a check-and-set (CAS) behavior to Redis transactions.

WATCHed keys are monitored in order to detect changes against them. If at least one watched key is modified before the EXEC command, the whole transaction aborts, and EXEC returns a Null reply to notify that the transaction failed.

As an example, lets have a separate key for each pet user:nava:pet:gingit:times_fed_today that is normally set to 0. When the pet has been fed a meal, the value is incremented. What happens if two clients attempt to increment the field times_fed_today at the same time, when the value is 0? The pet may be quite excited to receive two meals sequentially, but this is a race condition and isn't desirable.

When we WATCH a key before a transaction, the transaction will abort IF the WATCHed key is altered.

Here is an example using WATCH:

> WATCH user:nava:pet:gingit:times_fed_today
OK
> MULTI
OK
(TX)> INCR user:nava:pet:gingit:times_fed_today
QUEUED
(TX)> EXEC
1) (integer) 1

Here is another example of WATCHing the key when another client updates the key:

> WATCH user:nava:pet:gingit:times_fed_today
OK
> MULTI
OK
(TX)> incr user:nava:pet:gingit:times_fed_today
QUEUED
(TX)> exec
(nil)

Notice that a (nil) is returned instead of the new value of the key. This indicates that the key has been altered since the WATCH command was initially called, and will allow you to take the necessary steps to address the change in the value.

Note that the WATCH command does is not currently designed for use with Hash fields. By storing a single value associated with the key usr:nava:pet:gingit in a String type key, we can use the WATCH command

DISCARD aborts all commands that are queued in a current transaction and unwatches all keys that WATCH was called upon.



From the documentation linked above:

SUBSCRIBE, UNSUBSCRIBE and PUBLISH implement the Publish/Subscribe messaging paradigm where (citing Wikipedia) senders (publishers) are not programmed to send their messages to specific receivers (subscribers). Rather, published messages are characterized into channels, without knowledge of what (if any) subscribers there may be.

This structure allows Redis to PUBLISH a message without any intended recipient to a specified channel from any client. An client that SUBSCRIBEs to a specified channel will then receive a message sent specifically to that channel.

Imagine we have an aircraft just touched down at an airoport and will be arriving at a specific gate. This event should notify the following systems and departments within the airport:

  • Jet bridge team
  • Fuel tanker team to refill the Jet
  • Baggage claim carousel to display the flight number

These four services will all set off different activities within the airport when a specific flight lands. They will SUBSCRIBE to any messages from the flight, which PUBLISHes messages as they occur.

Here is one possible scenario where three different clients are subscribed to a specific channel arrivals and will receive any message published:

Jet Bridge client:

SUBSCRIBE arrivals

Fuel Tanker client:

SUBSCRIBE arrivals

Baggage client:

SUBSCRIBE arrivals

Arrival client:

PUBLISH arrivals "UA001:43:4"

What we see with this:

  • One publisher sends a message to to a channel
  • Multiple subscribers will receive a message to a channel they subscribe to.
  • This has many uses with Redis, as it is fast and requires no extra keys or data.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment