Redis数据库——内存分配器

大家好,这里是编程Cookbook。本文详细介绍Redis数据库的内存分配器,这是redis为什么这么快的原因,以及其作为内存数据库的内存管理策略。
Redis 的内存分配器
Redis 的内存管理设计对性能和内存利用率有重要影响。其内存分配依赖底层的内存分配器和自身的一些优化机制。
内存分配器的作用
Redis 是一个基于内存的数据库,需要频繁地进行内存分配和释放操作。内存分配器的性能和设计直接影响 Redis 的整体性能。
- 内存分配器的主要职责:
- 高效分配和释放内存。
- 尽量减少内存碎片。
- 提供线程安全的并发支持(多线程场景)。
Redis 支持的内存分配器
Redis 支持多种内存分配器,常见的有以下三种:
分配器 | 优点 | 缺点 | 使用场景 |
---|---|---|---|
jemalloc | 高性能、低碎片率、多线程支持【内存利用率高】 | 小对象(<4KB)分配速度略低于 tcmalloc | Redis 默认 ,推荐使用 |
libc | 简单易用,操作系统 默认 |
性能和碎片控制较差 | 非高并发、低性能需求场景 |
tcmalloc | 极高并发性能、多线程优化 【内存分配速度高】 | 内存利用率略低于 jemalloc(因线程缓存冗余) | 极高并发场景,可替代 jemalloc |
Redis 的内存分配器设计和优化是其性能的重要保障。正确选择分配器并监控内存使用,可进一步提升系统稳定性和效率。
- tcmalloc:适合
短生命周期
小对象。 - jemalloc:适合
长生命周期
中型对象。
jemalloc
默认分配器
- 自 Redis 3.0 起默认使用 jemalloc(由 Facebook 优化维护的高性能分配器)。
核心特点
- 内存碎片控制
- 采用 大小分级(Size Class) 管理,减少外部碎片。
- 通过 延迟释放(Decay-Based Purge) 策略合并空闲内存。
- 多线程优化
- 使用 线程本地缓存(Thread-Specific Cache) 降低锁竞争。
- 适合 Redis 的 多连接长生命周期 场景。
- 稳定性
- 长期运行后内存利用率仍保持较高水平。
优点
- 高并发下性能优异,尤其适合 频繁分配/释放中型对象(如 Redis 的键值对)。
- 内存碎片率显著低于 libc。
缺点
- 小对象(<4KB)分配速度略低于 tcmalloc(因 tcmalloc 的线程缓存更激进)。
libc(glibc malloc)
简介
- 操作系统默认的通用内存分配器(如 glibc 的
ptmalloc2
)。
核心问题
- 性能瓶颈
- 依赖全局锁,多线程竞争时性能骤降。
- 内存碎片
- 频繁分配/释放后易产生碎片,导致 内存浪费 或 OOM。
- 扩展性差
- 无法有效利用多核 CPU。
使用场景
- 仅推荐用于 低并发、短生命周期进程(如命令行工具)。
优点
- 无需额外依赖,兼容性极佳。
缺点
- 绝对禁止用于 Redis 等高性能服务。
tcmalloc(Thread-Caching Malloc)
简介
- Google 开发,专为高并发场景优化(如 Golang 运行时默认使用)。
核心特点
- 线程本地缓存
- 小对象(<256KB)分配无锁,吞吐量极高。
- 中央堆管理
- 大对象直接由全局堆分配,减少碎片。
- 动态调整
- 自动收缩线程缓存,避免内存浪费。
优点
- 小对象分配性能碾压 jemalloc(适合海量微请求场景)。
- 对 短生命周期对象 更友好(如 HTTP 请求临时内存)。
缺点
- 长期运行可能积累碎片(需定期重启或手动释放)。
- 内存利用率略低于 jemalloc(因线程缓存冗余)。
使用场景
- 替代 jemalloc 的场景:
- 需要极致并发性能(如每秒百万级分配)。
- 对象生命周期短(如微服务、Go 程序)。
tcmalloc vs jemalloc 核心区别总结
对比维度 | tcmalloc | jemalloc |
---|---|---|
设计目标 | 极致高并发的小对象分配速度 | 平衡性能与内存碎片控制 |
线程缓存上限 | 单线程可缓存 ≤256KB 内存块 | 默认单线程缓存 ≤32KB(可配置调大) |
小对象分配 | <256KB 完全无锁,速度极快 | <32KB 无锁,更大对象需竞争 Arena |
大对象处理 | ≥256KB 走中央堆(全局锁) | 通过多 Arena 分区减少竞争 |
内存碎片控制 | 较差(依赖定期回收线程缓存) | 优秀(延迟合并策略 + 主动碎片整理) |
适用对象大小 | <256KB 短生命周期小对象(如 HTTP 请求) | 32KB~1MB 中长期对象(如数据库键值) |
典型场景 | Go 微服务、高频短生命周期任务 | Redis、MySQL、RocksDB 等长期运行服务 |
内存利用率 | 较低(线程缓存更激进) | 较高(严格限制缓存膨胀) |
配置调优 | TCMALLOC_MAX_THREAD_CACHE_BYTES |
MALLOC_CONF="lg_tcache_max:18" |
一句话总结 | “速度疯魔”,适合瞬发小对象 | “稳定至上”,适合扛压的中大型对象 |
- 选 tcmalloc:是 高频、超小对象(<256KB)、
能容忍内存浪费
(如 Go 程序)。 - 选 jemalloc:需要 长期稳定运行、处理中型对象、
严格控制碎片
(如 Redis/数据库)。
Redis 内存分配器的选择
Redis 默认使用 jemalloc
,兼顾性能和碎片化控制,是推荐的内存分配器。
选择其他分配器步骤
- 更改编译选项:
- 在 Redis 源码目录下,编译时指定
MALLOC
参数即可选择内存分配器。 - 示例:
make MALLOC=libc # 使用 libc malloc make MALLOC=tcmalloc # 使用 tcmalloc
- 在 Redis 源码目录下,编译时指定
- 查看当前分配器:
- 编译完成后,通过
ldd redis-server
命令查看当前分配器:ldd redis-server | grep malloc
- 编译完成后,通过
jemalloc 的优势
作为默认分配器,jemalloc 在 Redis 中表现优异,具体优势包括:
- jemalloc 的优势体现在减小内存碎片方面。
- jemalloc 在 64 位系统中,将内存空间划分为小、大、巨大三个范围;
- 每个范围内又划分了许多小的内存块单位;
- 当 Redis 存储数据时,会选择大小最合适的内存块进行存储。size单位是字节。
1. 减少内存碎片
- jemalloc 按内存块大小将内存分为不同类别(class),分配时选择最合适的类别,避免内存碎片化。
2. 线程本地缓存
- jemalloc 为每个线程维护独立的分配缓存,减少线程间竞争。
3. 内存分配效率高
- jemalloc 的分配算法更适合 Redis 的高并发特性,可以快速分配和释放内存。
4. 内存统计
- jemalloc 提供了详细的内存统计工具,可通过 Redis 的
DEBUG
命令查看:redis-cli DEBUG malloc-stats
Redis 内存管理机制
除了底层的内存分配器,Redis 还采用了多种机制优化内存使用:
内存预分配
- Redis 会为某些数据结构(如 SDS)预先分配一定的内存,避免频繁的内存分配操作。
- 优势:
- 提升性能,减少频繁分配和释放内存的开销。
- 动态扩容:当内存不足时,会按一定比例扩展,减少扩容次数。
内存碎片处理
- 问题:
- Redis 在分配和释放内存时,会出现碎片化问题。
- 指标:
- jemalloc 内置了优化机制,通过内存池减少碎片。
- 提供
INFO MEMORY
命令,可通过mem_fragmentation_ratio
指标监控内存碎片率。mem_fragmentation_ratio:1.05 # 碎片率 1.05,表示有 5% 的碎片
- 低于 1.2:正常。
- 高于 1.5:可能存在严重碎片,需分析和优化。
- 优化方案:
- 如果发现碎片率过高(如超过 1.5),可以尝试以下几种优化方法:
- 重启 Redis:重启 Redis 会清理内存中的碎片,重新进行内存分配。
- 调整 jemalloc 配置:优化 jemalloc 的参数设置,以提高内存使用效率。
- 定期清理空闲内存:在 Redis 长时间运行后,可以使用
redis-cli memory purge
手动触发 jemalloc 的内存清理操作,减少碎片化。
- 如果发现碎片率过高(如超过 1.5),可以尝试以下几种优化方法:
内存淘汰机制
当 Redis 内存占用达到上限时,会根据设置的淘汰策略移除一些数据。
- 常见策略:
-
针对设置了过期时间的键
volatile-lru
:仅对设置了过期时间的键,淘汰最近最少使用的键。volatile-ttl
:仅对设置了过期时间的键,淘汰剩余时间最短的键。volatile-random
:仅对设置了过期时间的键,随机淘汰。volatile-lfu
:仅对设置了过期时间的键,淘汰访问频率最低的键。
-
针对所有键
allkeys-lru
:对所有键,淘汰最近最少使用的键。allkeys-random
:对所有键,随机淘汰。allkeys-lfu
:对所有键,淘汰访问频率最低的键。
-
无淘汰策略
noeviction
:不进行淘汰,当内存超限时直接返回错误。
压缩内存结构
- Redis 内部使用紧凑型数据结构(如 ziplist、intset)来节省内存。
- 特点:
- 小规模数据(如小列表、小哈希)会使用压缩存储结构,减少内存开销。
Redis 内存优化建议
Redis 内存监控
Redis 提供了多种命令监控内存使用情况:
-
查看内存使用情况:
INFO MEMORY
输出示例:
used_memory:1048576 # 已使用的内存 used_memory_peak:2097152 # 内存峰值 mem_fragmentation_ratio:1.02 # 内存碎片率
-
查看键的内存占用:
MEMORY USAGE key
-
查看内存分配统计:
使用 jemalloc 的jemalloc.stats.print
。redis-cli -p 6379 debug jemalloc stats
开启最大内存限制
- 配置
maxmemory
参数,避免超出物理内存,报OOM(Out of Memory)错误。
分布式存储
- 在高内存使用场景下,采用 Redis Cluster 扩展容量。