Posts Caches
Redis vs Valkey vs Dragonfly vs KiviDB
Redis 8.10, Valkey 9.1, Dragonfly 2.0 and KiviDB 1.1 on a 64-core Graviton3: SET/GET at 100 bytes and 10 KB, pipelining, and memory per key, with io_uring allowed through Docker's seccomp profile. One of KiviDB's founders asked for this run and agreed we'd publish whatever came out.
Dragonfly took the p99 and memory sections, KiviDB took pipelining by 33% and out-served it on small values in every pass, and the two tied at 10 KB on the network ceiling.
Small value throughput
SET/GET mix (1:10 ratio) with 100-byte values, pipeline depth 1. RESP2, no TLS.
Dragonfly 1.41M ops/s at the lowest p99 (0.45 ms); KiviDB 1.46M (0.46 ms), the most ops/s on every pass; Redis 182K (2.03 ms); Valkey 180K (2.04 ms)
Test 1 Small value throughput
The mean of each candidate's samples: the statistic the scorecard ranks this section by.
Mean of 10 passes per candidate; the whisker spans the fastest and slowest pass; the lead over KiviDB is inside the noise.
SET/GET mix (1:10) at pipeline depth 1 with 100-byte values, 200 connections. The section ranks on p99, and Dragonfly's median was 16 µs lower than KiviDB's, inside both engines' range across passes (0.407 to 0.511 ms and 0.423 to 0.535 ms). KiviDB's throughput lead is outside its range: its slowest pass, 1.44M ops/s, beat Dragonfly's fastest, 1.41M. Redis and Valkey were within 1% of each other at about an eighth of that throughput, and client CPU stayed under 40% on every pass.
Large value throughput
SET/GET mix (1:10 ratio) with 10 KB values, pipeline depth 1. RESP2, no TLS.
Dragonfly 398K ops/s at the lowest p99 (0.91 ms); KiviDB 398K (0.95 ms), both at the network ceiling; Valkey 108K (3.49 ms); Redis 107K (3.52 ms)
Test 1 Large value throughput
The mean of each candidate's samples: the statistic the scorecard ranks this section by.
Mean of 10 passes per candidate; the whisker spans the fastest and slowest pass; the lead over KiviDB is inside the noise.
SET/GET mix (1:10) at pipeline depth 1 with 10 KB values, 200 connections. Dragonfly and KiviDB delivered 397,525 and 397,526 ops/s, 29.6 Gbit/s of values into the client against the instances' 30 Gbit/s rating, so this section can't separate them. Their p99s swung by more than half the median between passes (0.799 to 1.335 ms and 0.775 to 1.279 ms), and the 40 µs between the medians is well inside that. Both ran 3.7 times the throughput of Redis and Valkey at about a quarter of their p99, and Redis and Valkey were within 1% of each other.
Pipeline effect
SET/GET mix (1:10 ratio) with 100-byte values, comparing pipeline depth 1 vs 30. Candidate statistic: pipeline-30 throughput.
KiviDB 22.2M ops/s at pipeline 30, a floor with the client at 69% CPU; Dragonfly 16.7M; Redis 1.48M; Valkey 1.37M
Test 1 Pipeline depth 1
SET/GET mix at pipeline depth 1 (reuses small-value throughput data)
Median of 10 passes per candidate; the whisker spans the lowest and highest pass.
Test 2 Pipeline depth 30
SET/GET mix at pipeline depth 30
Median of 10 passes per candidate; the whisker spans the lowest and highest pass.
SET/GET mix (1:10) with 100-byte values at pipeline depth 30, 200 connections. KiviDB's slowest pass, 21.5M ops/s, beat Dragonfly's fastest, 16.8M, and its median led by 33%. That number is a floor: the client averaged 69% CPU in KiviDB's cell (66 to 70% per pass), 50 memtier threads can keep at most 50 of its 64 vCPUs busy, and KiviDB's passes got slower as client CPU rose (r = −0.95 over ten passes). Dragonfly's cell ran the client at 34% with no such pattern. Pipelining raised throughput 15.2 times for KiviDB, 11.9 for Dragonfly, 8.2 for Redis and 7.6 for Valkey, and Redis led Valkey by 8%.
Memory efficiency
Baseline-corrected memory after loading keys with 100-byte values. RSS delta is the primary metric (allocator-neutral).
Dragonfly 112.5 MB RSS delta (118 B/key); Redis 157.5 MB (165 B/key); Valkey 162.5 MB (170 B/key); KiviDB 254.9 MB (267 B/key)
Test 1 Memory efficiency
The mean of each candidate's samples: the statistic the scorecard ranks this section by.
One measurement per candidate.
1,000,000 keys with 100-byte values loaded into a fresh engine; the statistic is the used_memory_rss delta over the empty engine's baseline. Dragonfly used 29% less than Redis and 31% less than Valkey, and KiviDB used 2.3 times Dragonfly's memory and 1.6 times Redis's. The delta leaves out each engine's idle RSS, which was 111.6 MB for Dragonfly against 11.7 to 24.1 MB for the other three. memtier's default values are 100 ASCII bytes, which Dragonfly packs to 88 bytes each (7 bits per byte); the other three store them as sent.
Test conditions: rig, corpus, protocol
- Machine
- AWS c7g.metal (engine) + c7g.16xlarge (client)
- Chip
- AWS Graviton3
- Cores
- 64 physical (engine)
- Memory
- 128 GiB (engine), 128 GiB (client)
- OS
- Ubuntu 24.04 LTS arm64 (arm64)
- Runtime
- Docker (redis:8.10.3, valkey/valkey:9.1.3, dragonflydb/dragonfly:v2.0.2, kividb/kividb:v1.1.2) with Docker's default seccomp profile plus the io_uring syscalls and memlock unlimited; memtier_benchmark 2.5.1; RESP2, no TLS
- Warmups
- 2 unmeasured passes
- Measured
- 10 passes per tool
- Process
- Container per engine, network_mode host, seccomp: Docker's default profile plus io_uring_setup, io_uring_enter and io_uring_register, memlock unlimited: Dragonfly 64 proactor threads and KiviDB 62 worker threads (its default, cores − 2), both on io_uring, on 64 physical cores; Redis and Valkey single-threaded I/O (default io-threads 1); 16 GiB maxmemory for all four
- Cache
- Preloaded keyspace, 100% hit ratio on GET workloads; working sets sized below 50% of maxmemory; eviction flags set for parity of intent, never triggered
- Output
- memtier_benchmark 2.5.1: ops/s, p50/p99/p99.9 latency, client CPU per 10 s pass; memory via INFO MEMORY: baseline-corrected used_memory and used_memory_rss (process RSS) deltas
What did we learn?
Dragonfly took the p99 and memory sections, KiviDB took pipelining by 33% and out-served it on small values in every pass, and the two tied at 10 KB on the network ceiling.
| Candidate | Small value throughput (ms, lower is better) | Large value throughput (ms, lower is better) | Pipeline effect (M ops/s, higher is better) | Memory efficiency (MB, lower is better) |
|---|---|---|---|---|
| Redis | 2.03 | 3.52 | 1.48 | 158 |
| Valkey | 2.04 | 3.48 | 1.37 | 162 |
| Dragonfly | 0.45 | 0.94 | 16.72 | 112 |
| KiviDB | 0.47 | 0.97 | 22.40 | 255 |
KiviDB 1.1.2 ran its default 62 worker threads and Dragonfly 2.0.2 its default 64, both on io_uring. On 100-byte SET/GET, KiviDB's slowest pass (1.44M ops/s) beat Dragonfly's fastest (1.41M); Dragonfly's p99 was lower, 0.45 ms to 0.46, by less than either engine's spread between passes. At pipeline depth 30 KiviDB did 22.2M ops/s to Dragonfly's 16.7M, and that's a floor: the client sat at 66 to 70% CPU and KiviDB's passes slowed as it climbed. At 10 KB both did 398K ops/s, 29.6 Gbit/s against the instances' 30 Gbit/s rating, a tie. Memory went to Dragonfly: 118 bytes of RSS per key to KiviDB's 267, Redis's 165 and Valkey's 170. Redis 8.10.3 and Valkey 9.1.3 stayed within 1% of each other everywhere but pipeline 30, where Redis led by 8%. A second run under Docker's stock seccomp profile, which blocks io_uring, cost KiviDB 33% of its small-value throughput and Dragonfly nothing measurable. Medians of ten passes; the table shows means.
What this does not prove
- KiviDB's pipeline-30 cell ran the client at 66 to 70% CPU, where 50 memtier threads can keep at most 50 of its 64 vCPUs busy, and KiviDB's passes got slower as client CPU rose, so its 22.2M ops/s is a floor.
- At 10 KB, Dragonfly and KiviDB both delivered 398K ops/s, which is 29.6 Gbit/s of values into the client against the instances' 30 Gbit/s rating, so that section measures the network and can't separate them.
- Latency is closed-loop at 200 connections and measured at the client, so each engine's p99 comes at its own throughput. Between passes, p99 swung by 23 to 59% of its median in Dragonfly's and KiviDB's small- and large-value cells, far more than the gaps between them.
- Docker's default seccomp profile blocks io_uring, so this run allowed io_uring_setup, io_uring_enter and io_uring_register on top of it. In the second 2026-10-29 run, under the stock profile, Dragonfly fell back to epoll with no measurable change, while KiviDB fell back to tokio and lost 33% of its small-value throughput and 9% at pipeline 30.
- The two 2026-10-29 runs used different instance pairs, and Redis and Valkey, which have no io_uring code path, differed by up to 5.3% between them, so a gap under about 5% between the runs can't be put down to the seccomp profile.
- These numbers don't compare with the 2026-08-31 post's: the client is now a c7g.16xlarge at 200 connections instead of a c7g.4xlarge at 196, io_uring is allowed through seccomp, and every engine is a newer release. That post's Dragonfly most likely ran on epoll too, since it used the same AMI and Docker's default profile, but no startup log was kept to show it either way.
- Dragonfly ran 64 proactor threads, its default on 64 cores, and KiviDB its default 62 worker threads (cores minus two), both read from their startup logs. Redis and Valkey ran their default single-threaded I/O (io-threads 1); turning on their I/O threads is a different benchmark.
- Persistence was off for all four. For KiviDB that's its default: no AOF, and snapshots only on an explicit SAVE or BGSAVE.
- The official Redis 8.10.3 image loads its bundled Search, JSON, TimeSeries and Bloom modules, and they were left on as shipped; they add about 12 MB of baseline RSS over Valkey and don't affect GET or SET.
- Memory is the baseline-corrected used_memory_rss delta after loading 1,000,000 keys with 100-byte values into a fresh engine: 267 B per key for KiviDB, 118 for Dragonfly, 165 for Redis and 170 for Valkey. Dragonfly's per-shard tables scale with its thread count, so its fixed cost differs at other thread counts.
- memtier's default value is 100 ASCII bytes, and Dragonfly packs an all-ASCII value into 7 bits per byte, so it stores each of these values in 88 bytes where the other three store 100; binary values wouldn't pack, so the next run adds a second memory cell loaded with random bytes. Dragonfly's RSS delta also leaves out its 112 MB idle RSS, which the keys partly fill: by its own used_memory it added 130 bytes per key.
- The rig was one c7g.metal engine host and one c7g.16xlarge client in a single cluster placement group in us-east-1, speaking RESP2 without TLS. maxmemory was 16 GiB for all four, and eviction never triggered: working sets stay at or below half of it.
- KiviDB checks GitHub for a newer release at startup, before it listens; that was left on, as shipped.
Rerun it yourself: the runner and configuration are in the repository, and the methodology page covers what every run holds constant. Think a result is wrong? Open an issue with your rig and your samples.
Redis is a registered trademark of Redis Ltd. Valkey is a trademark of The Linux Foundation. Dragonfly is a trademark of DragonflyDB Ltd. KiviDB is a trademark of KIVIDB PRIVATE LIMITED.