Redis: the basic data structures
Last updated
Was this helpful?
Last updated
Was this helpful?
The first chapter is for you get an overview of the core Redis data structures - STRING
, LIST
, SET
, HASH
and SORTED SET
. A practical application is used to explain the concepts
This is by no means a complete list of all the Redis data structures
This application is loosely inspired by . It's features are as follows
Basic
User registration (using a unique name)
Submit a news item
Get a news item
View all news items sorted in descending order by upvotes
Comments
Post a comment on a news item
View all comments for a news item
Upvote
Upvote a news item
Get number of upvotes for a news item
All the functionality (registration, news items submission etc.) is exposed via a REST API
The following components have been used
Here is a quick overview of the application specific entities and the Redis data structures they map to
users
- a SET
which stores all users (just the user names to keep it simple)
news:[id]
- news details are stored in a HASH
(id
is the news ID)
news-id-counter
- a STRING
data type
news:[id]:comments
- a LIST
to store comments associated with a news item (id
is the news ID)
comments-id-counter
- STRING
key which acts as a counter for generating comment ID
news-upvotes
- SORTED SET
with news ID as key and number of upvotes as value
Its time to dive into the nitty gritty. This section focuses on the code and the details of how to test the application is covered in the next one
The code package structure is as follows
The api
directory contains logic for the REST API handlers for news items, comments etc. models
contains a single file models.go
which has the Go struct
s representations for the JSON data which will be exchanged between the client and the API. Finally, main.go
serves as the entry point
Let's move on to the details
The new user registration process is implemented using a REST endpoint which accepts the user name as an input (via HTTP POST
). It adds to the users
set using SADD
(e.g. SADD users abhirockzz
)
A Set stores unique elements only - same applies to SET
s in Redis
SADD
returns 0 in case the element (the user name in this case) is not added to the set - this allows registration request for a duplicate user to be rejected
In the above code snippet, notice the following lines
connection to Redis is established before invoking any of the HTTP handler (in response to a API request)
the request is aborted in case the connection is not successful (there is no point proceeding)
the Redis connection is closed after the request
Redis server endpoint is extracted from the environment variable REDIS_SERVER
. On successful connection, the object is set in a (Gin) context specific variable called redis
- the HTTP request handler(s) (as seen above), Get
s the client object from the context variable
The REST API for news item submission accepts the news item details via HTTP POST
and it makes of the HASH
, SORTED SET
and STRING
data structures
the API simply returns the (unique) ID for the submitted news item (as the response)
the users
SET is first checked to see if a registered user has submitted the request
The STRING
data type (news-id-counter
) is used as a unique (news) ID generator. The interesting part is that it is integer aware and can be incremented in an atomic manner using INCR
command (e.g. INCR news-id-counter
) which makes it ideal for implementing counters
Once the unique news ID is generated, the news item details are stored in a HASH
(using HMSET
command). Think of HASH
as a Map
-like data structure which is a great fit for storing objects. In this case, we're storing the url
, title
and submittedBy
attributes for a news item in HASH
whose naming convention is news:[id]
e.g. news:42
. Here is a what the an example command look like - HMSET news:42 title "Why did I spend 1.5 months creating a Gameboy emulator?" url "http://blog.rekawek.eu/2017/02/09/coffee-gb/" submittedBy "tosh"
Finally, we add the news item ID and the number of upvotes (zero to begin with) to a SORTED SET
(called news-upvotes
). This data structure is like a SET
(unique elements only) but with an additional property that the elements/members can be associated with a score which opens up a bunch of interesting options (discussed in upcoming section)
Implementing a REST API for comments on a post is as as simple as using the LPUSH
command to add it to a Redis LIST
whose naming pattern is news:[id]:comments
(e.g. news:42:comments
). LPUSH
inserts the comment to the beginning of the list such that the older comments are pushed further in. This way, when fetched (details below), the latest (newer) comments will show up first
The REST API to get all the comments associated with a specific news item makes use of the LRANGE
command which returns a subset of the list (given start
and stop
index). In this case we want all the contents, hence our start
index is 0 and the stop
index is the length of the list itself e.g. LRANGE news:42:comments 0 10
. We find out the length of the list using LLEN
command
Upvoting a news item is achieved by incrementing its score in the SORTED SET
(news-upvotes
) using the ZINCRBY
command
Upvotes for a news item are represented as a score in a Redis SORTED SET
which means we can just use the ZSCORE
on SORTED SET
(news-upvotes
) to get the number of upvotes i.e. ZSCORE news-upvotes 42
(where 42
is the news ID)
The REST endpoint for news item details accepts a news ID (as a Path Parameter
of a HTTP GET
request) and returns information about the news item which includes its URL, title, who it was submitted by along with the number of upvotes and comments
The real workhorse is the getNewsItemDetails
function. It fetches the details from the the Redis HASH
specific to the news item (e.g. news:42
where 42
is the news ID) and creates a model.NewsItem
instance which is the representation of the details to be returned
This is an extension of the above functionality which returns all the news items. Important thing to note is that the returned list of news items are sorted in descending order of the number of upvotes they have received i.e. the news items with the maximum upvotes shows up first
This is where the SORTED SET
shines. Since we had stored the upvotes for a news item as a score in the news-upvotes
Redis SORTED SET
, getting back sorted list of these news items (in descending order) is achieved using the ZREVRANGE
command which accepts the start
and stop
index (in this case it's the length of the SORTED SET
which is found using ZCARD
)
As earlier, the details of the each of the news item is extracted from the respective HASH
and the entire result is sent back to client as a slice of model.NewsItem
s
The docker-compose.yml
defines the redis
and news-sharing-app
services
REDIS_SERVER
environment variable is used by the application code andPORT
variable is used by Gin
Get the project - git clone https://github.com/abhirockzz/practical-redis.git
cd practical-redis/news-sharing-service/
Invoke the startup script ./run.sh
(this in turn invokes docker-compose
commands)
Stop the application by invoking ./stop.sh
from another terminal
Replace
DOCKER_IP
with the IP address of your Docker instance which you can obtain usingdocker-machine ip
. The port (8080
in this case) is the specified indocker-compose.yml
Create a few users
-d
accepts the payload - in this case, the user name
You should see HTTP 204
status as the response
Submit a couple of news items
If successful, the news ID is returned as a payload in the HTTP
200
response
and another one
Get details for a specific news item
For news item 1
- curl -X GET http://DOCKER_IP:8080/news/1
For news item 2
- curl -X GET http://DOCKER_IP:8080/news/2
A JSON payload representing the news item details is returned
Comment on news items
the
1
in the URLhttp://DOCKER_IP:8080/news/1/comments
is the news ID
Let's post a comment for news item 2
as well
You should see HTTP 204
status as the response
Get comments for a specific news items
For news item 1
For news item 2
This API returns a JSON response similar to below
Upvote news items
Execution of this command is equal to one upvote. So, repeat it for as many upvotes you like and please note that
1
is the news ID
Let's give 3 upvotes to news ID 1
(repeat this thrice)
.. and 2 upvotes for news ID 2
(repeat twice)
You should see HTTP 204
status as the response
Get all news items
The JSON representation of multiple news items is returned
is the HTTP web framework for REST API
as the Redis client
for Redis
to orchestrate all the application components
Source code is available on
client
is nothing but a handle to the redis.Client
object - but was the connection established in the first place ? A was used to achieve this. As a result
The redis
service is based on the and the news-sharing-app
is based on a custom Dockerfile
A is used wherein a different image is used for building our Go app and a different image is used as the base for running it
golang
is used for the build process which results in a single binary (for linux)
since we have the binary with all dependencies packed in, all we need is the minimal image for running it and thus we use the lightweight scratch
for this purpose
Install , or any other HTTP tool to interact with the REST endpoints of the service