You are currently viewing Exploring the Fundamentals of Redis: CLI Functionality, Essential Commands, and Practical Projects

Exploring the Fundamentals of Redis: CLI Functionality, Essential Commands, and Practical Projects

Leveraging In-Memory Database Capabilities for Enhanced Project Performance

Redis has emerged as a widely adopted in-memory database solution, offering unparalleled benefits such as efficient caching and robust rate limiting mechanisms. In this comprehensive blog post, we will delve into the world of Redis, exploring its role as an in-memory database, understanding the compelling reasons behind its popularity, and delving into essential features that make it an indispensable tool. Join us as we embark on a journey to harness the true potential of Redis. Let’s dive in!

What is an in-memory database?

You can use Redis in a lot of ways. But there are two main reasons I can think of:

  1. You are creating an application where you want to make your code layer stateless. Why? – Because if your code is stateless, it is horizontally scalable. Therefore, you can use Redis as a central storage system and let your code handle just the logic.
  2. You are creating an application where multiple apps might need to share data. For example, what if somebody is trying to bruteforce your site at payments.codedamn.com, and once you detect it, you’d also like to block them at login.codedamn.com? Redis lets your multiple disconnected/loosely connected services share a common memory space.

Redis Basics

Redis is relatively simple to learn as there are only a handful of commands you’ll need to know. In the next couple sections, we’ll cover a few main Redis concepts and some useful common commands.

Redis has a CLI which is a REPL version of the command line. Whatever you write will be evaluated.

The above image shows you how to do a simple PING or hello world in Redis in one of my codedamn Redis course exercises (the course is linked at the end if you want to check it out).

This Redis REPL is very useful when you’re working with the database in an application and quickly need to get a peek into a few keys or the state of Redis.

Common Redis commands

Here are a few very commonly used commands in Redis to help you learn more about how it works:

SET

SET allows you to set a key to a value in Redis.

Here’s an example of how it works:

SET codeacademia "developer from india"

This sets the key codeacademia to the value developer from india.

GET

GET allows you to get the keys you’ve set.

Here’s the syntax:

GET codeacademia

This will return the string “developer from india” as we set above.

SETNX

This key will set a value only if the key does not exist. This command has a number of use cases, including not accidentally overwriting the value of a key which might already be present.

Here’s how it works:

SET key1 value1
SETNX key1 value2
SETNX key2 value2

After running this example, your key1 will have the value value1 and key2 as value2. This is because the second command will have no effect as key1 was already present.

MSET

MSET is like SET, but you can set multiple keys together in one command. Here’s how it works:

MSET key1 "value1" key2 "value2" key3 "value3"

Right now we are using key and value as the prefix for keys and values. But in reality when you write such code it’s easy to lose track of what is a key and what is a value in such a long command.

So one thing you can do is always quote your value using double quotes, and leave your keys without quotes (if they are valid keynames without quotes).

MGET

MGET is similar to GET, but it can return multiple values at once, like this:

MGET key1 key2 key3 key4

This will return four values as an array: value1value2value3 and null. We got key4 as null because we never set it.

DEL

This command deletes a key – simple enough, right?

Here’s an example:

SET key value
GET key # gives you "value"
DEL key 
GET key # null

INCR and DECR

You can use these two commands to increment or decrement a key which is a number. They are very useful and you’ll use them a lot, because Redis can perform two operations in one – GET key and SET key to key + 1.

This avoids roundtrips to your parent application, and makes the operation also safe to perform without using transactions (more on this later)

Here’s how they work:

SET favNum 10
INCR favNum # 11
INCR favNum # 12
DECR favNum # 11

EXPIRE

The EXPIRE command is used to set an expiration timer to a key. Technically it’s not a timer, but a kill timestamp beyond which the key will always return null unless it’s set again.

SET bitcoin 100
EXPIRE bitcoin 10

GET bitcoin # 100
# after 10 seconds
GET bitcoin # null

EXPIRE uses a little bit more memory to store that key as a whole (because now you have to also store when that key should expire). But you probably won’t ever care about that overhead.

TTL

This command can be used to learn how much time the key has to live.

Example:

SET bitcoin 100
TTL bitcoin # -1
TTL somethingelse # -2

EXPIRE bitcoin 5
# wait 2 seconds
TTL bitcoin # returns 3
# after 1 second
GET bitcoin # null
TTL bitcoin # -2

So what can we learn from this code?

  1. TTL will return -1 if the key exists but doesn’t have an expiration
  2. TTL will return -2 if the key doesn’t exist
  3. TTL will return time to live in seconds if the key exists and will expire

SETEX

You can perform SET and EXPIRE together with SETEX.

Like this:

SETEX key 10 value

Here, the key is “key”, the value is “value”, and the time to live (TTL) is 10. This key will get unset after 10 seconds.

Now that you have fundamental knowledge of basic Redis commands and how the CLI works, let’s build a couple of projects and use those tools in real life.

Build an API Caching System with Redis

In this intriguing project, we unveil the power of Redis in constructing an API caching system that empowers you to store and retrieve data from a third-party server, mitigating the risk of rate limitations. With caching as a fundamental component, your website’s responsiveness and speed will soar to new heights, ensuring a seamless user experience.

To facilitate your journey, we have crafted an interactive development environment on codedamn, allowing you to effortlessly construct the API caching solution using Node.js directly within your browser. Experience the magic of Redis and try our API caching lab for free!

For those seeking a ready-made solution without the need for building from scratch, we present the core logic implemented in Node.js:

const express = require('express')
const path = require('path')
const fetch = require('node-fetch')
const redis = require('./redis-client')

const app = express()

app.use(express.json())

app.post('/data', async (req, res) => {
	const repo = req.body.repo

	const value = await redis.get(repo)

	if (value) {
		// means we got a cache hit
		res.json({
			status: 'ok',
			stars: value
		})

		return
	}

	const response = await fetch(`https://api.github.com/repos/${repo}`).then((t) => t.json())

	if (response.stargazers_count != undefined) {
		await redis.setex(repo, 60, response.stargazers_count)
	}

	res.json({
		status: 'ok',
		stars: response.stargazers_count
	})
})

app.get('/', (req, res) => {
	res.sendFile(path.join(__dirname, 'index.html'))
})

app.listen(process.env.PUBLIC_PORT, () => {
	console.log('Server ready')
})

Let’s see what’s happening here:

  • We try to get the repo (which is the passed repo format – facebook/react) from our Redis cache. If present, great! We return the star count from our redis cache, saving us a roundtrip to GitHub’s servers.
  • If we don’t find it in cache, we do a request to GitHub’s servers, and get the star count. We check if the star count is not undefined (in case a repo doesn’t exist/is private). If it has a value, we setex the value with a timeout of 60 seconds.
  • We set a timeout because we don’t want to serve stale values over time. This helps us refresh our star count at least once a minute.

Rate limiting API with Redis

In this compelling project, we delve into the critical task of fortifying specific endpoints against malicious activities by imposing rate limits and enforcing IP-based blocking mechanisms. By strategically implementing these measures, we shield sensitive API endpoints, such as login functionalities, from potential abuse or overwhelming requests from a single source.

Our lab focuses on IP address-based rate limiting, providing you with a hands-on experience to explore and master this technique. Head over to codedamn, where you can engage in this codelab at no cost, and witness the powerful impact of rate limiting in action.

For those seeking a ready-made solution, devoid of the development process, we present the core logic implemented in Node.js:

const express = require('express')
const path = require('path')
const fetch = require('node-fetch')
const redis = require('./redis-client')

const app = express()

app.use(express.json())

app.post('/api/route', async (req, res) => {
	// add data here
	const ip = req.headers['x-forwarded-for'] || req.ip

	const reqs = await redis.incr(ip)
	await redis.expire(ip, 2)

	if (reqs > 15) {
		return res.json({
			status: 'rate-limited'
		})
	} else if (reqs > 10) {
		return res.json({
			status: 'about-to-rate-limit'
		})
	} else {
		res.json({
			status: 'ok'
		})
	}
})

app.get('/', (req, res) => {
	res.sendFile(path.join(__dirname, 'index.html'))
})

app.listen(process.env.PUBLIC_PORT, () => {
	console.log('Server ready')
})

Let’s understand this code block:

  • We try to extract the IP from the x-forwarded-for header (or you can use req.ip as we are using express)
  • We INCR the IP address field. If our key in Redis never existed, INCR would automatically set it to 0 and increment, that is finally set it to 1.
  • We set the key to expire in 2 seconds. Ideally you’d want a larger value – but this is what the codedamn challenge specified above, so there we have it.
  • Finally we check the request counts, if they are greater than a certain threshold, we block the request from reaching the main function body.

More on Redis

Redis is much more than what we have learned so far. But the good thing is that we have learned enough to start working with it already!

In this section, let’s cover a few more Redis fundamentals.

Redis is single threaded

Redis runs as a single threaded process, even on a multiple core system supporting multi threading. This is not a performance nightmare, but a safety measure against inconsistent read/writes in a multi threaded environment.

If Redis were multi threaded, to ensure thread safety when accessing a single key, you’d eventually have resolved to some locking mechanism, which probably would perform worse than single threaded/sequential access anyway.

Redis Transactions

Of course, you cannot do everything in Redis in a single command. But you can surely ask it to do a block of commands in a single go (that is, nobody else talks to Redis while it is executing that block). You can do that using the MULTI command.

Here’s how that works:

MULTI
SET hello world
SET yo lo
SET number 1
INCR number
EXPIRE hello 10
EXPIRE yo 5
EXEC

This will perform all these operations in one go, that is it will not run anything at all after MULTI, and will run everything at once the moment it sees the EXEC keyword.

Redis includes support for lists and sets for more advanced use cases. You can also use Redis as a broadcasting service where you publish to a channel and others who have subscribed to the channel receive a notification. This is very useful in multi-client architecture.

Leave a Reply