· via dev.to (home feed)
Redis 8.12-rc1 ships Roaring Bitmaps in core, cutting sparse bitmap memory from 24MB to 239KB
Redis 8.12-rc1 moves Roaring Bitmaps from a side project into the server core, with an opt-in representation and a new BITRANGEINTARRAY command. Independent tests show a 103x memory cut on sparse bitmaps; dense ones grow slightly.

Redis 8.12-rc1 brings Roaring Bitmaps into the server
Redis 8.12-rc1 folds a formerly separate project, redis-roaring, into the server itself. The release gives bitmaps an optional second internal representation that instances enable through the new bitmap-default-roaring setting, and it arrives alongside a command, BITRANGEINTARRAY, that returns the offsets of set bits in a range directly. According to a hands-on write-up on dev.to, the release notes present the change as a way to reduce memory and speed up bitmap operations — and measurements on sparse, realistically shaped data bear that out dramatically, with important caveats for dense data and existing code.
Sparse bitmaps shrink about a hundredfold
The dev.to author benchmarked a shape that mirrors a common production pattern: roughly 20,000 of 200 million possible user IDs being active on a given day. Concretely, 20,000 bits were set at pseudo-random offsets across a 200,000,000-bit range, twice, with the same random seed, so the legacy and roaring keys mark identical bits.
MEMORY USAGE reported 25,165,860 bytes — about 24MB — for the legacy bitmap and 244,837 bytes — about 239KB — for the roaring one, roughly 103 times smaller. OBJECT ENCODING distinguishes the two as raw and bitmap-roaring.
BITCOUNT, which scans the whole value, sped up as well: 1,040–1,056 requests per second on the legacy bitmap versus 14,706–15,152 on the roaring one across three benchmark runs, about a 14x gain. Per the write-up, that follows from the data structure itself: counting set bits means traversing its containers rather than scanning the value byte by byte.
Dense bitmaps see little benefit
Roaring Bitmaps compress clustered runs and sparse clusters. A bitmap with about half its bits set and no pattern offers nothing to compress. The author tested a 1,000,000-bit value with every even bit set — an easy case for run-length encoding — and it still came out slightly larger under roaring, 132,376 bytes versus 131,103 bytes, about 1% worse. BITCOUNT throughput on the dense pair was nearly the same, 23,622–25,000 req/s legacy against 26,087–27,778 req/s roaring, because a 125KB scan was already fast.
A distinct type, not a string encoding
The detail with the biggest operational weight: a roaring bitmap is not another encoding of the string type, as listpack or intset are encodings of other types. TYPE on a roaring key reports bitmap, and Redis enforces the boundary. String-oriented commands — GETRANGE, APPEND, SETRANGE, STRLEN — fail with WRONGTYPE on a roaring key, while the bit-oriented family, including GETBIT, BITCOUNT, BITFIELD and BITOP, behaves as before. BITOP even accepts a roaring key and a legacy string-backed bitmap in the same operation.
That boundary creates a silent break for a pattern that exists in real code: writing flags with SETBIT into a key that is later read back with GETRANGE. Once the new default is on, the first fresh key takes the roaring path and those string reads start failing.
Migration and edge behavior to know about
The bitmap-default-roaring setting is consulted only when a key is created. Switching it back off leaves existing roaring keys untouched, and the author found no command that converts a bitmap between representations in place — migrating means reading a key out and rebuilding it under the other setting.
BITRANGEINTARRAY is not limited to the new type. It works on legacy bitmaps and on plain strings, where it returns the set bits of the ASCII encoding, matching how Redis's other bit commands treat strings. It also never returns an error for bad input: nonexistent keys and inverted ranges produce empty arrays, a negative end is interpreted the way GETRANGE interprets it, and an end far past the bitmap's length is clamped. That is forgiving, but it means a range-arithmetic mistake, such as passing a negative value where a small positive one was intended, quietly returns the wrong number of results.
There is also a versioning trap. No official Docker image exists for 8.12-rc1 yet, and a build from the tagged source identifies itself through INFO server as version 8.11.240 — a whole minor version behind the release tag — which would mislead any script that gates features on the reported version.
Why it matters
Bitmaps keyed over huge ID spaces — daily active users, feature flags, deduplication sets — are a long-standing Redis pattern, and the sparse shape tested here is the common one. A hundredfold memory reduction and a 14x faster full scan could make some of those workloads affordable where they previously were not. The trade is workload-dependent, though: dense bitmaps pay a small penalty, existing keys cannot be converted in place, and the new type boundary can break string-command usage without warning. The numbers also come from a single independent benchmark rather than official Redis figures, so teams should validate against their own data shapes before enabling the default — and let the versioning settle before scripting around a release candidate.
- #redis
- #databases
- #data-structures
- #performance
- #memory-optimization