NHT

Redis Bitmaps: Track Millions of Active Users with Just 122KB of RAM

A practical system-design guide to tracking user online/offline status using Redis Bitmaps. Calculate RAM requirements, learn core commands, and solve real-world production pitfalls.

Nguyen Hoang TuanNguyen Hoang Tuan3 Jun 20268 min read

One of the most common requirements in modern social apps, chat services, and collaborative dashboards is tracking user online/offline status. At first glance, it feels like a simple Boolean flag. However, when your system scales to millions of active users, a naive implementation can quickly bring your database to its knees.

This guide explores a textbook system-design solution: Redis Bitmaps. By representing user status as individual bits, you can track millions of users in a fraction of a megabyte with O(1) read and write speeds.


The Problem: The High Cost of the is_online Column

In a traditional relational database, you might add an is_online column to the users table or store a timestamp like last_active_at. Every time a user loads a page, clicks a button, or sends a WebSocket handshake, your application executes a query:

UPDATE users SET is_online = 1, last_active_at = NOW() WHERE id = 12345;

This works fine for a few thousand users. But at scale, this approach runs into major bottlenecks:

  • Write Amplification: Databases are optimized for structured reads, but writing is relatively slow and involves updating indices, writing to transaction logs (WAL), and hitting disk storage.
  • Connection Pool Exhaustion: Millions of active users sending heartbeats every few seconds will quickly saturate your database connection pool.
  • Lock Contention: Frequent updates to the same user rows lead to locking overhead, degrading the performance of other critical features.

The Solution: What is a Redis Bitmap?

Instead of treating online status as an entire row or table entry, we can treat it as a single binary digit (bit):

  • 1 represents Online
  • 0 represents Offline

Redis provides a specialized set of commands to treat string values as arrays of bits. This feature is called Bitmaps. In a Redis Bitmap, the offset in the bit array corresponds to the user's ID, and the bit value (0 or 1) represents their status.

Because Redis operates entirely in-memory, bitwise operations are incredibly fast—taking O(1) constant time for setting and retrieving status.


The Math: Calculating Memory Footprint

To see why Bitmaps are so powerful, let's break down the memory math. In a Bitmap:

Memory usage = 1 bit per user

Let's calculate the RAM needed to store the online status of 1,000,000 users:

  1. Total Bits: 1,000,000 bits
  2. Convert to Bytes: 1,000,000 bits / 8 = 125,000 bytes
  3. Convert to Kilobytes: 125,000 bytes / 1024 ≈ 122 KB

Here is how memory usage scales at different user thresholds:

| User Count | Total Bits | Memory Size (KB/MB) | | :--- | :--- | :--- | | 1,000,000 | 1,000,000 bits | ~122 KB | | 5,000,000 | 5,000,000 bits | ~610 KB | | 10,000,000 | 10,000,000 bits | ~1.2 MB | | 50,000,000 | 50,000,000 bits | ~6.0 MB |

Even if you have 50 million registered users, your entire active-status index fits into 6 megabytes of RAM. That is a trivial cost for any modern server.


Implementation: Core Redis Commands

Trialling Bitmaps in Redis requires only four simple commands: SETBIT, GETBIT, and BITCOUNT. Let's assume we use the key user:online:bitmap.

1. Marking a User Online

When user 12345 connects (e.g., establishes a WebSocket connection), set the bit at offset 12345 to 1:

SETBIT user:online:bitmap 12345 1

2. Marking a User Offline

When user 12345 disconnects or logs out, set the bit back to 0:

SETBIT user:online:bitmap 12345 0

3. Checking User Status

To determine if user 12345 is currently online:

GETBIT user:online:bitmap 12345
# Returns 1 (Online) or 0 (Offline)

4. Counting All Online Users

To get the total number of users currently online (the count of all 1 bits):

BITCOUNT user:online:bitmap
# Returns the total active user count

Production Pitfalls & Solutions

While Bitmaps are highly efficient, bringing them into a real production system requires navigating a few critical gotchas.

Pitfall 1: String-based IDs (UUIDs) and Sparse IDs

The bit offset must be an integer. If your system uses UUIDs (like e5b02bcf-a8e5-4d7a...), you cannot use them directly as offsets.

Additionally, Redis allocates memory dynamically based on the highest offset. If you have only two users with IDs 1 and 99,999,999, Redis will allocate memory for all 100 million bits in between—consuming around 12MB of RAM just to store two statuses.

Solutions:

  • Auto-Increment IDs: Ensure you only use sequential integer IDs.
  • ID Mapping: If you must use UUIDs, keep a Redis Hash (or an internal service) that maps UUIDs to a sequential integer series starting from 0.

Pitfall 2: Silent Disconnects (The Heartbeat Problem)

What happens if a user's phone dies, they lose internet connectivity, or their app crashes? The application server will not be able to execute SETBIT user:online:bitmap <user_id> 0. That user will appear online indefinitely.

Furthermore, individual bits in a Redis Bitmap do not support Time-to-Live (TTL). You cannot set an expiration time on a single bit.

Solutions:

  • WebSocket Heartbeats: Require clients to ping the server every 1-5 minutes. Every time the server receives a heartbeat, it runs SETBIT <id> 1.
  • Key Rotation: To clean up users who disappeared without sending an offline signal, clear or rotate your bitmaps periodically.

Scaling Up: Time-Bucketed Bitmaps

To solve the heartbeat cleanup issue and gain deeper analytics, a common pattern is Time-Bucketed Bitmaps (e.g., daily or hourly keys).

Instead of one global key, store activity in keys containing dates or hours:

  • user:online:2026-06-03
  • user:online:2026-06-03:10h

Every time a user pings, set their bit in the current hourly/daily bitmap:

SETBIT user:online:2026-06-03:10h 12345 1
EXPIRE user:online:2026-06-03:10h 86400  # Set key TTL to 24 hours

By adding a TTL to the whole key, inactive user data is automatically cleaned up by Redis when the key expires, avoiding memory leaks.

Real-World Benefit: Computing DAU and MAU

Storing daily active users in separate bitmaps allows you to compute analytical queries instantly using bitwise operations. For example, to calculate the Daily Active Users (DAU) across three days, you can run BITOP OR to merge the keys and get the total count:

# Perform bitwise OR on 3 days of bitmaps and save the result to a temporary key
BITOP OR dau:three_days user:online:2026-06-01 user:online:2026-06-02 user:online:2026-06-03

# Count the unique users who logged in during those three days
BITCOUNT dau:three_days

This operation runs in-memory on the Redis server and takes only milliseconds, saving you from complex, slow SQL queries on raw event logs.


Summary & Recommendations

Redis Bitmaps are a fantastic choice when:

  • You need high-speed, low-overhead binary status tracking (e.g., online/offline, feature flags, newsletter subscriptions).
  • Your user IDs are dense, sequential integers.
  • You want to perform quick analytics like DAU/MAU.

However, avoid Bitmaps if:

  • You need to track complex states (e.g., "Away", "Busy", "Do Not Disturb").
  • You must store metadata, such as the user's IP address, device type, or the exact millisecond they went offline.
  • Your ID space is sparse or relies entirely on UUIDs without an intermediate mapping layer.

If you are currently scaling your backend and database, building a resilient base is key. Learn more about structure in NestJS Modular Architecture: Building Production APIs That Scale, or ensure your public-facing apps are discovery-ready using Next.js SEO Checklist for the App Router.

Facing performance issues or scaling challenges?

I specialize in building low-latency map infrastructure, real-time streaming pipelines (Kafka, ClickHouse), and highly optimized backend systems. Let's work together to scale your product.

Let's Work Together

Written by

Nguyen Hoang Tuan

Nguyen Hoang Tuan

Full-stack developer focused on practical backend architecture, web performance, and production delivery.

Related Articles