Sources:
Redis (from Remote Dictionary Server) is an open-source in-memory data structure store, used as a database, cache, message broker, and streaming engine.
Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams.
To achieve top performance, Redis works with an in-memory dataset. Depending on your use case, Redis can persist data either by periodically dumping the dataset to disk or by appending each command to a disk-based log.
Redis supports asynchronous replication, with fast non-blocking synchronization and auto-reconnection with partial resynchronization on net split.
Redis is often used as a cache on top of other databases to improve performance. However, Redis is a fully-fledged primary database that can be used to store and persist multiple data formats for complex applications.
Redis keys are binary safe, so you can use any binary sequence as a key, from a string to the content of a JPEG. An empty string is also a valid key.
Very long keys are not a good idea. The lookup of the key in the dataset may require several costly key-comparisons. Hashing a large value (such as with SHA1) is a better idea from the perspective of memory and bandwidth.
Very short keys are often also not a good idea. There is little benefit to writing "u1000flw" instead of "user:1000:followers". Readability makes up for the extra memory consumed.
Try to stick with a schema. Dots or dashes are often used for multi-word fields, for example: "comment:4321:reply.to" or "comment:4321:reply-to".
The maximum allowed key size is 512 MB.
The Redis string type is the simplest type of value you can associate with a Redis key. It's also the only data type in memcached, and so natural for newcomers to Redis.
Since Redis keys are strings, when you use a string type as a value too, you are mapping a string to a string. The string data type is useful for a number of use cases, such as caching HTML fragments or pages.
set mykey somevalue
# OK
get mykey
# "somevalue"
The SET
and GET
commands are the way to set and retrieve a string value. SET
will replace any existing value already stored in the key, even if the key is associated with a non-string value.
SET
can take arguments, for example to fail if the key already exists or only succeed if the key already exists.
set mykey newval nx
# (nil)
set mykey newval xx
# OK
Strings can be atomically incremented or decremented using the INCR
, INCRBY
, DERC
, and DECRBY
commands. These parse the string as an integer, increment/decrement it, and set the obtained value as the new value.
Since these commands are atomic, multiple clients issuign INCR
commands against the same key will never enter a race condition. The read-increment-set operation is performed while all the other clients are not executing a command.
set counter 100
incr counter
# (integer) 101
incrby counter 50
# (integer) 151
The GETSET
command sets a key to a new value, returning the old value as the result.
To reduce latency, you can set or retrieve the values of multiple keys in a single command: MSET
and MGET
. When MGET
is used, Redis returns an array of values.
mset a 10 b 20 c 30
# OK
mget a b c
# 1) "10"
# 2) "20"
# 3) "30"
Some commands are not defined on particular types and can be useful when interacting with keys of any type.
EXISTS
returns 1 or 0 to signal if a given key exists in the database.
DEL
deletes a key and associated value, whatever the value is, returning 1 if the key existed and was removed or 0 if no such key was found.
TYPE
returns the kind of value stored at the specified key.
set mykey hello
# OK
type mykey
# string
exists mykey
# (integer) 1
del mykey
# (integer) 1
type mykey
# none
exists mykey
# (integer) 0
Key expiration lets you set a timeout/"time to live" for a key. When the time to live expires, the key is automatically destroyed.
Key expiration can be set using either second or millisecond precision. The expire time resolution is always 1 millisecond.
Information about expires are replicated and persisted on disk. The time virtually passes when your Redis server remains stopped, meaning Redis saves the darte at which a key will expire.
Use EXPIRE
to set a key's expiration. Use PERSIST
to remove the expire and make the key persist forever.
set mykey somevalue
# OK
expire key 5
# (integer) 1
# after some time
get key
# (nil)
You can create keys with expires using other commands.
set key 100 ex 10
# OK
ttl key
# (integer) 9
Redis lists are implemented via linked lists. This means that even if you have millions of elements inside a list, the operation of adding a new element to the head or tail of the list is performed in constant time. The speed of using LPUSH
to add an element to the head of a list with ten elements is the the same as adding to the head of a list with 10 million elements.
The downside is that accessing an element by index is slower than in lists implemented with an array. The operation requires an amount of work proportional to the index of the accessed element. For fast access to the middle of a large collection, Redis uses sorted sets.
The LPUSH
command adds a new element to the head of a list, while RPUSH
adds a new element to the tail of a list.
LRANGE
extracts ranges of elements from a list.
rpush mylist A
# (integer) 1
rpush mylist B
# (integer) 1
lpush mylist first
# (integer) 3
lrange mylist 0 -1
# 1) "first"
# 2) "A"
# 3) "B"
LRANGE
takes two indexes, the first and last element of the range to return. Both indexes can be negative, telling Redis to start counting from the end: -1 is the last element, -2 the penultimate.
Both LPUSH
and RPUSH
can push multiple elements into a list in a single call.
rpush mylist 1 2 3 4 5 "and a string"
# (integer) 9
lrange mylist 0 -1
# 1) "first"
# 2) "A"
# 3) "B"
# 4) "1"
# 5) "2"
# 6) "3"
# 7) "4"
# 8) "5"
# 9) "and a string"
You can also pop elements from the left or right of a list. Popping an element both retrieves it from the list and eliminates it from the list at the same time.
Redis will return a null value if there are no elements in the list.
rpush mylist 1 2 3
# (integer) 3
rpop mylist
# "c"
rpop mylist
# "b"
rpop mylist
# "a"
rpop mylist
# (nil)
Representative use cases for lists include:
- Remembering the latest updated posts by users
- Communicating between processes, using a consumer-producer pattern where the producer pushes items into a list and a consumer (usually a worker) consumes those items and executes actions. Redis has special list commands to make this use case both more reliable and efficient.
For example, image your home page shows your latest photos published and you want to speed up access.
- Every time a user posts a new photo, its ID is added into a list with
LPUSH
- When users visit the homepage,
LRANGE 0 9
is used to get the latest 10 posted items.
In many use cases you just want a list to store the latest items, whether they are logs, social media updates, etc.
Redis lets you use lists as a capped collection, only remembering the latest n items and discarding all the oldest items using the LTRIM
command.
LTRIM
is similar to LRANGE
but instead of displaying the specified range of elements it sets this range as the new list value. All elements outside the given range are removed.
rpush mylist 1 2 3 4 5
# (integer) 5
ltrim mylist 0 2
# OK
lrange mylist 0 -1
# 1) "1"
# 2) "2"
# 3) "3"
A simple but useful pattern is doing a list push operation and a list trim operation together to add a new element and discard elements exceeding a limit. For example, adding a new element and taking only the 1000 newest elements as the list.
lpush mylist some_element
ltrim mylist 0 999
Lists have a special feature that makes them suitable to implement queues, and in general as a building block for inter-process communication systems: blocking operations.
Imagine you want to push items into a list with one process, and use a different process to do work with those items. This producer/consumer setup can be implemented as follows:
- To push items into the list, producers call
LPUSH
- To extract/process items from the list, consumers call
RPOP
However, it's possible that sometimes the list is empty and there is nothign to process, so RPOP
just returns null. In this case, a consumer is forced to wait some time and retry RPOP
. This polling is not a good idea in this context because:
- It forces Redis and clients to process useless commands (all the requests when the list is empty will get no work done and just return null)
- It adds a delay to the processing of items, since after a worker receives null, it waits some time. To make the delay smaller, you could wait less time between calls to
RPOP
but this means more useless calls to Redis
Redis' BRPOP
and BLPOP
commands are versions of RPOP
and LPOP
that are able to block if the list is empty. They will return to the caller only when a new element is added to the list, or when a user-specified timeout is reached.
# wait for elements in tasks, but return after 5 seconds if no element is available
brpop tasks 5
# 1) "tasks"
# 2) "do_something_else"
You can use 0 as a timeout to wait for elements forever, and you can also specify multiple lists in order to wait on multiple lists at the same time, and get notified when the first list receives an element.
Regarding BRPOP
:
- Clients are served in an ordered way. The first client that blocked waiting for a list is served first when an element is pushed by some other client
- The return value is different compared to
RPOP
. It's a two-element array since it also includes the name of the key, becauseBRPOP
andBLPOP
are able to block while waiting for elements from multiple lists - If the timeout is reached, null is returned
It's possible to build safer queues, or rotating queues, using LMOVE
. There is also a blocking variant of this command, called BLMOVE
With Redis data types composed of multiple elements (Lists, Streams, Sets, Sorted Sets, Hashes), Redis has the following behaviour:
- When you add an element to an aggregate data type, if the target key does not exist, an empty aggregate data type is created before adding the element
- When you remove elements from an aggregate data type, if the value remians empty, the key is automatically destroyed (with the exception of Stream)
- Calling a read-only command, such as
LLEN
(which returns the length of a list), or a write command removing elements, with an empty key, always produces the same result as if the key is holding an empty aggregate type of the type the command expects to find.
Redis hashes consist of field-value pairs.
hset user:1000 username antirez birthyear 1977 verified 1
# (integer) 3
hget user:1000 username
# "antirez"
hget user:1000 birthyear
# "1977"
hgetall user:1000
# 1) "username"
# 2) "antirez"
# 3) "birthyear"
# 4) "1977"
# 5) "verified"
# 6) "1"
While hashes are useful for representing objects, the number of fields you can put inside a hash has no practical limits other than available memory, so you can use hashes in serveral different ways inside an application.
HSET
sets multiple fields of the hash, while HGET
retrieves a single field. HMGET
is similar to HGET
but returns an array of values:
hmget user:1000 username birthyear no-such-field
# 1) "antirez"
# 2) "1977"
# 3) (nil)
Individual fields can be operated on, such as with HINCRBY
.
hincrby user:1000 birthyear 10
# (integer) 1987
hincrby user:1000 birthyear 10
# (integer) 1997
Small hashes are encoded differently to make them very memory efficient.
Redis sets are unordered collections of strings.
SADD
adds new elements to a set. There are also commands for testing if a given element already exists, performing the intersection, union or difference between multiple sets, and so on.
sadd myset 1 2 3
# (integer) 1
smembers myset
# 1. 3
# 2. 1
# 3. 2
sismember myset 3
# (integer) 1
sismember myset 30
# (integer) 0
Sets are good for expressing relations between objects. For instance, to implement tags. A simple way to model the problem is to have a set for every object you want to tag. The set contains the IDs of the tags associated with the object.
For example, if new article ID 1000 is tagged with tags 1, 2, 5, and 77, a set can associate these tag IDs with the news item.
sadd news:1000:tags 1 2 5 77
# (integer) 4
You might also want to have the inverse relation as well, a list of all the news articles tagged with a given tag.
sadd tag:1:news 1000
# (integer) 1
sadd tag:2:news 1000
# (integer) 1
sadd tag:5:news 1000
# (integer) 1
sadd tag:77:news 1000
# (integer) 1
Then to get all tags for a given object:
smembers news:1000:tags
# 1. 5
# 2. 1
# 3. 77
# 4. 2
All this assumes you have another data strcuture (such as a Redis hash) which maps tag IDs to tag names.
If you want a list of all the objects with the tags 1, 2, 10, and 27, you can use the SINTER
command, which performs the intersection between dffereent sets.
sinter tag:1:news tag:2:news tag:10:news tag:27:news
In addition to intersections, you can perform operations on sets such as unions, difference, extracting a random element.
You can extract a random element from a set and return it to the client using SPOP
.
SUNIONSTORE
performs the union between multiple sets and stores the result in another set. Since the union of a single set is itself, you can use SUNIONSTORE
to copy a set.
sunionstore game:1:deck deck
# (integer) 52
SCARD
provides the number of elements inside a set, also known as the cardinality of the set.
scard game:1:deck
# (integer) 52
When you need to get random elements from a set without removing them from the set, use the SRANDMEMBER
command. It also features the ability to return both repeating and non-repeating elements.
Sorted Sets are a data type similar to a mix between a Set and a Hash. Like sets, sorted sets are composed of unique, non-repeating string elements, so in a sense a sorted set is a set as well.
While elements inside sets are not ordered, every element in a sorted set is associated with a floating point value, called the score. This is similar to a hash since every element is mapped to a value.
Elements in a sorted set are taken in order. They are not ordered on request, order is a peculiarity of the data structure used to represent sorted sets.
They are ordered according to the following rule:
- If B and A are two elements with different scores, then A > B if A.score > B.score
- If B and A have exactly the same score, then A > B if the A string is lexicographically greater than the B string. B and A strings can't be equal since sorted sets only have unique elements.
zadd hackers 1957 "Sophie Wilson"
zadd hackers 1965 "Yukihiro Matsumoto"
zadd hackers 1912 "Alan Turing"
ZADD
is similar to SADD
but takes one additional argument (placed before the element to be added) which is the score. ZADD
is variadic, so you are free to specify multiple score-value pairs.
Sorted sets make it easy to return sorted elements becuase they are already sorted by the score. You use ZRANGE
to access a range between two indexes. You can access elements in reverse using ZREVRANGE
.
zrange hackers 0 -1
# 1) "Alan Turing"
# 2) "Sophie Wilson"
# 3) "Yukihiro Matsumoto"
Using the WITHSCORES
argument will return scores as well.
zrange hackers 0 -1 withscores
# 1) "Alan Turing"
# 2) "1912"
# 3) "Sophie Wilson"
# 4) "1957"
# 5) "Yukihiro Matsumoto"
# 6) "1965"
Sorted Sets can operate on ranges. The ZRANGEBYSCORE
command will return all the elements with a score within a specified range (both extremes included).
zrangebyscore hackers -inf 1950
# 1) "Alan Turing"
It's also possible to remove ranges of elements using ZREMRANGEBYSCORE
. This returns the number of removed elements.
zremrangebyscore hackers 1940 1960
# (integer) 1
The ZRANK
command lets you perform a get-rank operation, accessing the position of an element in a set of ordered elements. ZREVRANK
will get the rank, considering the elements sorted in descending order.
zrank hacker "Alan Turing"
# (integer) 0
From Redis 2.8 it's possible to get ranges lexicographically, assuming elements in a sorted set are all inserted with the same identical score.
The main commands to operate with lexicographical ranges are:
ZRANGEBYLEX
ZREVRANGEBYLEX
ZREMRANGEBYLEX
ZLEXCOUNT
zadd hackers 0 "Sophie Wilson" 0 "Yukihiro Matsumoto" 0 "Alan Turing"
zrange hackers 0 -1
# 1) "Alan Turing"
# 2) "Sophie Wilson"
# 3) "Yukihiro Matsumoto"
Using ZRANGEBYLEX
you can ask for lexicographical ranges. Ranges can be inclusive or exclusive, depending on the first character.
zrangebylex hackers [B [W
# 1) "Sophie Wilson"
This feature allows you to use sorted sets as a generic index
Sorted sets' scores can be updated at any time. Calling ZADD
against an element already included in the sorted set will update its score (and position) with O(log(N)) time complexity. As such, sorted sets are suitable when there are tons of updates.
Their characteristics make sorted sets suitable for leader boards. You combine the ability to take users sorted by their high score, plus the get-rank operation, in order to show the top-N users, and the user rank in the leader board.
Bitmaps are not an actual data type, but a set of bit-oriented operations defined on the String type. Since strings are binary-safe blobs and their maximum length is 512MB, they are suitable to set up to 2^32 different bits.
Bit operations are divided into two groups: constant-time single-bit operations, like setting a bit to 1 or 0, or getting its value, and operations on groups of bits, for example counting the number of set bits in a given range of bits, such as population counting.
One of the biggest advantages of bitmaps is that they often provide extreme space savings when storing information. For example, in a system where different users are represented by incremental user IDs, it's possible to remember a single bit information (for example, whether a user wants to receive a newsletter) of 4 billion users using just 512MB of memory.
Bits are set and retrieved using the SETBIT
and GETBIT
commands.
SETBIT
takes as its first argument the bit number, and as its second argument the value to set the bit to, which is 1 or 0. The command automatically enlarges the string if the addressed bit is outside the current string length.
GETBIT
returns the value of the bit at the specified index. Out of range bits (addressing a bit that is outside the length of the string stored into the target key) are always considered to be zero.
setbit key 10 1
# (integer) 1
getbit key 10
# (integer) 1
getbit key 11
# (integer) 0
There are three commands operating on groups of bits:
BITOP
performs bit-wise operations between different strings. The provided operations are AND, OR, XOR, and NOTBITCOUNT
performs population counting, reporting the number of bits set to 1BITPOS
finds the first bit having the specified value of 0 or 1
Both BITPOS
and BITCOUNT
are able to operate with byte ranges of the string, instead of running for the whole length of the strings.
setbit key 0 1
# (integer) 1
setbit key 100 1
# (integer) 0
bitcount key
# (integer) 2
Common uses for bitmaps are:
- Real-time analytics of all kinds
- Storing space-efficient but high performance boolean information associated with object IDs
Bitmaps can be split into multiple keys, for example for the sake of sharding the data set and because in general it is better to avoid working with huge keys. To split a bitmap across different keys instead of setting all the bits into a key, store M bits per key and obtain the key name with bit-number/M, and the Nth bit to address inside the key with bit-number MOD M.
A HyperLogLog is a probabilistic data structure used in order to count things (technically estimating the cardinality of a set).
Usually counting unique items requires using an amount of memory proportional to the number of items you want to count, because you need to remember the elements you have already seen in the past in order to avoid counting them multiple times. However there is a set of algorithms that trade memory for precision: you end with an estimated measure with a standard error (in the Redis implementation, less than 1%). You no longer need to use an amount of memory proportional to the number of items counted, and instead can use a constant amount of memory, 12K bytes in the worst case, and a lot less if the HyperLogLog has seen very few elements.
HyperLogLogs in Redis, while technically a different data structure, are encoded as a Redis string, so you can call GET
to serialize a HyperLogLog and SET
to deserialize it back to the server.
Conceptually, the HyperLogLog API is like using Sets to do the same task. You would SADD
every observed element into a set, and use SCARD
to check the number of elements inside the set, which are unique since SADD
will not re-add an existing element.
While you don't really add iterms to a HyperLogLog, because the data structure only contains a state that does not include actual elements, the API is the same:
- Every time you see a new element, you add it to the count with
PFADD
- Every time you want to retrieve the current approximation of the unique elements added with
PFADD
so far, you usePFCOUNT
pfadd hll a b c d
# (integer) 1
pfcount hll
# (integer) 4
An example use case could be counting unique queries performed by users in a search form every day.
External programs talk to Redis using a TCP socket and a Redis-specific protocol, which is implemented in the Redis client libraries for each supported programming language. Redis also provides a command-line utility to send commands to Redis.
redis-cli has two main modes: an interactive REPL mode where the user types Redis commands and receives replies, and a command mode, where the CLI is executed with additional arguments and the reply is printed to the standard output.
You can check if Redis is working by sending a ping
command from redis-cli.
Runing redis-cli
followed by a command and its arguments will send this command to the Redis instance running on localhost port 6379. You can change the host and port using redis-cli.
If you run redis-cli without arguments, the program will start in interactive mode.
redis-cli
ping
# PONG
set mykey somevalue
# OK
get mykey
# "somevalue"
Redis replies are typed and you will see the type of the reply (string, array, integer, nil, error) between parentheses. redis-cli only shows this additional information for human readability when it detectds the standard output is a tty or terminal. For all other outputs it wil auto-enable the raw output mode.
redis-cli INCR mycounter > /tmp/output.txt
cat /tmp/output.txt
# 8
You can enforce raw output on the terminal with the --raw
option and enforce human-readable output when writing to files or in a pipe to other commands by using -no-raw
.
When redis-cli parses a command, whitespace characters automatically delimit the arguments. You can use quoted and escaped strings to input string values that contain whitespaces or non-printable characters.
Double quoted strings support the following escape sequences:
\"
- double-quote\n
- newline\r
- carriage return\t
- horizontal tab\b
- backspace\a
- alert\\
- backslash\xhh
- any ASCII character represented by a hexadecimal number
Single quotes assume the string is literal, and allow only the following escape sequences:
\'
- single quote\\
- backslash
AUTH some_admin_user ">^8T>6Na{u|jp>+v\"55\@_;OU(OR]7mbAYGqsfyu48(j'%hQH7;v*f1H${*gD(Se'"
By default, redis-cli connects to the server at 127.0.0.1 on port 6379. To specify a different hostname or IP adress, use the -h
option. To set a different port, use the -p
option.
If your instance is password-protected, the -a <password>
option will perform authentication saving the need to use the AUTH
command.
For security, you should provide the password to redis-cli automatically via the REDISCLI_AUTH
environment variable.
redis-cli -a myUnguessablePazzzzzword123 PING
PONG
It's possible to send a command that operates on a database number other than the default number zero by using the -n <dbnum>
option:
redis-cli FLUSHALL
# OK
redis-cli -n 1 INCR a
# (integer) 1
redis-cli -n 1 INCR a
# (integer) 2
redis-cli -n 2 INCR a
# (integer) 1
Some, or all, of this information can be provided by using the -u <uri>
option and the URI pattern redis://user:password@host:port/dbnum
.
redis-cli -u redis://LJenkins:p%40ssw0rd@redis-16379.hosted.com:16379/0 PING
# PONG
By default, redis-cli uses a plain TCP connection to connect to Redis. You can enable SSL/TLS using the --tls
option, along with --cacert
or --cacertdir
to configure a trusted root certificate bundle or directory.
If the target server requires authentication using a client-side certificate, you can specify a certificate and a corresponding private key using --cert
and --key
.
There are two ways you can use redis-cli to receive input from other commandsvia the standard input.
One is to use the target payload as the last argument from from stdin. For example, to set the Redis key net_services
to the contents of the file /etc/services
from a local filesystem, use the -x
option.
redis-cli -x SET net_services < /etc/services
# OK
redis-cli GETRANGE net_services 0 50
# "#\n# Network services, Internet style\n#\n# Note that "
A different approach is to feed redis-cli a sequence of commands written in a text file. The commands in the fle will be executed sequentially by redis-cli as if they were typed by the user in interactive mode.
Strings can be quoted inside the file so it's possible to have single arguments with spaces, newlines, or other special characters: SET arg_example "This is a single argument"
cat /tmp/commands.txt
# SET item:3374 100
# INCR item:3374
# APPEND item:3374 xxx
# GET item:3374
cat /tmp/commands.txt | redis-cli
# OK
# (integer) 101
# (integer) 6
# "101xxx"
It's possible to execute a single command a specified number of times with a user-selected pause between executions. This could be useful if you want to monitor some key content of INFO
field output, or if you want to simulate some recurring write event.
This is controlled by the -r
option (how many times to run a command) and the -i
option (the interval in seconds between commands). By default the interval is set to 0, so commands are executed as soon as possible. The interval can also be specified in milliseconds, such as 0.1 to represent 100 milliseconds.
redis-cli -r 5 INCR counter_value
# (integer) 1
# (integer) 2
# (integer) 3
# (integer) 4
# (integer) 5
To run the same command indefinitely, use -1 as the count value.
TODO
redis-cli can export data from Redis to an external program using a CSV output feature.
The --csv
flag will only work on a single command, not the entirety of a DB as an export.
redis-cli LPUSH mylist a b c d
# (integer) 4
redis-cli --csv LRANGE mylist 0 -1
# "d", "c", "b", "a"
From Redis 3.2 onwards, redis-cli has extensive support for using the degbugging facility of Lua scripting.
Even without using the debugger, redis-cli can be used to run scripts from a file as an argument.
cat /tmp/script.lua
# return redis.call('SET', KEYS[1],ARGV[1])
redis-cli --eval /tmp/script.lua location:hastings:temp , 23
# OK
The Redis EVAL
command takes the list of keys the script uses, and the other non-key arguments, as different arrays. When calling EVAL
you provide the number of keys as a number.
When calling redis-cli with the --eval
option, there is no need to specify the number of keys explicitly. Instead, it uses the convention of seperating keys and arguments with a comma.
The --eval
option is useful when writing simple scripts. For more complex work, the Lua debugger is recommended. The two can be mixed since the debugger can also execute scripts from an external file.
By default, Redis binds to all interfaces and has no authentication at all.
The following steps will increase security:
- make sure the port Redis uses to listen for connections (default 6379, cluster modde 16379, Sentinel 26379) is firewalled, so that it's not possible to contact Redis from the outside world.
- Use a configuration file where the
bind
directive is set in order to guarantee that Redis listens on only the network interfaces you are using. For example, only the loopback interface, 127.0.0.1, if you are accessing Redis locally from the same computer. - Use the
requirepass
option to add an additional layer of security so that clients will be required to authenticate using theAUTH
command. - Use spiped or another SSL tunnelling software to encrypt traffic between Redis servers and Redis clients if your environment requires encryption.
A Redis instance exposed to the internet without any security is very simple to exploit, so make sure to at least apply a firewall layer.
A complex application serving millions of users through multiple microservices might need a relational database, full text search, graph database, document database, and caching service.
The challenges of multiple data services include:
- All these data services need to be deployed and maintained
- Your team needs know-how for each data service
- There are different scaling and infrastructure requirements for each data service
You could use the managed data services from Cloud Service Providers, but this could be very expensive as you pay for each service seperately.
Your application code also becomes more complex when having to interact with with different databases, each requiring a seperate connector and logic.
There is also the issue of higher latency because of more network hopping.
As a multi-model database, Redis can resolve most of these challenges:
- You run and maintain just one data service
- This requires only one programmatic interface
- Latency will be reduced by using a single data service endpoint
Redis allows you to store different data structures, while also acting as a cache.
Redis Core is a key-value store that supports storing multiple types of data, such as:
- Strings
- Sets
- Bitmaps
- Sorted Sets
- Bit Fields
- Geospatial
- Hashes
- Hyperloglog
- Lists
- Streams
Redis Core can be extended with modules for different data types:
- RediSearch for search functionality
- RedisGraph for graph data storage
- RedisJSON for document data storage
- RedisTimeSeries for time series data
This is modular and you can choose which functionality you want to add.
Since you don't need to implement a caching layer, there is less complexity in your application.
As an in-memory database, Redis is fast and performant making both the application and its test suites faster.
Redis doesn't need a schema and so it doesn't need time to initialize the database and build a schema before running the tests.
Since Redis is an in-memory database, if the server or Redis process fails all the stored data will be lost.
The simplest way to have data backups is to replicate Redis, so if the primary instance goes down, the replicas will still be running.
Redis has multiple methods for persisting data:
- Snapshots (RDB)
- Produces single-file point-in-time snapshots of your dataset
- Can be configured based on time or number of writes passed
- Data is stored on disk. Great for backup and disatser recovery
- You may lose the latest minutes of data since the last snapshot