Redis vs Memcached Benchmark: Throughput and Memory Measured - NextGenBeing
Back to discoveries

Redis vs Memcached Benchmark: Throughput and Memory Measured

Redis 8 vs Memcached 1.6 measured with memtier_benchmark in Docker: GET/SET throughput, p50/p99 latency, io-threads, and memory per key at four value sizes.

AI Workflows 7 min read
Bekzod Erkinov

Bekzod Erkinov

May 8, 2026 • 142 views
Redis vs Memcached Benchmark: Throughput and Memory Measured
Photo by Markus Winkler on Unsplash
Size:
Height:
7 min read 2,872 words Focus mode: Eye care:

Listen to Article

Loading...
0:00 / 0:00
0:00 0:00
Low High
0% 100%
⏸ Paused ▶️ Now playing... Ready to play ✓ Finished
Table of contents · 4 sections

Redis vs Memcached Benchmark: Throughput and Memory Measured

This Redis vs Memcached benchmark answers one question: for your value size, which cache costs less memory, and does the faster one on paper actually win on a small box? On one shared i7-8550U laptop under Docker Desktop, throughput did not separate them, and memory flipped with value size: Memcached used 27% more per key at 64 B and 11-12% less from 1 KB up. Memcached -t 4 also showed about a third of Redis's median GET latency (~0.6 vs ~1.9 ms), but that is latency under CPU contention in a VM, so the absolute values are inflated and only the ratio is worth noting.

The numbers below come from one Docker setup on one noisy laptop, so each table shows the run-to-run range next to the median. For the "which one should I pick" reasoning, read When to use Redis vs Memcached in 2026; this post is the measurement side.

Benchmark setup

Everything ran on 2026-10-07 on a Windows 11 laptop (Intel i7-8550U, 4 cores / 8 threads, 8 GB visible to Docker) under Docker Desktop, with the CPU already busy with other work. All images are pinned to tags that exist on Docker Hub (redis:8.10.2, memcached:1.6.45, redislabs/memtier_benchmark:2.5.2; I checked each tag against the Docker Hub API on 2026-10-07). These are the versions that produced every number in this post:

Redis server v=8.10.2 sha=00000000:1 malloc=jemalloc-5.3.0 bits=64
memcached 1.6.45
memtier_benchmark v=2.5.2 libevent=2.1.12-stable

Load: 40 connections, 1 SET per 10 GETs, random keys over 100,000, 15 s per run, 3 runs per config, keys prefilled first (otherwise you benchmark misses). Memcached needs --protocol=memcache_binary; memtier speaks the Redis protocol by default.

Caveats, all of which apply to every table:

  • Not a pure engine comparison. Memcached -t 4 runs four worker threads; default Redis runs commands on one. Redis's benchmark docs say it is "mostly, a single-threaded server from the POV of commands execution" and that comparing one instance to a multi-threaded store is not fair. The -t 1 and --io-threads rows exist to separate thread count from engine.
  • Shared silicon. --cpuset-cpus 0-3 for servers and 4-7 for memtier index the Docker Desktop VM's vCPUs. I did not verify how those map to the laptop's four physical cores, so the two sides may well compete for the same cores, and the VM adds a virtual network hop. Trust shapes and ratios, not absolute ops/s.
  • Not covered: pipelining, TTLs, eviction pressure, write-heavy loads.

The throughput script

Runnable with bash and Docker (RUNS=1 SECS=5 bash tput.sh for a quick pass). A bare --save is how this word-split config list gives Redis an empty save schedule.

#!/bin/bash
# Throughput + latency. Servers on CPUs 0-3, memtier on CPUs 4-7, same Docker network.
RUNS=${RUNS:-3}; SECS=${SECS:-15}
REDIS=redis:8.10.2; MEMC=memcached:1.6.45; MT_IMG=redislabs/memtier_benchmark:2.5.2
docker network create bnet >/dev/null 2>&1
mt() { docker run --rm --network bnet --cpuset-cpus 4-7 $MT_IMG memtier_benchmark "$@"; }
KEYS="--key-minimum=1 --key-maximum=100000"
# name | port | memtier protocol flag | memtier threads | clients/thread | server image and command
cfgs=(
 "redis-default|6379||4|10|$REDIS redis-server --save  --appendonly no"
 "redis-io3|6379||4|10|$REDIS redis-server --save  --appendonly no --io-threads 3"
 "redis-io3-mt3|6379||3|13|$REDIS redis-server --save  --appendonly no --io-threads 3"
 "memcached-t1|11211|--protocol=memcache_binary|4|10|$MEMC memcached -t 1 -m 1024"
 "memcached-t4|11211|--protocol=memcache_binary|4|10|$MEMC memcached -t 4 -m 1024")
for cfg in "${cfgs[@]}"; do
  IFS='|' read -r name port proto th cl srv <<< "$cfg"
  for size in 64 1024; do
    docker rm -f srv >/dev/null 2>&1
    docker run -d --name srv --network bnet --cpuset-cpus 0-3 $srv >/dev/null; sleep 2
    mt -s srv -p $port $proto --threads=4 --clients=10 $KEYS --data-size=$size \
       --key-pattern=P:P --ratio=1:0 -n allkeys --hide-histogram >/dev/null 2>&1   # prefill, GETs must hit
    for run in $(seq $RUNS); do
      mt -s srv -p $port $proto --threads=$th --clients=$cl $KEYS --data-size=$size \
         --key-pattern=R:R --ratio=1:10 --test-time=$SECS --hide-histogram 2>&1 |
      awk -v n=$name -v s=$size -v r=$run '/^Totals/{t=$2} /^Gets/{g50=$6;g99=$7} /^Sets/{s50=$6;s99=$7}
        END{printf "%s %sB run%d ops=%s get_p50=%s get_p99=%s set_p50=%s set_p99=%s\n",n,s,r,t,g50,g99,s50,s99}'
    done
  done
done
docker rm -f srv >/dev/null 2>&1

Redis vs Memcached throughput and latency

The redis.conf comments (8.0 branch) advise I/O threads only on machines with at least 4 cores, leaving a spare ("if you have a four cores boxes, try to use 3 I/O threads"), and say to run the benchmark itself threaded to match. Hence --io-threads 3, and a redis-io3-mt3 row with memtier at 3 threads (3 x 13 = 39 connections).

RUNS=3 SECS=15 bash tput.sh on the pinned images, 30 runs in total. Medians of 3; latencies in ms:

Config Value Ops/s (median) Ops/s range GET p50 GET p99 SET p50 SET p99
Redis default 64 B 17,014 13,068-19,317 1.89 11.9 1.91 11.8
Redis --io-threads 3 64 B 7,544 5,852-8,093 1.43 40.4 1.44 39.7
Redis io-threads 3, memtier --threads 3 64 B 8,724 8,665-13,455 1.61 31.9 1.67 31.2
Memcached -t 1 64 B 9,836 8,755-10,854 1.62 30.3 1.65 30.3
Memcached -t 4 64 B 11,507 9,452-12,995 0.59 37.6 0.60 37.9
Redis default 1 KB 14,262 12,771-21,255 1.85 17.8 1.85 17.7
Redis --io-threads 3 1 KB 9,115 5,763-11,738 1.67 33.0 1.68 32.5
Redis io-threads 3, memtier --threads 3 1 KB 10,152 7,854-12,063 1.53 29.4 1.53 29.1
Memcached -t 1 1 KB 12,685 10,140-16,015 1.71 22.3 1.71 21.4
Memcached -t 4 1 KB 14,305 9,881-18,967 0.57 31.9 0.57 32.3

The raw per-run lines behind this table are saved as bench/tput.txt next to the scripts.

What the data supports:

  • Throughput didn't pick a winner. At 1 KB, Memcached -t 4 (9.9k-19.0k) and default Redis (12.8k-21.3k) overlap almost completely, with the same median to within 0.3%. At 64 B default Redis was ahead (13.1k-19.3k against 9.5k-13.0k for Memcached -t 4), even though Memcached had four worker threads to Redis's one.
  • Memcached -t 4 had the lowest GET latency in every run. Its GET p50 was 0.55-0.62 ms in all six runs; default Redis was 1.82-1.95 ms in all six. Its p99 was not better: per-run GET p99 was 21-45 ms for Memcached -t 4 against 4.6-25 ms for default Redis (the medians are about 32-38 ms against 12-18 ms).
  • --io-threads 3 made Redis slower here. At 64 B all three --io-threads 3 runs (best 8.1k) came in below the worst default-Redis run (13.1k), and the median was still lower at 1 KB. With the clients and servers sharing eight logical CPUs, extra Redis I/O threads compete with memtier for cores.
  • Matching memtier --threads to 3 changed little. The ranges overlap with the plain io-threads row.
  • The p99 column is mostly scheduler noise. Tails of 10-45 ms against a 1-2 ms median come from server and client threads contending for CPU; I wouldn't rank anything on it.

Redis vs Memcached memory usage per key

I loaded 100,000 sequential keys (memtier-1 to memtier-100000, about 13 bytes on average) into a fresh container per size. "cgroup" is container memory from docker stats (MemUsage) minus the idle baseline, divided by 100,000. MemUsage is rounded to a few significant digits in MiB, which is worth roughly 0.1-1 MiB (1-10 B/key) of error, and depending on the cgroup version and setup it may exclude page cache, which doesn't matter for these in-process stores but means the numbers aren't comparable with other tools. Both servers held all 100,000 keys with zero evictions.

Value size Redis (cgroup B/key) Memcached (cgroup B/key) Memcached vs Redis
64 B 128 163 +27%
256 B 378 402 +6%
1 KB 1,350 1,203 -11%
4 KB 5,225 4,593 -12%

The memory script and its output

#!/bin/bash
# Memory per key: fresh container per value size, 100,000 keys loaded once each.
REDIS=redis:8.10.2; MEMC=memcached:1.6.45; MT_IMG=redislabs/memtier_benchmark:2.5.2
docker network create bnet >/dev/null 2>&1
mt() { docker run --rm --network bnet --cpuset-cpus 4-7 $MT_IMG memtier_benchmark "$@"; }
LOAD="--threads=2 --clients=5 --key-minimum=1 --key-maximum=100000 --key-pattern=P:P --ratio=1:0 -n allkeys --hide-histogram"
cg() { docker stats --no-stream --format '{{.MemUsage}}' srv | awk '{print $1}'; }   # container total (cgroup)
for size in 64 256 1024 4096; do
  for sys in redis memcached; do
    docker rm -f srv >/dev/null 2>&1
    if [ $sys = redis ]; then
      docker run -d --name srv --network bnet $REDIS redis-server --save  --appendonly no >/dev/null; port=6379; proto=""
    else
      docker run -d --name srv --network bnet $MEMC memcached -m 1024 >/dev/null; port=11211; proto="--protocol=memcache_binary"
    fi
    sleep 2; base=$(cg)
    mt -s srv -p $port $proto $LOAD --data-size=$size >/dev/null 2>&1; sleep 2
    if [ $sys = redis ]; then
      echo "redis size=$size keys=$(docker exec srv redis-cli dbsize) used_memory=$(docker exec srv redis-cli info memory | tr -d '\r' | awk -F: '/^used_memory:/{print $2}') cgroup_idle=$base cgroup_loaded=$(cg)"
    else
      st=$( (printf 'stats\r\nquit\r\n'; sleep 1) | docker run --rm -i --network bnet alpine nc srv 11211 | tr -d '\r')
      echo "memcached size=$size keys=$(echo "$st" | awk '/curr_items/{print $3}') item_bytes=$(echo "$st" | awk '/ bytes /{print $3}') evictions=$(echo "$st" | awk '/ evictions/{print $3}') cgroup_idle=$base cgroup_loaded=$(cg)"
    fi
  done
done
docker rm -f srv >/dev/null 2>&1

Output of that exact script on the pinned images, saved as bench/mem-pinned.txt. The table is (loaded - idle) MiB x 1,048,576 / 100,000; for Redis at 64 B, (19.07 - 6.891) MiB = 128 B/key:

redis size=64 keys=100000 used_memory=14200200 cgroup_idle=6.891MiB cgroup_loaded=19.07MiB
memcached size=64 keys=100000 item_bytes=13588895 evictions=0 cgroup_idle=2.758MiB cgroup_loaded=18.27MiB
redis size=256 keys=100000 used_memory=38199672 cgroup_idle=6.398MiB cgroup_loaded=42.49MiB
memcached size=256 keys=100000 item_bytes=32788895 evictions=0 cgroup_idle=2.047MiB cgroup_loaded=40.4MiB
redis size=1024 keys=100000 used_memory=134201592 cgroup_idle=6.598MiB cgroup_loaded=135.3MiB
memcached size=1024 keys=100000 item_bytes=109588895 evictions=0 cgroup_idle=2.039MiB cgroup_loaded=116.8MiB
redis size=4096 keys=100000 used_memory=518198808 cgroup_idle=6.832MiB cgroup_loaded=505.1MiB
memcached size=4096 keys=100000 item_bytes=416788895 evictions=0 cgroup_idle=2.762MiB cgroup_loaded=440.8MiB

Where the bytes go

The counters below are measured; the explanations after them are my interpretation. I took the servers' own stats on freshly loaded containers (stats slabs / stats for Memcached, MEMORY STATS for Redis; saved as bench/slabs.txt):

Value size Memcached item bytes/key Slab chunk size total_malloced/key Redis keys.bytes-per-key
64 B 136 152 157 84
1 KB 1,096 1,184 1,185 1,284
4 KB 4,168 4,544 4,561 5,124
  • Memcached stores items in fixed slab chunks, 1 MB pages at a time, and all 100,000 items landed in one slab class each time. At 64 B an item is 136 B but its chunk is 152 B, and total_malloced rounds up to whole pages (15 pages, 157 B/key), so the container's 163 B/key is mostly chunk size plus page rounding. At 1 KB and 4 KB the chunk padding is 88 and 376 B per key.
  • Redis reports keys.bytes-per-key of 84 at 64 B, and 1,284 and 5,124 at 1 KB and 4 KB. My inference is that Redis's smaller per-key overhead explains the 64 B win over Memcached's item header plus chunk rounding; I didn't read either source or inspect allocator internals, so treat the cause as unconfirmed. The measured container numbers above don't depend on it.

The practical consequence: at 4 KB, total_malloced (4,561 B/key) is about 9% above the bytes counter (4,168). Sizing a Memcached box from bytes under-provisions; budget from total_malloced or the container's cgroup number. A short load test also can't show fragmentation after weeks of churn.

Redis vs Memcached: which to pick

  • Values under about 256 B: Redis was smaller (Memcached +6% to +27% per key), and throughput didn't favor Memcached.
  • Values of 1 KB and up: Memcached saved 11-12% per key and had about a third of Redis's median GET latency with -t 4 on this contended box. A modest saving, not a reason to add a second system if Redis is already in your stack.
  • Decision rule: if your median value is under 256 B, size from Redis. Over 1 KB, a 10% memory saving on a 10 GB cache is about 1 GB, so measure both on your own data with the scripts below before deciding. The decision post covers persistence, data structures and the rest.

Next steps: test your own value size

  1. In tput.sh change for size in 64 1024 to your real value size(s), and in mem.sh change for size in 64 256 1024 4096 likewise. Use your own key naming if key length is far from 13 bytes.
  2. Run RUNS=3 SECS=15 bash tput.sh | tee tput.txt and bash mem.sh | tee mem.txt on a machine with no other load.
  3. Compare the min-max ranges, not the medians. If ranges overlap, as default Redis and Memcached -t 4 did at 1 KB, throughput isn't a reason to switch.
  4. Compare GET p50 per config, and memory per key from cgroup_loaded minus cgroup_idle. Check keys, curr_items and evictions in the output first.
Bekzod Erkinov

Bekzod Erkinov

Author

Founder of NextGenBeing. Software engineer working with Laravel, Python, and cloud infrastructure. Writes about patterns that actually hold up in production. Based in Tashkent, Uzbekistan.

Free guide

Get the AI-Assisted Developer's Field Guide

The workflow, prompts, and tools I use to ship faster with AI — free when you subscribe. Plus new deep-dives in your inbox. No spam, unsubscribe anytime.

Comments (0)

Please log in to leave a comment.

Log In

Related Articles

Don't miss the next deep dive

Get one well-researched tutorial in your inbox each week. No spam, unsubscribe anytime.