Redis

Sanket Saxena
14 min readJun 4, 2023

--

Redis, an open-source, in-memory data structure store, is used as a database, cache, and message broker. It supports various data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries, and streams. This article will delve into the advanced features of Redis, such as Streams, Redis JSON, Redis OM, Redis Search, Redis TimeSeries, Redis Insights, Redis Graph, RedisBloom, and Redis Enterprise Cloud. We will explore each of these features in detail, with examples in Node.js to illustrate their practical applications.

Redis Streams

Redis Streams is a feature that allows you to handle data as a stream. It provides commands like XADD, XREAD, XGROUP, XACK, XDEL, XTRIM, XRANGE, XREVRANGE, and XINFO to add, read, acknowledge, delete, trim, range over the stream data, and fetch information about the stream.

XRANGE

The XRANGE command is used to fetch a range of messages from the stream. It takes the stream key and two IDs as arguments. The IDs represent the start and end of the range. Here's an example in Node.js:

const redis = require('redis');
const client = redis.createClient();
client.xrange('mystream', '-', '+', function(err, stream) {
console.log(stream);
});

XADD

The XADD command is used to add a message to the stream. It takes the stream key, an ID, and one or more field-value pairs as arguments. Here's an example in Node.js:

client.xadd('mystream', '*', 'field1', 'value1', 'field2', 'value2', function(err) {
if(err) throw err;
});

XREAD

The XREAD command is used to read messages from one or more streams. Here's an example in Node.js:

client.xread('STREAMS', 'mystream', '$', function(err, stream) {
console.log(stream);
});

XGROUP

The XGROUP command is used to create, destroy, and manage consumer groups. Here's an example of creating a consumer group in Node.js:

client.xgroup('CREATE', 'mystream', 'mygroup', '$', function(err) {
if(err) throw err;
});

XACK

The XACK command is used to acknowledge a message on behalf of a consumer group. Here's an example in Node.js:

client.xack('mystream', 'mygroup', '1526569495631-0', function(err) {
if(err) throw err;
});

XDEL

The XDEL command is used to delete a message from a stream. Here's an example in Node.js:

client.xdel('mystream', '1526569495631-0', function(err) {
if(err) throw err;
});

XTRIM

The XTRIM command is used to limit the length of a stream to a certain size. Here's an example in Node.js:

client.xtrim('mystream', 'MAXLEN', '~', '1000', function(err) {
if(err) throw err;
});

XINFO

The XINFO command is used to fetch information about the stream and its consumer groups. Here's an example in Node.js:

client.xinfo('STREAM', 'mystream', function(err, info) {
console.log(info);
});

Redis JSON

Redis JSON is a module that allows users to store, update, and fetch JSON values from Redis. It provides a way to persist data in JSON format.

Persisting Data in JSON

You can store JSON data in Redis using the JSON.SET command. Here's an example in Node.js:

let data = {
name: 'John',
age: 30,
city: 'New York'
};
client.json_set('user', '.', JSON.stringify(data), function(err) {
if(err) throw err;
});

Fetching JSON Data

You can fetch JSON data from Redis using the JSON.GET command. Here's an example in Node.js:

client.json_get('user', function(err, json) {
if(err) throw err;
console.log(JSON.parse(json));
});

Updating JSON Data

You can update JSON data in Redis using the JSON.SET command with the path argument. Here's an example in Node.js:

client.json_set('user', '.age', '31', function(err) {
if(err) throw err;
});

Deleting JSON Data

You can delete JSON data from Redis using the JSON.DEL command. Here's an example in Node.js:

client.json_del('user', function(err) {
if(err) throw err;
});

Redis OM

Redis OM (Object Mapping) is a high-level API that provides a way to interact with Redis in an object-oriented manner. It supports indexing and two types of models: EmbeddedJsonModel and JsonModel.

Indexing

Redis OM allows you to create indexes on fields of your JSON objects. This makes it easier to search for specific data. Here’s an example of how you can create an index in Node.js:

client.json_set('user', '.', JSON.stringify({name: 'John', age: 30, city: 'New York'}), function(err) {
if(err) throw err;
client.json_index_add('user', 'name', function(err) {
if(err) throw err;
});
});

Searching Indexed Data

Once you have indexed data, you can search for it using the JSON.QGET command. Here's an example in Node.js:

client.json_qget('user', '.name', 'John', function(err, json) {
if(err) throw err;
console.log(JSON.parse(json));
});

Using the JsonModel

The JsonModel is a high-level API that allows you to interact with JSON data as if it were a JavaScript object.Here's an example in Node.js:

const redis = require('redis');
const RedisOM = require('redis-om');
const client = redis.createClient();
const om = new RedisOM(client);
const User = om.define('User', {
name: String,
age: Number,
city: String
});
let user = new User({name: 'John', age: 30, city: 'New York'});
user.save(function(err) {
if(err) throw err;
});

Using the EmbeddedJsonModel

The EmbeddedJsonModel is similar to the JsonModel, but it allows you to embed other models within it. Here's an example in Node.js:

const Address = om.define('Address', {
street: String,
city: String,
state: String,
zip: String
});
const User = om.define('User', {
name: String,
age: Number,
address: Address
});
let address = new Address({street: '123 Main St', city: 'New York', state: 'NY', zip: '10001'});
let user = new User({name: 'John', age: 30, address: address});
user.save(function(err) {
if(err) throw err;
});

Fetching Data with JsonModel and EmbeddedJsonModel

You can fetch data from Redis using the find method of the JsonModel and EmbeddedJsonModel. Here's an example in Node.js:

User.find({name: 'John'}, function(err, users) {
if(err) throw err;
console.log(users);
});

Updating Data with JsonModel and EmbeddedJsonModel

You can update data in Redis using the save method of the JsonModel and EmbeddedJsonModel. Here's an example in Node.js:javascriptCopy code

User.find({name: 'John'}, function(err, users) {
if(err) throw err;
let user = users[0];
user.age = 31;
user.save(function(err) {
if(err) throw err;
});
});

Deleting Data with JsonModel and EmbeddedJsonModel

You can delete data from Redis using the delete method of the JsonModel and EmbeddedJsonModel. Here's an example in Node.js:

User.find({name: 'John'}, function(err, users) {
if(err) throw err;
let user = users[0];
user.delete(function(err) {
if(err) throw err;
});
});

Redis Search

Redis Search is a feature that provides full-text search capabilities to Redis. It’s integrated with Redis OM, which takes care of deciding whether to use Redis Search or not based on the data type and index.

Full-Text Search

You can perform a full-text search in Redis using the FT.SEARCH command. Here's an example in Node.js:

client.ft_search('myindex', 'hello world', function(err, results) {
if(err) throw err;
console.log(results);
});

Adding Data to the Index

You can add data to the index in Redis using the FT.ADD command. Here's an example in Node.js:

client.ft_add('myindex', 'doc1', 1.0, 'FIELDS','body', 'hello world', function(err) {
if(err) throw err;
});

Removing Data from the Index

You can remove data from the index in Redis using the FT.DEL command. Here's an example in Node.js:

client.ft_del('myindex', 'doc1', function(err) {
if(err) throw err;
});

Updating Data in the Index

You can update data in the index in Redis using the FT.ADD command with the REPLACE option. Here's an example in Node.js:

client.ft_add('myindex', 'doc1', 1.0, 'REPLACE', 'FIELDS', 'body', 'goodbye world', function(err) {
if(err) throw err;
});

Fetching Data from the Index

You can fetch data from the index in Redis using the FT.GET command. Here's an example in Node.js:

client.ft_get('myindex', 'doc1', function(err, doc) {
if(err) throw err;
console.log(doc);
});

Creating a Secondary Index

You can create a secondary index in Redis using the FT.CREATE command with the SCHEMA option. Here's an example in Node.js:

client.ft_create('myindex', 'SCHEMA', 'body', 'TEXT', 'title', 'TEXT', function(err) {
if(err) throw err;
});

Searching with a Secondary Index

You can search with a secondary index in Redis using the FT.SEARCH command with the INKEYS option. Here's an example in Node.js:

client.ft_search('myindex', 'hello world', 'INKEYS', '1', 'doc1', function(err, results) {
if(err) throw err;
console.log(results);
});

Aggregating Search Results

You can aggregate search results in Redis using the FT.AGGREGATE command. Here's an example in Node.js:

client.ft_aggregate('myindex', 'hello world', 'GROUPBY', '1', '@body', 'REDUCE', 'COUNT', '0', 'AS', 'count', function(err, results) {
if(err) throw err;
console.log(results);
});

Redis TimeSeries

Redis TimeSeries is a Redis module that provides a way to store and query time series data in Redis.

Creating a TimeSeries Key

You can create a TimeSeries key in Redis using the TS.CREATE command. Here's an example in Node.js:

client.ts_create('temperature', function(err) {
if(err) throw err;
});

Adding Data to a TimeSeries Key

You can add data to a TimeSeries key in Redis using the TS.ADD command. Here's an example in Node.js:

client.ts_add('temperature', '*', '20.5', function(err) {
if(err) throw err;
});

Fetching Data from a TimeSeries Key

You canfetch data from a TimeSeries key in Redis using the TS.RANGE command. Here's an example in Node.js:

client.ts_range('temperature', '-', '+', function(err, data) {
if(err) throw err;
console.log(data);
});

Aggregating Data from a TimeSeries Key

You can aggregate data from a TimeSeries key in Redis using the TS.RANGE command with the AGGREGATION option. Here's an example in Node.js:

client.ts_range('temperature', '-', '+', 'AGGREGATION', 'avg', '60', function(err, data) {
if(err) throw err;
console.log(data);
});

You can create a rule for a TimeSeries key in Redis using the TS.CREATERULE command. This allows you to automatically aggregate data into another TimeSeries key. Here's an example in Node.js:

client.ts_createrule('temperature', 'avg_temperature', 'avg', '60', function(err) {
if(err) throw err;
});

Fetching Information about a TimeSeries Key

You can fetch information about a TimeSeries key in Redis using the TS.INFO command. Here's an example in Node.js:javascriptCopy code

client.ts_info('temperature', function(err, info) {
if(err) throw err;
console.log(info);
});

Deleting a TimeSeries Key

You can delete a TimeSeries key in Redis using the DEL command. Here's an example in Node.js:

client.del('temperature', function(err) {
if(err) throw err;
});

Fetching the Last Data Point from a TimeSeries Key

You can fetch the last data point from a TimeSeries key in Redis using the TS.GET command. Here's an example in Node.js:

client.ts_get('temperature', function(err, data) {
if(err) throw err;
console.log(data);
});

Redis Insights

RedisInsights is a free GUI for Redis that provides an intuitive and efficient way to interact with your databases. It allows you to monitor performance, analyze memory usage, execute commands, and more.

Monitoring Performance

RedisInsights provides a real-time performance monitoring dashboard that shows key metrics such as operations per second, memory usage, and CPU usage.

Analyzing Memory Usage

RedisInsights provides a memory analysis tool that allows you to see how your memory is being used. It can help you identify large keys, detect memory leaks, and optimize your memory usage.

Executing Commands

RedisInsights provides a command line interface that allows you to execute any Redis command. It also provides auto-complete and syntax highlighting to make it easier to write commands.

Exploring Data

RedisInsights provides a data explorer that allows you to browse your keys in a tree-like structure. You can also view and edit the values of your keys.

Managing Connections

RedisInsights allows you to manage multiple Redis connections. You can add, edit, and remove connections, and switch between them with ease.

Importing and Exporting Data

RedisInsights allows you to import and export data in various formats such as JSON, CSV, and Redis protocol. This makes it easy to migrate data between different Redis instances or to backup your data.

Analyzing Slow Logs

RedisInsights provides a slow log analyzer that allows you to identify slow commands that might be affecting your performance. You can view the details of each command, including its execution time and arguments.

Configuring Redis

RedisInsights allows you to view and edit your Redis configuration. You can change configuration parameters on the fly without having to restart your Redis server.

Redis Graph

RedisGraph is a graph database module for Redis. It allows you to store, query, and process graph data in Redis.

Creating a Graph

You can create a graph in RedisGraph using the GRAPH.QUERY command with the CREATE clause. Here's an example in Node.js:

client.graph_query('mygraph', 'CREATE (:person {name: "John", age: 30})', function(err) {
if(err) throw err;
});

Adding Data to a Graph

You can add data to a graph in RedisGraph using the GRAPH.QUERY command with the CREATE clause. Here's an example in Node.js:

client.graph_query('mygraph', 'CREATE (:person {name: "Jane", age: 25})', function(err) {
if(err) throw err;
});

Querying a Graph

You can query a graph in RedisGraph using the GRAPH.QUERY command with the MATCH clause. Here's an example in Node.js:

client.graph_query('mygraph', 'MATCH (p:person) RETURN p', function(err, results) {
if(err) throw err;
console.log(results);
});

Updating Data in a Graph

You can update data in a graph in RedisGraph using the GRAPH.QUERY command with the SET clause. Here's an example in Node.js:

client.graph_query('mygraph', 'MATCH (p:person {name: "John"}) SET p.age = 31', function(err) {
if(err) throw err;
});

Deleting Data from a Graph

You can delete data from a graph in RedisGraph using the GRAPH.QUERY command with the DELETE clause. Here's an example in Node.js:

client.graph_query('mygraph', 'MATCH (p:person {name: "John"}) DELETE p', function(err) {
if(err) throw err;
});

Creating a Relationship between Nodes

You can create a relationship between nodes in a graph in RedisGraph using the GRAPH.QUERY command with the CREATE clause. Here's an example in Node.js:

client.graph_query('mygraph', 'MATCH (a:person {name: "John"}), (b:person {name: "Jane"}) CREATE (a)-[:knows]->(b)', function(err) {
if(err) throw err;
});

Querying a Relationship between Nodes

You can query a relationship between nodes in a graph in RedisGraph using the GRAPH.QUERY command with the MATCH clause. Here's an example in Node.js:

client.graph_query('mygraph', 'MATCH (a:person)-[:knows]->(b:person) RETURN a, b', function(err, results) {
if(err) throw err;
console.log(results);
});

Deleting a Relationship between Nodes

You can delete a relationship between nodes in a graph in RedisGraph using the GRAPH.QUERY command with the DELETE clause. Here's an example in Node.js

client.graph_query('mygraph', 'MATCH (a:person)-[r:knows]->(b:person) DELETE r', function(err) {
if(err) throw err;
});

Using the Bulk Data Loader

RedisGraph provides a bulk data loader that allows you to load large amounts of data into your graph. Here’s an example of how to use it:

redisgraph-bulk-loader mygraph --nodes nodes.csv --relations relations.csv

Using the GraphBLAS Operations

RedisGraph supports GraphBLAS operations, which allow you to perform complex matrix and vector operations on your graph data. These operations are accessible through the GRAPH.QUERY command.

Using the Cypher Query Language

RedisGraph uses the Cypher query language, which is a powerful and expressive language for querying graph data. It supports a wide range of operations, including pattern matching, filtering, aggregation, and graph algorithms.

RedisBloom

RedisBloom is a Redis module that provides probabilistic data structures, such as Bloom filters, Cuckoo filters, Count-Min sketch, and Top-K.

Bloom Filter vs Cuckoo Filter

Bloom filters and Cuckoo filters are both probabilistic data structures that are used to test whether an element is a member of a set. They are space-efficient and fast, but they allow a small probability of false positives.

Bloom filters are simpler and can handle any number of elements, but they don’t support deletion of elements. Cuckoo filters, on the other hand, are more complex and have a limit on the number of elements they can handle, but they support deletion of elements.

Here’s an example of how to use a Bloom filter in Node.js:

client.bf_add('myfilter', 'item', function(err, result) {
if(err) throw err;
console.log(result); // 1 means the item was added, 0 means it was already present
});

And here’s an example of how to use a Cuckoo filter:

client.cf_add('myfilter', 'item', function(err, result) {
if(err) throw err;
console.log(result); // 1 means the item was added, 0 means it was already present
});

Count-Min Sketch

Count-Min sketch is a probabilistic data structure that is used to determine the frequency of events in a stream. It is space-efficient and fast, but it allows a small probability of overestimating the frequency.

Here’s an example of how to use a Count-Min sketch in Node.js:

client.cms_incrby('mysketch', 'event', 1, function(err,count) {
if(err) throw err;
console.log(count); // the estimated frequency of the event
});

Top-K

Top-K is a probabilistic data structure that is used to maintain the top K frequently seen items. It is space-efficient and fast, but it allows a small probability of underestimating the frequency of items that are not in the top K.

Here’s an example of how to use Top-K in Node.js:

client.topk_add('mytopk', 'item', function(err, result) {
if(err) throw err;
console.log(result); // 1 means the item is in the top K, 0 means it is not
});

Redis Enterprise Cloud

Redis Enterprise Cloud is a fully managed cloud service for Redis. It provides high availability, infinite scaling, active-passive replication, conflict-free replication, and tiered storage.

High Availability

Redis Enterprise Cloud provides automatic failover, persistent storage, and disaster recovery to ensure that your data is always available.

Infinite Scaling

Redis Enterprise Cloud allows you to scale your database up and down without any downtime. It also supports sharding to distribute your data across multiple nodes.

Active-Passive Replication

Redis Enterprise Cloud supports active-passive replication, which allows you to have a standby replica of your database in a different region for disaster recovery.

Conflict-Free Replication

Redis Enterprise Cloud supports conflict-free replicated data types (CRDTs), which allow you to replicate data across multiple regions without conflicts.

Tiered Storage

Redis Enterprise Cloud supports tiered storage, which allows you to store hot keys in main memory and not-so-hot keys in flash memory. This can significantly reduce your memory costs.

In conclusion, Redis is a powerful tool that provides a wide range of features and capabilities. Whether you’re building a simple cache or a complex real-time analytics system, Redis has you covered. With its advanced features and modules, you can handle any data structure or use case with ease.

Redis vs Memcached

1. Structure and Data Structures They Use:

  • Redis: Redis (Remote Dictionary Server) is an open-source, in-memory data structure store used as a database, cache, and message broker. It supports various types of data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries, and streams. This wide range of data structures makes Redis quite flexible for a variety of use cases.
  • Memcached: Memcached is a high-performance, distributed memory object caching system intended for use in speeding up dynamic web applications by alleviating database load. It primarily uses a giant hash table and deals mainly with simple key-value stores. Unlike Redis, it doesn’t support a wide range of data structures.

2. Role of Multithreading:

  • Redis: Until version 6.0, Redis was primarily single-threaded, meaning that it used only one CPU core to execute all tasks. From version 6.0 onward, Redis introduced I/O threading for disk and network operations. However, the main functionality (such as executing commands) is still processed in a single thread.
  • Memcached: Memcached is multithreaded. It can use multiple CPU cores, which allows it to handle many network connections and read/write operations concurrently, potentially improving performance on multi-core machines.

3. Practical Scenarios and Preference:

  • Real-time analytics: Redis’s support for complex data structures, persistence, and built-in commands for manipulating and retrieving data makes it suitable for implementing real-time analytics. Memcached is not ideal for this use-case due to its lack of data structures and persistence.
  • Caching: Both Redis and Memcached are excellent for caching simple key-value data. However, if the cached data is not easily reproducible and you want persistence, Redis would be a better choice because of its snapshotting and AOF (Append Only File) capabilities.
  • Session storage: For session storage, Redis’s built-in support for data structures such as lists, sets, and hashes can be very beneficial. You could still use Memcached for session storage, but only if your needs are basic, such as storing simple string data.
  • Message queue systems: Redis’s support for Pub/Sub and data structures like Lists and Sorted Sets makes it suitable for implementing message queue systems. Memcached does not support Pub/Sub or these data structures, making it unsuitable for this scenario.
  • Rate limiting: Redis can handle complex operations like increments and expirations, which can be useful in rate limiting scenarios. Memcached can also handle this to a certain degree, but with less flexibility.

4. Speed:

Both Redis and Memcached are renowned for their speed, as they are both in-memory databases. However, the speed can depend on the specific use case and the data structures involved. In general, Redis performs better with complex data structures and operations, whereas Memcached may perform better with simple key-value operations due to its multithreaded nature.

5. Fault Tolerance and Data Loss:

  • Redis: Redis is more robust against data loss. It offers options for data persistence, such as RDB (periodic snapshots) and AOF (logging every write operation). Also, Redis has built-in replication, transactions, and different levels of on-disk persistence to minimize the chances of data loss.
  • Memcached: Memcached does not offer data persistence. If the server is restarted, all data is lost. This makes it less suitable for situations where data is hard to recreate or must be preserved over the long term.

6. Scalability:

  • Redis: Redis supports replication where one master can have multiple slaves. Redis Cluster can automatically partition data across multiple Redis nodes, offering increased scalability and performance.
  • Memcached: Memcached can also be easily scaled by adding more servers, as it is designed as a distributed system from the ground up. However, it lacks the built-in data partitioning that Redis Cluster provides.

In summary, the choice between Redis and Memcached depends on your specific use case and requirements. Redis’s versatility in supporting various data structures and offering data persistence makes it more flexible for complex applications, while Memcached’s simplicity and multithreading make it a good fit for cases that require straightforward, high-speed key-value caching.

--

--

Sanket Saxena
Sanket Saxena

Written by Sanket Saxena

I love writing about software engineering and all the cool things I learn along the way.

No responses yet