Skip to content

What is Redis?

Verified on Redis 8.10.2 (redis:8.10.2-alpine), ioredis 6.0.0, Node 24, on an Apple-silicon Mac. Every output block below is copied from a real run.

Redis stores every key and its associated value directly in RAM. When your application sets a key, the data lives in memory — no disk seek, no page fault, no file-system overhead. When it reads the key back, the answer comes from the same memory. This is why Redis latency is consistently sub-millisecond even under heavy load.

Each value is typed. Redis is not a simple string bag — it understands data structures natively: Strings, Lists, Hashes, Sets, Sorted Sets, Streams, and more. The server can operate on those structures atomically, which means you can append to a list, increment a counter, or pop from a queue without any application-level locking.

Command execution is single-threaded — the process is not

Section titled “Command execution is single-threaded — the process is not”

Redis executes commands on one thread. That thread reads a command from a socket, runs it to completion, writes the reply, then moves to the next one. Nothing else is allowed to touch the keyspace while a command runs.

“Redis is single-threaded” is the half-truth people repeat, so look at the real process. This is ps -T (one line per thread) inside the container, right after start:

PID USER TIME COMMAND
1 redis 0:00 redis-server *:6379
26 redis 0:00 {bio_close_file} redis-server *:6379
27 redis 0:00 {bio_aof} redis-server *:6379
28 redis 0:00 {bio_lazy_free} redis-server *:6379
29 redis 0:00 {jemalloc_bg_thd} redis-server *:6379
30 redis 0:00 {jemalloc_bg_thd} redis-server *:6379

Thread 1 runs your commands. The bio_* threads do background work that must never block it (closing files, fsync for the AOF, freeing big values lazily — see UNLINK later), and jemalloc_bg_thd is allocator housekeeping. INFO threads shows the I/O side, and io-threads is the knob:

cli/intro-what-is-redis-1.redis
127.0.0.1:6379> PING
PONG
127.0.0.1:6379> CONFIG GET io-threads
1) "io-threads"
2) "1"
127.0.0.1:6379> SET language "Redis"
OK
127.0.0.1:6379> TYPE language
string

Starting a second throwaway server with redis-server --io-threads 4 adds more I/O threads (INFO threads then lists io_thread_0 to io_thread_3), and Redis 8 reworked how they read and write client sockets. Even then, command execution stays on the main thread — I/O threads only parse and write bytes. Source: Redis 8.0 what’s new and the io-threads section of the shipped redis.conf.

This design has two consequences:

  • Speed — no lock contention on the data, no context switch between commands. One core is enough for hundreds of thousands of simple commands per second.
  • Each command is atomic — while one command runs, no other command can observe half of it. That is per command. Two commands in a row are two separate turns of the loop, and another client can run in between (you will prove that below).
 
Who runs what: one command thread, separate I/O and background threads

The phrase “Redis is a cache” is common but incomplete. A plain cache maps keys to opaque blobs. Redis maps keys to rich data structures you can manipulate on the server side:

TypeDescription
StringText, numbers, or raw bytes up to 512 MB
ListOrdered sequence; push/pop from either end
HashField-value map inside a single key
SetUnordered collection of unique members
Sorted SetSet where each member carries a numeric score
StreamAppend-only log of timestamped entries

Because operations happen server-side, you avoid round trips. Instead of GET → modify locally → SET, you do LPUSH or ZINCRBY in one network call.

For years the extra capabilities lived in modules you installed separately, and “Redis Stack” was the bundle that shipped them together. Redis 8 merges the two: what used to be Redis Community Edition is now called Redis Open Source, and the formerly modular features are built into the server itself. Ask the real server what it loaded (trimmed to names and versions):

Terminal window
127.0.0.1:6379> MODULE LIST # names + ver only; each entry also has path/args
vectorset ver 1
bf ver 81001 (RedisBloom, path .../redisbloom.so)
timeseries ver 81001
search ver 81001
ReJSON ver 81000

And the built-in JSON type works out of the box, with no install step:

cli/intro-what-is-redis-2.redis
127.0.0.1:6379> JSON.SET user:1 $ '{"name":"Ada"}'
OK
127.0.0.1:6379> JSON.GET user:1
"{\"name\":\"Ada\"}"
127.0.0.1:6379> TYPE user:1
ReJSON-RL
127.0.0.1:6379> DEL user:1
(integer) 1
Also built in since Redis 8What it gives you
JSONStore and update JSON documents in place with JSONPath
Search and querySecondary indexes, full-text search, and vector search over hashes or JSON
Time seriesDownsampling, aggregation, and retention for timestamped samples
ProbabilisticBloom filter, Cuckoo filter, Count-Min sketch, Top-K, t-digest
Vector set (beta)A native type for vector similarity search

Redis 8 also landed what the project calls its largest performance jump — the release notes cite over 30 optimizations, up to 87% lower command latency and up to 18% faster replication (Redis 8.0 what’s new; these are vendor numbers, not measured here) — plus the three hash commands you will meet in the Hashes lesson.

Licence and forks — what “Redis” means in 2026

Section titled “Licence and forks — what “Redis” means in 2026”

Which server you run matters for legal and for compatibility reasons, so know the timeline:

DateEventSource
up to Redis 7.2BSD-3-Clause open source—
2024-03-28Redis moves to dual source-available RSALv2 / SSPLv1redis.io: dual source-available licensing
8 days laterLinux Foundation announces Valkey, a BSD-licensed fork of Redis 7.2.4 (first release 7.2.5)valkey.io: history
2025-05-01AGPLv3 added as a third licence option starting with Redis 8redis.io: AGPLv3

This course teaches Redis Open Source 8.x. Valkey speaks the same wire protocol and its core commands (strings, lists, hashes, sets, sorted sets, streams, MULTI, Lua) match the 7.2 line, but it diverges on newer features. Commands introduced in Redis 8 — HGETEX, HSETEX, HGETDEL (8.0), XACKDEL (8.2), SET ... IFEQ and DELEX (8.4) — are called out with their version where they appear, so you know when a lesson will not run on an older server or on Valkey. I did not run a Valkey container for this lesson: the compatibility statement comes from the docs above, not from a test.

Use caseRedis feature
CacheTTL-based expiry on any key
Session storeHash per session ID, fast reads
Task queueLPUSH / BRPOP on a List
LeaderboardSorted Set with ZADD / ZRANK
Rate limiterINCR + EXPIRE on a counter key
Pub/Sub messagingPUBLISH / SUBSCRIBE channels

Run PING to confirm the server is alive, then use TYPE to inspect any key (the first session above did exactly that):

PING

An assistant writes a page-view counter and justifies it with the sentence everyone knows:

🐛 Spot the bug

import { Redis } from 'ioredis';
const redis = new Redis(process.env.REDIS_URL!);
// Redis is single-threaded, so this read-modify-write is safe.
async function recordVisit() {
const n = Number((await redis.get('visits')) ?? 0);
await redis.set('visits', n + 1);
}
await Promise.all(Array.from({ length: 50 }, recordVisit));
console.log(await redis.get('visits'));
Show the answer

Run against the 8.10.2 container, the last line prints 1, not 50. “Single-threaded” only guarantees that each of the two commands runs alone; GET and SET are two turns of the event loop, and the other 49 GETs all ran before the first SET landed. It is the classic lost update. The fix is to make it one command:

src/intro-what-is-redis-visits.ts
// @run
import { Redis } from 'ioredis';
const redis = new Redis(process.env.REDIS_URL!);
await redis.del('b1:visits');
await Promise.all(Array.from({ length: 50 }, () => redis.incr('b1:visits')));
console.log(await redis.get('b1:visits')); // "50"
await redis.del('b1:visits');
redis.disconnect();

The same test printed 50. You will meet INCR properly in the Counters lesson.

Think of Redis as one very fast clerk at a single counter. Customers (clients) queue up; the clerk finishes one request completely before looking at the next, so nobody ever sees a half-finished request. Helpers in the back room (background threads) shred paper, sync the ledger to disk and free shelves, but they never serve the counter. The clerk is fast because the shelves are RAM, and the counter rule is why each command is atomic. A customer who needs two separate requests treated as one must ask for it explicitly: a single multi-step command, MULTI, or a Lua script.

ChoiceBenefitCost
Store data in RAM (in-memory)Sub-millisecond latency; reads and writes are far faster than diskRAM costs much more than disk, and the whole dataset must fit in available memory
Single-threaded command executionNo lock contention; every command is atomic by construction; easy to reason aboutOne slow command stalls every client; one instance executes commands on one core (scale out with sharding, not threads)
Redis Open Source 8 (RSALv2 / SSPLv1 / AGPLv3 options)Built-in JSON, search, time series, probabilistic typesNot BSD any more; check the licence with your legal team, or evaluate Valkey (BSD) if that matters
  • “Redis is single-threaded.” Command execution is. The process has background threads (ps -T) and optional I/O threads (io-threads). Getting this wrong makes people believe multi-command sequences are atomic, or that Redis cannot use more than one core at all.
  • “Redis is just a cache.” It is a data-structure server. Whether it is only a cache depends on your persistence and maxmemory configuration, not on the software.
  • “Redis is open source, so it is BSD.” It was, until 7.2. Today there are three licence options (RSALv2, SSPLv1, AGPLv3); Valkey is the BSD fork.
  • “Redis Stack is a separate product you install.” Not since Redis 8 — MODULE LIST on the plain image shows JSON, search, time series and Bloom already loaded.
  • Assuming Redis is durable by default — the defaults favor speed, not durability. If you need data to survive a restart, you must configure persistence (RDB/AOF) yourself.
  • Letting the dataset grow without setting maxmemory — because everything lives in RAM, an unbounded dataset with no eviction policy configured can use up all available memory and OOM the machine.
  • Assuming single-threaded means “slow” — the single command thread is exactly what makes Redis fast, since there is no context switching. But it also means one expensive command (e.g. KEYS * on a large dataset) blocks every other client until it finishes. The First Commands lesson measures that.

Real-world example

Stack Overflow ran an L1 (local memory)/L2 (Redis) cache in front of SQL Server: a request checks local cache, then Redis, and only queries the database on a miss — at a reported scale of roughly 160 billion Redis operations a month across their fleet (Stack Overflow: The Architecture – 2016 Edition, Nick Craver, 2016 — a ten-year-old number, treat it as history).

Why is Redis so fast compared to disk-based databases?
What does 'Redis is single-threaded' actually guarantee?
50 concurrent workers each run GET visits, then SET visits (n+1). The final value is 1. Why?
Which data type would you use to implement a leaderboard with ranked scores?
On the plain redis:8.10.2 image, what does MODULE LIST show?
Which licence option was added for Redis 8 (2025-05-01)?