Redis local setup, data types and CLI commands.

In today’s fast world, everybody wants to use applications with a response time as little as possible. Redis is mainly used as a cache and it helps developers achieve high performance in their applications. This tutorial will show how to easily set up a Redis instance using Docker, demonstrate main datatypes, and examples of CLI commands.

Ivan Polovyi
Javarevisited
12 min readJan 18, 2023

--

The setup

The easiest way to create an instance of Redis locally is by using Docker and Docker Compose. To be able to create the docker container with Redis we have to create a definition file in yaml format called a docker-compose file. But I will present this file a little bit later. Before I have to talk about two additional files for the setup.

The first is a Redis config file. This file is used to store all configurations used by the Redis instance. By default, the instance has its own configuration file, but if you need to customize the configuration you will have to create your own file and point the instance to this file.

The config file has a lot of configurations and each configuration has a comment explaining its use. Usually, each version of Redis has its own file. The full list of config files can be found here. In this tutorial, I will use the latest version of Redis, at the moment it is version 7.0 and the full config file can be found here.

As I've said before it has tons of configurations and explanations for them. So the file is huge and it’s easy to get lost in it. For me, it is quite confusing to use the file “as is”. So I removed all comments and unused configs from the file and kept only the needed ones. If you need the additional configs and/or explanation you can always refer to the original file. So the file used in the setup looks like the below:

################################## NETWORK #####################################
bind 127.0.0.1 -::1
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300

################################# GENERAL #####################################
daemonize no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
always-show-logo no
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"

################################ SNAPSHOTTING ################################
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir ./

################################# REPLICATION #################################
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-diskless-sync-max-replicas 0
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100

################################## SECURITY ###################################
acllog-max-len 128
aclfile /etc/redis/users.acl

############################# LAZY FREEING ####################################
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
lazyfree-lazy-user-flush no

############################ KERNEL OOM CONTROL ##############################
oom-score-adj no
oom-score-adj-values 0 200 800

#################### KERNEL transparent hugepage CONTROL ######################
disable-thp yes

############################## APPEND ONLY MODE ###############################
appendonly no
appendfilename "appendonly.aof"
appenddirname "appendonlydir"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no

################################## SLOW LOG ###################################
slowlog-log-slower-than 10000
slowlog-max-len 128

################################ LATENCY MONITOR ##############################
latency-monitor-threshold 0

############################# EVENT NOTIFICATION ##############################
notify-keyspace-events ""

############################### ADVANCED CONFIG ###############################
hash-max-listpack-entries 512
hash-max-listpack-value 64
list-max-listpack-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-listpack-entries 128
zset-max-listpack-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes

########################### ACTIVE DEFRAGMENTATION #######################
jemalloc-bg-thread yes

The second file I'm going to use is a special file Redis ACL, short for Access Control List. As the name implies this file is used to define the users and their roles. In other words, this file is responsible for authentication and authorization.

There is one important point worth mentioning, for the instance to pick up this file, we have to uncomment in the config file the following line aclfile /etc/redis/users.acl in the security section. By default, the instance creates one user with a name default which does not have a password and has the authorization to perform all operations.

When we need to add a user we have to add the line in the ACL file with a special format, as explained here. The file used in this tutorial is below:

user default off nopass ~* +@all
user local_redis on +@all ~* &* >redis_password

It is not enough to add a new user, we have to disable the default user. Because if we don't do that, access to the instance will be open.

Now we can come back to the docker-compose definition file. The one we going to use is below.

version: '3.8'
services:
redis_local:
container_name: redis_local
image: redis:7.0.7
restart: always
command: ["redis-server", "/etc/redis/redis.conf"]
ports:
- "6379:6379"
volumes:
- ./config/redis.conf:/etc/redis/redis.conf
- ./config/users.acl:/etc/redis/users.acl

In this file, I specify the official docker image for the container. The full documentation about the image you can find below:

In the command section, I have to tell the instance to use the custom configuration file by passing its path as an argument. Then I map the port, for cases when access is needed from the outside of a container. And last but not least I map volumes. The first volume I map for the configuration file and the second is for the ACL file.

The path for the config file inside the container has to match the path passed as an argument in the command section of the docker-compose file. And the path for the ACL file inside the container has to match the path configured in the config file under the security section.

Ok, now we are ready to spin up the container with the Redis instance inside. To do that, from the directory where the docker-compose file is located execute the command:

$docker-compose up

or for Docker Compose used as a plugin:

$docker compose up

The creation of the instance may take a couple of minutes on the first run, depending on your PC and if you have a Redis image already pulled.

And then to check if everything works fine, execute the command to enter the container.

$docker container exec -it redis_local bash

More about Docker commands you can find here:

Execute the command to get the Redis CLI.

$redis-cli

Then command to log in, using credentials configured in the ACL file.

$AUTH local_redis redis_password

And after we PING a server. Without login, it won't work, because as you remember I disabled the default user.

Data types and commands

Redis has two kinds of data types: core and extensions. In this tutorial, I will focus on core data types, as extensions are used for special cases and deserve their own post. Core/basic datatypes are String, List, Hash, Set, and Sorted Set.

Redis is a schemaless, key–value database. The record in the Redis contains two parts: a key that represents an id of a value and a value itself.

Key

The key can be any unique binary sequence from a string to a content of a file. Using a file as a key is a not good idea.

There is good practice for key naming, according to the official documentation:

  1. The key can not be too short or too long. Max allowed size is 512 MB. One more bad idea is to use the key of 512 MB.
  2. Is recommended to create some kind of schema for naming the keys, that contain the name of the entity or an object and its id separated by the colon. For example “object:id”. If the schema contains more than one word is recommended to use dots and/or dashes, for example “object:id-number” or “object:id.number”. For collection types names in plural are preferred.
  3. The key is binary save. Redis does a binary comparison between the keys. It compares bytes of strings and not actual strings. So, the key “object:id”, “Object:id”, and “object:Id” are different.

The key is associated with the value. The value can be of different types. In this tutorial, I'm gonna use the following key schemas — customer:{id}, customer:{id}:payment-type, customer:{id}:payment-types.

The Redis naturally supports polymorphism in that mether you can associate the key with a value of one data type and then if needed change it to another data type. It is essential to know because command work on specific datatypes.

By default, keys are retained, but this behavior can be changed. We can set the expiration period for the key.

To list all available keys execute the command:

$KEYS * 

This command is for a development environment only, please do not use it in production, because it is a blocking command and it will scan all the available records and will degrade the performance of the Redis instance.

String

The simplest and most used data type is a String. The maximum size of a string is 512 MB.

To add the record to the Redis we use the SET command and get the record a GET command.

For example, I want to cache a customer payment type. I do the following:

$SET {KEY_NAME} {VALUE}

If the value consists of more than one word it has to be surrounded by the quotes. The same SET command is used when updating the value for the existing key. When the set command is executed against the existing key the value is updated.

$GET {KEY_NAME} 

And to delete the key:

$DEL {KEY_NAME}

Key expiration in action:

$SET {KEY_NAME} {VALUE} {SECONDS}

When the key was created I added an expiration argument equal to 60 seconds. After this time the key was evicted from the memory.

When the expiration period needs to be configured to the existing key, the following command is used:

$EXPIRE {KEY_NAME} {SECONDS}

In order to check the remaining time to live for the key execute the command:

$TTL {KEY_NAME}

When you want to check if the key exists you can execute the command:

$EXISTS {KEY_NAME}

MSET and MGET commands allow to perform SET and GET operations on multiple keys at once.

List

The List data type in Redis is implemented as a double-linked list. It allows duplicates and elements can be added to it from both sides — head and tail.

More on the linked list data structure you can find below:

In the Redis terminology, the head of the list is the left and the tail is the right it is used in the documentation and the names of commands.

Lists are useful to create queues, stacks, and capped lists. The list in Redis has O(1) time complexity.

We don't have to create the list before adding elements to it nor deleting the key when there are no elements in the list, because this is the responsibility of Redis.

In order to add the element to the list from the left (beginning) the below command is used:

$LPUSH {KEY_NAME} {VALUE}

And from the right side (end):

$RPUSH {KEY_NAME} {VALUE}

Both commands accept more than one value, so we can add a couple of elements separated by space at once.

In order to get elements from the left side of the list in the range execute the command:

$LRANGE {KEY_NAME} {START} {END}

Both the start and end can be negative numbers, which tells the command to start counting from the end of the list. For example to get all elements of the list the range has to be from 0 to -1. A zero is the first element and the minus one is the last element in the list.

To get the elements from the left side:

$LPOP {KEY_NAME}

And from the right:

$LPOP {KEY_NAME}

Both commands by default return one element, but this behavior can be changed by specifying the number of elements at the end of the command.

To obtain the number of elements inside the list execute the command:

$LLEN {KEY_NAME}

Capped list

The capped list is a list that retains a defined number of last elements. Useful to store “latest items”. For example, the customer has 4 payment types, but we wanna store the last 3 that he or she used.

In order to do this we gonna use the LTRIM command. This command:

$LTRIM {KEY_NAME} {START} {END}

As a result of execution, all the elements outside the given range are removed.

Hash

Think of a hash like a map (key-value data structure) associated with the key and it is useful to represent an object. To set the hash the below command is used:

$HSET {KEY_NAME} {KEY} {VALUE}

In order to get a field from a hash:

$HGET {KEY_NAME} {FIELD_NAME}

And to get multiple fields:

$HMGET {KEY_NAME} {FIELD_NAME}

In order to get all fields and values execute the command:

$HGETALL {KEY_NAME}

If you need to get all keys of the hash:

$HKEYS {KEY_NAME}

Set

The Set is an unordered collection of unique strings. The power of a set in its possibility to perform operations like checking if a given element exists in the set, and performing the union, intersection, and difference between sets.

To add the element to the sat the below command is used. This command allows adding multiple values to the set.

$SADD {KEY_NAME} {VALUE} 

To check the elements of a set:

$SMEMBERS {KEY_NAME}

In order to check if a value exists in a set, execute the command:

$SISMEMBER {KEY_NAME} {VALUE}

The set command that returns a number of elements inside the set is below:

$SCARD {KEY_NAME}

In order to perform an intersection between sets the following command is used. For example, we want to know common payment types among customers.

$SINTER {KEY_NAME_1} {KEY_NAME_N}

Sorted set

It is a set but ordered. The sorted set is similar to the hash in the matter that each value has a key, but the key here is called the score. The score is nothing more than a floating point number that governs the order of elements inside the set. To add an element to the ordered set use the command:

$ZADD {KEY_NAME} {SCORE} {VALUE}

In order to retrieve elements in the defined range use the command:

$ZRANGE {KEY_NAME} {START} {END}

If the scores are needed along the values, add WITHSCORES argument to the previous command.

$ZRANGE {KEY_NAME} {START} {END} WITHSCORES

To be able to retrieve elements from the set in a reverse, use the command:

$ZREVRANGE {KEY_NAME} {START} {END}

The full list of commands can be found in the official documentation.

The complete code can be found here:

Conclusion

Redis is a powerful tool that helps developers run their apps faster. To be able to use it in the right way as a first step is good to understand the datatypes and the commands. In this tutorial, I showed you how to create your local setup, and practice a variety of commands on different data types. Keep learning and discovering, because Redis can do a lot of cool stuff.

Thank you for reading! Please like and follow. If you have any questions or suggestions, please feel free to write me on my LinkedIn account.

Become a member for full access to Medium content.

--

--

Ivan Polovyi
Javarevisited

I am a Java Developer | OCA Java EE 8 | Spring Professional | AWS CDA | CKA | DCA | Oracle DB CA. I started programming at age 34 and still learning.