Redis 的内存模型
1,Redis 内存统计
- # info 命令可以显示 Redis 服务器的基本信息, 包括服务器基本信息, CPU, 内存, 持久化, 客户端连接信息等
- > info memory
- used_memory:1537568 # Redis 分配器分配的内存总量, 包括使用的虚拟内存
- used_memory_human:1.47M
- used_memory_rss:704512 # Redis 进程占据操作系统的内存, 包括内存碎片
- used_memory_rss_human:688.00K
- used_memory_peak:1537568 # Redis 内存使用的峰值
- used_memory_peak_human:1.47M
- used_memory_peak_perc:100.01% # 使用内存达到峰值内存的占比
- used_memory_overhead:1031150 # Redis 维护数据集的内部机制所需的内存开销, 包括所有客户端输出缓冲区, 查询缓冲区, AOF 重写缓冲区和主从复制的 backlog
- used_memory_startup:980736 # Redis 服务器启动时消耗的内存
- used_memory_dataset:506418 # 数据占用的内存大小, used_memory - used_memory_overhead
- used_memory_dataset_perc:90.95% # 数据占用的内存的占比,(used_memory_dataset/(used_memory-used_memory_startup))*100%
- total_system_memory:17179869184 # 系统内存
- total_system_memory_human:16.00G
- used_memory_lua:37888 # Lua 脚本存储占用的内存
- used_memory_lua_human:37.00K
- maxmemory:0 # Redis 实例的最大内存配置
- maxmemory_human:0B
- maxmemory_policy:noeviction # 当达到 maxmemory 时的淘汰策略
- mem_fragmentation_ratio:0.46 # 内存碎片化比率, 值小于 1, 说明使用了虚拟内存
- mem_allocator:libc # Redis 使用的内存分配器, 可以是 libc / jemalloc / tcmalloc
- active_defrag_running:0 # 是否有内存碎片整理任务运行, 0 表示没有
- lazyfree_pending_objects:0 # 是否存在延迟释放的挂起对象
2,Redis 数据存储
dictEntry:
Redis 中的每个键值对都会有一个 dictEntry, 存储指向 Key 和 Value 的指针, next 指向下一个 dictEntry, 用于解决冲突 (链表法).
RedisObject:
Redis 中的 Value 都通过 RedisObject 来存储, type 指明了 Value 对象的类型, ptr 字段指向对象所在的地址. 除此之外, 还会包含对象编码的信息等.
- # 获取对象类型
- > type key-name
- # 获取对象编码方式
- > object encoding key-name
- # lru 存储对象最后一次被命令程序访问的时间
- > object idletime key-name # 空转时间
- # 对象被引用的次数
- > object refcount key-name
- SDS
Redis 将 SDS( simple dynamic string ) 用于默认字符串的表示.
- # 1. SDS 的定义
- struct sdshdr {
- // 数组中已使用的字节的数量 = 字符串的长度
- int len;
- // 数组中未使用的字节的数量
- int free;
- // 字节数组
- char buf[];
- };
图示:
- --------
- | sdshdr |
- --------
- | free |
- | 2 |
- --------
- | len |
- | 5 |
- -------- --------------------------------------------
- | buf | ---> | 'R' | 'e' | 'd' | 'i' | 's' | '\0' | | |
- -------- --------------------------------------------
SDS 遵循 C 语言字符串以空字符串结尾的惯例, 保存空字符串的 1 字节空间不计算在 SDS 的 len 属性
里面, 并且为空字符分配额外的 1 字节空间, 以及添加空字符到字符串末尾等操作, 都是由 SDS 自动完成.
遵循空字符结尾的好处是: SDS 可以直接重用一部分 C 语言字符串函数库里面的函数.
- # 2. SDS 与 C 语言字符串的区别
- ----------------------------------------------------------------------
| C 字符串 | SDS |
----------------------------------------------------------------------
| 获取字符串长度的时间复杂度为 O(N) | 获取字符串长度的时间复杂度为 O(1) |
| 可能会造成缓存区溢出 | 不会造成缓存区溢出 |
| 修改字符串 n 次必然执行 n 次内存重分配 | 修改字符串 n 次最多执行 n 次内存重分配 |
| 只能保存文本数据 | 可以保存文本或者二进制数据 |
| 可以使用所有字符串函数库里面的函数 | 部分使用所有字符串函数库里面的函数 |
- ----------------------------------------------------------------------
- # 3. SDS 减少内存重分配的优化策略
- ## 3.1 空间预分配
空间预分配用于优化 SDS 的字符串增长操作.
额外分配的未使用空间数量的公式:
(1) 如果对 SDS 进行修改之后, SDS 的长度小于 1MB, 那么程序分配和 len 属性同样大小的未使用空间,
这时 SDS 的 len 属性的值将和 free 属性的值相同
(2) 如果对 SDS 进行修改之后, SDS 的长度将大于等于 1MB, 那么程序会分配 1MB 的未使用空间
## 3.2 惰性空间释放
惰性空间释放用于优化 SDS 的字符串缩短操作.
当缩短字符串时, 程序并不会立即回收缩短后多出来的字节, 而是使用 free 属性将这些字节的数量记录起来,
并等待将来使用.
来源: https://juejin.im/entry/5bbbfdbf6fb9a05d212eb354