用MEMORY USAGE测5种大小的字符串和两种1000条目的集合类型,看大key的内存开销占比是怎么变化的,以及不同类型编码对内存占用的影响。
MEMORY USAGE命令能告诉你一个key在内存里到底占了多少字节,包括各种内部开销。
代码for n in [10, 100, 1000, 10000, 100000]: r.set(f"key_{n}", "x" * n) print(r.memory_usage(f"key_{n}"))
测出的5组数字,看额外开销占比的变化趋势。
结果value 10字节 → MEMORY USAGE 48字节 (开销38字节,380%) value 100字节 → MEMORY USAGE 138字节 (开销38字节,38%) value 1000字节 → MEMORY USAGE 1051字节 (开销51字节,5.1%) value 10000字节 → MEMORY USAGE 10268字节 (开销268字节,2.7%) value 100000字节→ MEMORY USAGE 114717字节 (开销14717字节,14.7%)
从10字节到10000字节,开销占比是一路下降的(固定的对象头开销被摊薄);但到100000字节这一档,开销占比又跳回了14.7%——比10000字节那一档反而更高。这说明value从10000字节涨到100000字节的过程中,跨过了内存分配器的某个尺寸台阶,触发了更粗粒度的内存块分配(比实际需要的字节数多分配了一截),这部分"多分配但没真正用上"的空间,也被算进了MEMORY USAGE里。
1000个字段的hash,跟1000个元素的list,同样量级的数据,占用差多少。
结果1000个字段的hash(每个字段"fN"→"vN") OBJECT ENCODING → hashtable MEMORY USAGE → :38060字节 1000个元素的list(每个元素"vN") OBJECT ENCODING → listpack MEMORY USAGE → :5928字节 比例:38060 / 5928 ≈ 6.4倍
差了6.4倍,原因有两层:第一,hash存的是"字段名+值"两份数据,list只存"值"一份,数据量本身就接近两倍;第二,也是更主要的原因,hash在1000个字段时已经超过默认阈值转成了 hashtable 编码(真正的哈希表,每个桶都有额外的指针开销),而list的1000个元素还没超过listpack阈值,依然是紧凑的连续内存结构。同样"1000个条目",编码是紧凑结构还是标准哈希表,带来的内存差异远比看起来的大。
每一步都是本文上面用redis-py跑出来的原始结果。
MEMORY USAGE:测量单个key的实际内存占用