Skip to main content
PProfileMatch

Redis Data Types and Core Commands with redis-cli Examples

System Design & ArchitectureLesson 1 of 45 min read

What you'll learn

  • Choose between strings, hashes, lists, sets and sorted sets for a given problem
  • Use the core commands for each type and read the replies in redis-cli
  • Design readable key names and set expirations on keys
  • Recognise the two errors beginners see most: WRONGTYPE and non-integer INCR

On this page

  1. What Redis is, in one paragraph
  2. Strings and counters
  3. Hashes, lists, sets and sorted sets
  4. Key design, expiry and why you should not run KEYS
  5. Common mistakes and the errors they produce
  6. How this shows up in real projects and interviews

A pub is running its monthly quiz night, and the quizmaster wants a live leaderboard on a screen: teams, their tables, their members and their scores, updating as answers are marked. You could build it in SQL with an ORDER BY on every refresh, but the screen refreshes every few seconds and every table is hammering the same rows. Redis is built for exactly this shape of problem: small pieces of data, read and updated constantly, kept in memory.

What Redis is, in one paragraph

Redis is an in-memory data structure server. Clients connect over TCP, send a command such as ZADD or HSET, and get a reply. Unlike a plain key-value store, the value under a key is a typed data structure with its own operations. Commands are executed one at a time by a single thread, which is why each individual command is atomic. Everything lives in RAM, which makes it fast, and also means capacity is bounded by memory (see the last lesson for what that implies for durability).

Strings and counters

The simplest type is the string, which holds text, numbers or any bytes. Run redis-cli to open a prompt (the examples assume a local server on the default port). Keys use colons as a convention to group related things, which keeps quiz:2031-03-14:status readable at a glance.

redis-cli: strings
127.0.0.1:6379> SET quiz:2031-03-14:status open
OK
127.0.0.1:6379> GET quiz:2031-03-14:status
"open"
127.0.0.1:6379> INCR quiz:2031-03-14:attendees
(integer) 1
127.0.0.1:6379> INCRBY quiz:2031-03-14:attendees 5
(integer) 6
127.0.0.1:6379> GET quiz:2031-03-14:attendees
"6"
127.0.0.1:6379> TYPE quiz:2031-03-14:attendees
string
127.0.0.1:6379> TTL quiz:2031-03-14:attendees
(integer) -1
127.0.0.1:6379> TTL no:such:key
(integer) -2

Two things to notice. INCR creates the key at 0 if it is missing and returns the new number, so a counter needs no setup. And TTL returns -1 for a key that exists but has no expiry, and -2 for a key that does not exist, which are handy to remember when you debug.

Hashes, lists, sets and sorted sets

Here is the quiz modelled with the other four types. A hash is a small record of named fields, a list is an ordered sequence, a set holds unique members, and a sorted set keeps unique members ordered by a numeric score, which makes it ideal for a leaderboard.

redis-cli: a team and the leaderboard
127.0.0.1:6379> HSET team:7 name "Quizzy Rascals" table 12 score 0
(integer) 3
127.0.0.1:6379> HGET team:7 table
"12"
127.0.0.1:6379> HINCRBY team:7 score 4
(integer) 4

127.0.0.1:6379> RPUSH round:1:questions q1 q2 q3
(integer) 3
127.0.0.1:6379> LPOP round:1:questions
"q1"
127.0.0.1:6379> LRANGE round:1:questions 0 -1
1) "q2"
2) "q3"

127.0.0.1:6379> SADD team:7:members asha ben
(integer) 2
127.0.0.1:6379> SADD team:7:members asha
(integer) 0
127.0.0.1:6379> SISMEMBER team:7:members ben
(integer) 1
127.0.0.1:6379> SCARD team:7:members
(integer) 2

127.0.0.1:6379> ZADD leaderboard 12 team:7 18 team:3 9 team:5
(integer) 3
127.0.0.1:6379> ZREVRANGE leaderboard 0 1 WITHSCORES
1) "team:3"
2) "18"
3) "team:7"
4) "12"
127.0.0.1:6379> ZINCRBY leaderboard 10 team:5
"19"
127.0.0.1:6379> ZREVRANK leaderboard team:5
(integer) 0

The integers after HSET, SADD and ZADD count how many new items were added, which is why re-adding asha returns 0. ZREVRANGE lists from the highest score; since Redis 6.2 the same thing can be written ZRANGE leaderboard 0 1 REV WITHSCORES, and the older command still works. Notice that team:5 jumped from last to first with one ZINCRBY, and the rank query is a single cheap call. That is the pub's screen done.

TypeThink of it asCore commandsGood fit
StringA value or counterSET GET INCR EXPIRECached blobs, counters, flags
HashA small recordHSET HGET HINCRBY HGETALLUser profile, session fields
ListA queue or recent itemsLPUSH RPUSH LPOP LRANGERecent activity, simple work queues
SetUnique membersSADD SISMEMBER SINTER SCARDTags, who-has-seen, de-duplication
Sorted setMembers ordered by scoreZADD ZRANGE ZINCRBY ZRANKLeaderboards, time-ordered indexes

The same operations from Python with the redis-py client look like this. With decode_responses=True replies come back as str rather than bytes, and sorted-set scores come back as floats. This snippet needs a running server, which I could not start here, so treat the printed result as the documented behaviour.

leaderboard.py
import redis

r = redis.Redis(host="localhost", port=6379, decode_responses=True)

r.zadd("leaderboard", {"team:7": 12, "team:3": 18, "team:5": 9})
r.zincrby("leaderboard", 10, "team:5")

print(r.zrevrange("leaderboard", 0, 1, withscores=True))

Output

[('team:5', 19.0), ('team:3', 18.0)]

Key design, expiry and why you should not run KEYS

  • Name keys like a schema: entity:id:attribute, for example team:7:members. A consistent scheme is the only "table definition" Redis has, so write it down.
  • Set a TTL on anything that should not live forever: EXPIRE key 3600, or SET key value EX 3600 in one step. Plain SET on an existing key removes its TTL, which surprises people.
  • Prefer `SCAN` to `KEYS`: KEYS pattern walks the whole keyspace in one blocking call. SCAN returns a cursor and a batch at a time, and you repeat until the cursor comes back as 0. It may return a key more than once, so your code should tolerate duplicates.
  • Keep values and collections modest: one list or hash with millions of entries makes some operations slow and deletion painful. Split by day, by shard or by owner.

Common mistakes and the errors they produce

  • Using the wrong command for the type. Calling LPUSH on a key that holds a string replies (error) WRONGTYPE Operation against a key holding the wrong kind of value. Check with TYPE key.
  • Incrementing a non-number. INCR on a key holding "open" replies (error) ERR value is not an integer or out of range.
  • **Running KEYS * on a busy instance.** Nothing errors; every other client just stalls while the scan runs. Use SCAN.
  • Forgetting expiries. Memory climbs slowly until it hits the limit, then writes start failing or old data gets evicted (see the last lesson). Decide a lifetime for every key family.
  • Storing everything as one giant string of JSON. Updating a single field means reading and rewriting the whole value, and two writers overwrite each other. A hash lets you update one field atomically with HSET.

How this shows up in real projects and interviews

Interviewers use Redis types as a shorthand for design thinking: "how would you build a leaderboard / unique visitor counter / recent-items list?" The expected answers are a sorted set, a set (or HyperLogLog, if approximate is acceptable) and a capped list. On real teams, the first Redis use is usually a cache or a counter, and the skill that matters most is choosing the right type so the work happens inside Redis in one command, instead of the application fetching data and sorting it itself. Browse the jobs listing and you will see Redis often named next to caching and messaging.

Try it yourself

Model a bus-stop arrivals board in Redis: one hash per bus (route, delay in minutes), a sorted set of upcoming arrival times keyed by bus id, and a set of buses currently out of service. Write the redis-cli commands to add three buses, bump one bus's delay by 4, fetch the next two arrivals, and test whether a bus is out of service.

Show a hint

Use HINCRBY for the delay, ZADD with a Unix timestamp as the score, ZRANGE ... 0 1 WITHSCORES for the next two, and SISMEMBER for the out-of-service check.

Key takeaways

  • Redis stores typed structures (strings, hashes, lists, sets, sorted sets), and each command operates on one type atomically.
  • A sorted set keeps members ordered by score, which turns leaderboard queries into single cheap commands.
  • Name keys consistently, give transient data a TTL, and use SCAN rather than KEYS on anything real.
  • WRONGTYPE and value is not an integer are the typical early errors; TYPE key tells you what you actually stored.

Test yourself

A trick from this lesson's ride: Need a ranking? Pick the structure that keeps members sorted by a number: the score does the sorting. Four more tricks like it wait along the route, each followed by a question.

Ride this lesson

Frequently asked questions

What data types does Redis support?

The core types are strings, hashes, lists, sets and sorted sets. Redis also provides streams, bitmaps, HyperLogLog and geospatial indexes, and modules can add more. Each type has its own commands, so choosing the right one lets Redis do the work in a single atomic operation.

What is the difference between a Redis set and a sorted set?

A set stores unique members with no order. A sorted set also stores unique members but attaches a numeric score to each and keeps them ordered by it, so you can fetch ranges by rank or score. Use a sorted set for leaderboards and time-ordered indexes.

Why should I avoid the KEYS command in production?

KEYS scans the entire keyspace in a single blocking call, and Redis executes commands on one thread, so every other client waits. SCAN returns results in small batches with a cursor and does not block for long.