B1.3 · dive 1 of 4 — the engine change
One allocation, two reads.
The retired dict resolved one key in four memory reads across three
allocations. Valkey 8.1 inverts the layout around the cache line: a 64-byte bucket holds up to seven
entries, key and value embed in one object, and a lookup costs two memory reads.
The design is Viktor Söderqvist's open-addressing, cache-line-bucket table. The chapter is content/bcs1.3.md, and the threshold detail below is read from the 8.1.8 tree the chapter built.
The retired dict
content/bcs1.3.md · WhatThe old table was chained: a bucket array of pointers, each entry a separately allocated
dictEntry of three pointers, the key its own allocation — four memory reads to resolve one
key, plus two per collision hop, plus a second table during rehash.
Its cost ledger, per key: a bucket-pointer share, a 24-byte dictEntry plus
allocation overhead, a separate key allocation, and — with a TTL — a second entry in the expires dict.
Every line of that ledger is a pointer to chase, and every pointer is a cache line the CPU may not have.
The cache-line bucket
Söderqvist 2025 · object.c:47The 8.1 design inverts the layout around the cache line: buckets of 64 bytes holding up
to seven entries, an 8-byte metadata section — one child-pointer bit, seven presence bits, and seven
one-byte secondary hashes built from the bits not spent addressing the bucket, so mismatched slots are
skipped without touching their keys at a 1-in-256 false-positive rate. The dictEntry is
gone; key and value embed in the serverObject, one allocation per entry, two memory reads
per lookup.
Incremental rehash, the SCAN guarantee, and random sampling — the features
that ruled out adopting Swiss tables wholesale — are kept by construction. And one source detail matters
to ID design: every key embeds regardless of length, and keys of 128 bytes or more pre-reserve an expire
slot (KEY_SIZE_TO_INCLUDE_EXPIRE_THRESHOLD, object.c:47 in the 8.1.8 tree this
chapter built).
The headline, reproduced
bench/valkey-id/valkey_id_bench.outThe new table charges a share of 64 bytes across up to seven entries and one object that already contains key, value, and, when set, an inline 8-byte expire. The publication's headline is roughly 20 bytes saved per pair, roughly 30 with a TTL — and the claims reproduce on this project's own keys: 23 to 39 bytes saved, 29 on the TTL row.
fmt keylen redis7 valkey81 saved
brd14 14 88 65 23
uuid16 16 104 65 39
brd14+ttl 14 128 99 29
The staircase
content/bcs1.3.md · What · jemalloc 5.3.0jemalloc allocates in 16-byte classes in this range, so key length stops mattering within a class and matters sharply at its edges: an identifier's storage cost is a staircase, and the design question is which step each format stands on. The five measured key lengths plot the steps:
The staircase is the durable model: the 9.x line extends the same bucket structure with SIMD lookups and batch prefetching, so class-edge thinking outlives this release.
References
sources · relatedSources
- Söderqvist — A new hash tablethe design this dive reads: buckets, metadata, the kept guarantees
- Valkey 8.1.0 GAthe release the hash table shipped in; the headline savings
Related
- B1.3 — the module hubthe chooser, settled by measurement
- B1 — Ideas Behindthe chapter landing
- BCS — the course homethe law, the id anatomy, the chapter map
- Redis Patterns Appliedthe substrate patterns these keys feed