文章目录
Redis容量评估
计算Redis容量,并不只是仅仅计算key占多少字节,value占多少字节,因为Redis为了维护自身的数据结构,也会占用部分内存,本文章简单介绍每种数据类型(String
、Hash
、Set
、ZSet
、List
)占用内存量,供做Redis容量评估时使用。
Redis内存模型
要评估Redis占用内存量,首先需要了解Redis的内存模型。通过Redis的内存模型,才能不仅知其然,而且知其所以然。
查看内存占用
首先在redis-cli
命令行中键入memory stats
命令查看下Redis内存占用,大概如下图所示:
1) "peak.allocated" #Redis启动到现在,占用内存的峰值
2) (integer) 850160
3) "total.allocated" #当前使用的内存总量,Redis分配器分配的内存总量(单位是字节),包括使用的虚拟内存(即swap)
4) (integer) 827608
5) "startup.allocated" #Redis启动完成使用的内存字节数
6) (integer) 765616
7) "replication.backlog" #主从复制backlog使用的内存,默认10MB,backlog只在主从断线重连时发挥作用,主从复制本身并不依赖此项。
8) (integer) 0
9) "clients.slaves" #主从复制中所有slave的读写缓冲区
10) (integer) 0
11) "clients.normal" #除slave外所有其他客户端的读写缓冲区
12) (integer) 49630
13) "aof.buffer" #此项为aof持久化使用的缓存和aofrewrite时产生的缓存之和,如果关闭了appendonly那这项就一直为0
14) (integer) 0
15) "db.0" #redis每个db的元信息使用的内存,这里只使用了db0,所以只打印了db0的内存使用状态
16) 1) "overhead.hashtable.main"
2) (integer) 72
3) "overhead.hashtable.expires"
4) (integer) 0
17) "overhead.total" #redis额外的总开销内存字节数; 即分配器分配的总内存total.allocated,减去数据实际存储使用内存:startup.allocated+replication.backlog+clients.slaves+clients.normal+aof.buffer+dbx
18) (integer) 815318
19) "keys.count" #redis当前存储的key总量
20) (integer) 1
21) "keys.bytes-per-key" #平均每个key的内存大小:(total.allocated - startup.allocated) / keys.count
22) (integer) 61992
23) "dataset.bytes" #所有数据所使用的内存:total.allocated - overhead.total
24) (integer) 12290
25) "dataset.percentage" #所有数据占比:100 * dataset.bytes / (total.allocated - startup.allocated)
26) "19.5934348106384277"
27) "peak.percentage" #当前使用内存与历史最高值比例
28) "97.6251003742218018"
29) "fragmentation" #内存碎片比率
30) "2.1039986610412598"
总上图我们可以看出,Redis占用的内存并不仅仅包括数据内存dataset.bytes
,还包括启动初始化内存、主从复制占用内存、缓冲区内存等。
内存划分
Redis内存占用主要可以划分为如下几个部分:
- 数据
Redis数据占用内存dataset.bytes
包括key-value占用内存、dicEntry
占用内存、SDS
占用内存等。
数 据 所 占 内 存 = 当 前 所 占 总 内 存 ‘ t o t a l . a l l o c a t e d ‘ − 额 外 内 存 ‘ o v e r h e a d . t o t a l ‘ 数据所占内存=当前所占总内存`total.allocated`-额外内存`overhead.total` 数据所占内存=当前所占总内存‘total.allocated‘−额外内存‘overhead.total‘ - 初始化内存
redis启动初始化时使用的内存startup.allocated
,属于额外内存overhead.total
的一部分。 - 主从复制内存
用于主从复制,属于额外内存一部分。 - 缓冲区内存
缓冲内存包括客户端缓冲区、复制积压缓冲区、AOF缓冲区等;其中,客户端缓冲存储客户端连接的输入输出缓冲;复制积压缓冲用于部分复制功能;AOF缓冲区用于在进行AOF重写时,保存最近的写入命令。在了解相应功能之前,不需要知道这些缓冲的细节;这部分内存由jemalloc分配,因此会统计在used_memory中。 - 内存碎片
内存碎片是Redis在分配、回收物理内存过程中产生的。例如,如果对数据的更改频繁,而且数据之间的大小相差很大,可能导致redis释放的空间在物理内存中并没有释放,但redis又无法有效利用,这就形成了内存碎片。
内 存 碎 片 率 = R e d i s 进 程 占 用 内 存 / 当 前 所 占 内 存 ‘ t o t a l . a l l o c a t e d ‘ 内存碎片率=Redis进程占用内存/当前所占内存`total.allocated` 内存碎片率=Redis进程占用内存/当前所占内存‘total.allocated‘
内存碎片涉及到内存碎片率fragmentation
,该值对于查看内存是否够用比较重要:
该值一般>1,数值越大,说明内存碎片越多。如果<1,说明Redis占用了虚拟内存,而虚拟内存是基于磁盘的,速度会变慢,所以如果<1,就需要特别注意是否是内存不足了。
一般来说,mem_fragmentation_ratio在1.03左右是比较健康的状态(对于jemalloc来说);上面截图中的mem_fragmentation_ratio值很大,是因为还没有向Redis中存入数据,Redis进程本身运行的内存使得used_memory_rss 比used_memory大得多。
Redis数据内存
在了解了Redis内存模型之后,我们可以知道,Redis所占内存不仅仅时key-value,很包括其他内存占用。现在我们就来了解下Redis数据内存是如何占用的。
Redis数据内存分配
Redis数据内存除了包括key-value
,还包括dicEntry
、redisObject
、SDS
等。
- dicEntry:Redis是Key-Value数据库,因此对每个键值对都会有一个dictEntry,里面存储了指向Key和Value的指针;next指向下一个dictEntry,与本Key-Value无关。
- key:key的值并不是直接以字符串存储,而是存储在SDS结构中。
- redisObject:value的值既不是直接以字符串存储,也不是像Key一样直接存储在SDS中,而是存储在redisObject中。
- SDS:Redis没有直接使用C字符串(即以空字符’\0’结尾的字符数组)作为默认的字符串表示,而是使用了SDS。SDS是简单动态字符串(Simple Dynamic String)的缩写。
Redis数据内存计算
String
公式:
总 内 存 消 耗 = ( d i c t E n t r y 大 小 + r e d i s O b j e c t 大 小 + k e y S D S 大 小 + v a