Redis持久化RDB方式,是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后再替换之前的文件,用二进制压缩存储。
一、内存分配
- Redis在编译时便会指定内存分配器,内存分配器可以是libc、jemalloc、tcmalloc,默认是libc(Mac下)。
- libc:泛指,凡是符合实现了C标准规定的内容都是一种libc,如glic。
- Redis默认内存分配器(Mac下)
- glibc是Linux下C标准库的实现,全称
GNU C Library
。glibc本身是GNU旗下的C标准库,后来逐渐成为了Linux的标准C库,而Linux原来的标准C库libc逐渐不再被维护。- GNU计划,有译为“革奴计划”,是由理查德·斯托曼在1983年9月27日公开发起的自由软件集体协作计划,它的目标是创建一套完全自由的操作系统GNU。
- jemalloc:FreeBSD的内存分配器,最大优势在于多线程情况下的高性能以及内存碎片的减少,在Facebook有广泛应用。
- 安装时可指定
make MALLOC=jemalloc
- 安装时可指定
- tcmalloc:google开发的内存分配器,据称它的内存分配速度是glibc2.3中实现的malloc的数倍。
- 安装时可指定(Mac下)
brew install gperftools
make MALLOC=tcmalloc
- 安装时可指定(Mac下)
- libc:泛指,凡是符合实现了C标准规定的内容都是一种libc,如glic。
- 源码README.md
1 | Allocator |
使用
- 同时安装三个类型分配器,端口依次为6379、7379、8379
- 使用info memory查看信息(6.2.5),传送门
- used_memory:由Redis分配器分配的内存总量,包含了redis进程内部的开销和数据占用的内存,以字节为单位。
- used_memory_human:以人类更直观的单位展示分配的内存总量。
- used_memory_rss:向操作系统申请的内存大小,值一般大于used_memory,因为Redis的内存分配器会产生内存碎片。
- used_memory_rss_human:以人类更直观的单位展示向操作系统申请的内存大小。
- used_memory_peak:Redis的内存消耗峰值,以字节为单位。
- used_memory_peak_human:以人类更直观的单位展示内存消耗峰值。
- used_memory_peak_perc:使用内存达到峰值内存的百分比,即
(used_memory/used_memory_peak)*100%
。 - used_memory_overhead:Redis维护数据集的内部机制所需的内存开销,包括所有客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制backlog等。
- used_memory_startup:服务启动时消耗的内存。
- used_memory_dataset:数据占用的内存大小,即
used_memory-used_memory_overhead
。 - used_memory_dataset_perc:数据占用的内存大小的百分比,
(used_memory_dataset/(used_memory-used_memory_startup))*100%
。 - allocator_allocated:Total bytes allocated form the allocator, including internal-fragmentation. Normally the same as used_memory.
- allocator_active:Total bytes in the allocator active pages, this includes external-fragmentation.
- allocator_resident:Total bytes resident (RSS) in the allocator, this includes pages that can be released to the OS (by MEMORY PURGE, or just waiting).
- total_system_memory:系统内存总量,以字节为单位。
- total_system_memory_human:以人类更直观的单位展示系统内存总量。
- used_memory_lua:Lua脚本存储占用的内存,以字节为单位。
- used_memory_lua_human:Lua脚本存储占用的内存,以MB/KB为单位。
- used_memory_scripts:Number of bytes used by cached Lua scripts.
- used_memory_scripts_human:Human readable representation of previous value
- number_of_cached_scripts:
- maxmemory:Redis实例的最大内存配置,设置的最大内存。
- maxmemory_human:Redis实例的最大内存配置,设置的最大内存。
- maxmemory_policy:当达到maxmemory时的淘汰策略。
- allocator_frag_ratio:Ratio between allocator_active and allocator_allocated. This is the true (external) fragmentation metric (not mem_fragmentation_ratio).
- allocator_frag_bytes:Delta between allocator_active and allocator_allocated. See note about mem_fragmentation_bytes.
- allocator_rss_ratio:Ratio between allocator_resident and allocator_active. This usually indicates pages that the allocator can and probably will soon release back to the OS.
- allocator_rss_bytes:Delta between allocator_resident and allocator_active
- rss_overhead_ratio:Ratio between used_memory_rss (the process RSS) and allocator_resident. This includes RSS overheads that are not allocator or heap related.
- rss_overhead_bytes:Delta between used_memory_rss (the process RSS) and allocator_resident.
- mem_fragmentation_ratio:碎片率,约等于
used_memory_rss/used_memory
。 - mem_fragmentation_bytes: Delta between used_memory_rss and used_memory. Note that when the total fragmentation bytes is low (few megabytes), a high ratio (e.g. 1.5 and above) is not an indication of an issue.
- mem_not_counted_for_evict:
- mem_replication_backlog:
- mem_clients_slaves:
- mem_clients_normal:
- mem_aof_buffer:
- mem_allocator:内存分配器。
- active_defrag_running:0表示没有活动的defrag任务正在运行,1则相反(defrag意为内存碎片整理)。
- lazyfree_pending_objects:0表示不存在延迟释放的挂起对象,1则相反。
- lazyfreed_objects:
参数 libc jemalloc tcmalloc used_memory 1125632 1068768 1074984 used_memory_human 1.07M 1.02M 1.03M used_memory_rss 2105344 2621440 2367488 used_memory_rss_human 2.01M 2.50M 2.26M used_memory_peak 1186016 1128640 1139000 used_memory_peak_human 1.13M 1.08M 1.09M used_memory_peak_perc 94.91% 94.70% 94.38% used_memory_overhead 1080032 1027264 1033472 used_memory_startup 1062592 1006664 1012976 used_memory_dataset 45600 41504 41512 used_memory_dataset_perc 72.34% 66.83% 66.95% allocator_allocated 1080512 1237680 1033952 allocator_active 2067456 1581056 2329600 allocator_resident 2067456 4722688 2329600 total_system_memory 17179869184 17179869184 17179869184 total_system_memory_human 16.00G 16.00G 16.00G used_memory_lua 37888 37888 37888 used_memory_lua_human 37.00K 37.00K 37.00K used_memory_scripts 0 0 0 used_memory_scripts_human 0B 0B 0B number_of_cached_scripts 0 0 0 maxmemory 0 0 0 maxmemory_human 0B 0B 0B maxmemory_policy noeviction noeviction noeviction allocator_frag_ratio 1.91 1.28 2.25 allocator_frag_bytes 986944 343376 1295648 allocator_rss_ratio 1.00 2.99 1.00 allocator_rss_bytes 0 3141632 0 rss_overhead_ratio 1.02 0.56 1.02 rss_overhead_bytes 37888 -2101248 37888 mem_fragmentation_ratio 1.95 2.55 2.29 mem_fragmentation_bytes 1024832 1593696 1333536 mem_not_counted_for_evict 0 0 0 mem_replication_backlog 0 0 0 mem_clients_slaves 0 0 0 mem_clients_normal 17440 20496 20496 mem_aof_buffer 0 0 0 mem_allocator libc jemalloc-5.1.0 tcmalloc-2.9 active_defrag_running 0 0 0 lazyfree_pending_objects 0 0 0 lazyfreed_objects 0 0 0 使用redis-benchmark压测对比内存碎片率
TODO
参考
二、内存分析管理优化
启动redis服务后,进入命令模式,输入
info memory
得到如下结果used_memory
: Total number of bytes allocated by Redis using its allocator (either standard libc, jemalloc, or an alternative allocator such as tcmalloc),翻译过来就是分配的内存总量,即存储的所有数据占用的内存。used_memory_human
: Human readable representation of previous value,以可读格式返回使用的内存量。
used_memory_rss
: Number of bytes that Redis allocated as seen by the operating system (a.k.a resident set size). This is the number reported by tools such as top(1) and ps(1),从操作系统角度显示Redis进程占用的物理内存总量(操作系统分配给redis的实际内存大小)。used_memory_rss_human
: Human readable representation of previous value,以可读格式返回Redis进程占用的物理内存总量。
used_memory_peak
: Peak memory consumed by Redis (in bytes),内存使用的最大值,表示used_memory峰值。used_memory_peak_human
: Human readable representation of previous value,以可读格式返回内存使用的最大值。
used_memory_peak_perc
: The percentage of used_memory_peak out of used_memory,使用内存达到峰值内存的百分比,即(used_memory/ used_memory_peak) * 100%
。used_memory_overhead
: The sum in bytes of all overheads that the server allocated for managing its internal data structures,Redis为了维护数据集的内部机制所需的内存开销,包括所有客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制的backlog。used_memory_startup
: Initial amount of memory consumed by Redis at startup in bytes,Redis服务器启动时消耗的内存。used_memory_dataset
: The size in bytes of the dataset (used_memory_overhead subtracted from used_memory),数据占用的内存大小,即used_memory - used_memory_overhead
。used_memory_dataset_perc
: The percentage of used_memory_dataset out of the net memory usage (used_memory minus used_memory_startup),数据占用的内存大小的百分比,(used_memory_dataset/(used_memory - used_memory_startup)) * 100%
。total_system_memory
: The total amount of memory that the Redis host has,系统总内存。total_system_memory_human
: Human readable representation of previous value,以更直观的可读格式显示整个系统内存。
used_memory_lua
: Number of bytes used by the Lua engineused_memory_lua_human
: Human readable representation of previous value
used_memory_scripts
: Number of bytes used by cached Lua scripts,Lua脚本存储占用的内存。used_memory_scripts_human
: Human readable representation of previous value,以更直观的可读格式显示Lua脚本存储占用的内存。
maxmemory
: The value of the maxmemory configuration directive,Redis实例的最大内存配置。maxmemory_human
: Human readable representation of previous value,以更直观的可读格式显示Redis实例的最大内存配置。
maxmemory_policy
: The value of the maxmemory-policy configuration directive,当达到maxmemory时的淘汰策略。mem_fragmentation_ratio
: Ratio between used_memory_rss and used_memory,内存的碎片率,used_memory_rss/used_memory
,4.0版本之后可以使用memory purge手动回收内存。- 当mem_fragmentation_ratio>1时,说明used_memory_rss-used_memory多出的部分内存并没有用于数据存储,而是被内存碎片所消耗,如果两者相差很大,说明碎片率严重。
- 当mem_fragmentation_ratio<1时,这种情况一般出现在操作系统把Redis内存交换(Swap)到硬盘导致,出现这种情况时要格外关注,由于硬盘速度远远慢于内存,Redis性能会变得很差。
mem_allocator
: Memory allocator, chosen at compile time,内存分配器。active_defrag_running
: Flag indicating if active defragmentation is active,表示没有活动的defrag任务正在运行,1表示有活动的defrag任务正在运行(defrag:表示内存碎片整理)。lazyfree_pending_objects
: The number of objects waiting to be freed (as a result of calling UNLINK, or FLUSHDB and FLUSHALL with the ASYNC option),表示redis执行lazy free操作,在等待被实际回收内容的键个数。
分析:Redis进程内的内存主要包括:
自身内存 + 对象内存 + 缓冲内存 + 内存碎片
,其中自身内存 + 对象内存 + 缓冲内存 = used_memory
,操作系统实际分配给redis的内存used_memory_rss - used_memory = 内存碎片
。- 自身内存:消耗很少
- 对象内存:Redis中最占内存的一块,存储着用户所有数据,包括key和value对象的内存(尽量避免过长的key和合理使用value的类型)。
- 缓冲内存:分为客户端缓冲、复制积压缓冲区、AOF缓冲区。
- 客户端缓冲:指的是所有连接到Redis的服务器tcp连接输入输出缓冲,输入缓冲无法控制,最大空间1G;输出缓冲可通过client-output-buffer-limit控制。
- 普通客户端:
client-output-buffer-limit normal 0 0 0
,普通客户端默认并没有对输出缓冲区做限制。但是如果当有大量的慢连接客户端接入时,这部分消耗就不能忽略了,因为消费的很慢,在成输出缓冲区数据积压。所以可以设置maxclients做限制。 - 从客户端:
client-output-buffer-limit slave 256mb 64mb 60
,主节点会每一个从节点单独建立一条连接用于命令复制。当主节点网络延迟较高或主节点挂载大量的从节点时,这部分内存消耗将占用很大一部分,建议主节点挂载从节点最好不要超过2个。 - 订阅客户端:
client-output-buffer-limit pubsub 32mb 8mb 60
,当生产消息的速度快于消费的速度时,输出缓冲区容易积压消息
- 普通客户端:
- 复制积压缓冲区:一个可重用的固定大小缓冲区用于实现部分复制功能,根据repl-backlog-size参数控制,默认1MB。对于复制积压缓区,主节点有一个,所有从节点啊共享这个缓冲区,因此可以设置较大的值,比如100MB,这部分投入是有价值的,可以有效避免全量复制。
- AOF缓冲区:用于AOF重写期间保存最近写入的命令,等待被刷到磁盘。
- 客户端缓冲:指的是所有连接到Redis的服务器tcp连接输入输出缓冲,输入缓冲无法控制,最大空间1G;输出缓冲可通过client-output-buffer-limit控制。
- 内存碎片:以下场景容易出现高内存碎片问题:
- 频繁更新,对已经存在的key进行append、set、range操作
- 大量过期键删除,键对象过期删除后,释放的空间无法得到拆分利用
- 执行AOF/RDB重写时Redis创建的子进程,也会产生碎片
内存管理
- 设置最大可用内存
config set maxmemory 6GB
和设置内存溢出策略控制config set maxmemory-pol-icy{policy} 动态配置
,由于内存碎片实际占用系统内存更大,超过最大可用内存,redis将执行内存溢出控制策略。- noeviction:默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息(error)OOM command not al-lowed when used memory,此时Redis只响应读操作。
- volatile-lru:根据LRU(最近最少使用)算法删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略。
- allkeys-lru:根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。
- allkeys-random:随机删除所有键,直到腾出足够空间为止。
- volatile-random:随机删除过期键,直到腾出足够空间为止。
- volatile-ttl:根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。
- 删除过期键
- 惰性删除:当客户端读取带有超时属性的键时,如果已经超过键设置的过期时间,会执行删除操作并返回空。但这种方式下,当过期键一直没有访问将无法得到及时删除,从而导致内存不能及时释放。
- 定时删除:Redis内部维护一个定时任务,默认每秒运行10次,采用了自适应算法,根据键的过期比例、使用快慢两种速率模式回收键。
- 设置最大可用内存
内存优化
- 缩减键值对象
- 控制键的数量
- 字符串优化
- 编码优化
- 共享对象池
参考
三、内存查看
几种方式
方式一:bigkeys方式:
redis-cli -h localhost -p 6379 -a your_passwod --bigkeys -i -i 0.1
,使用scan方式对key进行统计不会对redis造成阻塞-------- summary -------
以上部分显示了扫描过程-------- summary -------
部分- 每种数据结构中最大的Key
- string类型以字节长度为衡量标准,统计较为准确
- list,set,zset,hash等都是以元素个数作为衡量标准,元素个数多不能说明其占的内存就一定多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type. You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).
[00.00%] Biggest string found so far '"str"' with 6 bytes
[00.00%] Biggest stream found so far '"testStream"' with 1 entries
[00.00%] Biggest string found so far '"testHyperLogLog"' with 24 bytes
[00.00%] Biggest zset found so far '"myZset"' with 3 members
[00.00%] Biggest zset found so far '"testZset"' with 4 members
[00.00%] Biggest set found so far '"testSet"' with 2 members
[00.00%] Biggest list found so far '"list"' with 2 items
-------- summary -------
Sampled 8 keys in the keyspace!
Total key length in bytes is 60 (avg len 7.50)
Biggest list found '"list"' has 2 items
Biggest string found '"testHyperLogLog"' has 24 bytes
Biggest stream found '"testStream"' has 1 entries
Biggest set found '"testSet"' has 2 members
Biggest zset found '"testZset"' has 4 members
1 lists with 2 items (12.50% of keys, avg size 2.00)
0 hashs with 0 fields (00.00% of keys, avg size 0.00)
2 strings with 30 bytes (25.00% of keys, avg size 15.00)
1 streams with 1 entries (12.50% of keys, avg size 1.00)
1 sets with 2 members (12.50% of keys, avg size 2.00)
3 zsets with 9 members (37.50% of keys, avg size 3.00)
方式二:
debug object key
方式,如:debug object testZset
(4.0版本以前查看某个key的内存占用方式)- Value at:key的内存地址
- refcount:引用次数
- encoding:编码类型
- serializedlength:序列化长度
- lru_seconds_idle:空闲时间
1
Value at:0x7fc51d728ae0 refcount:1 encoding:ziplist serializedlength:47 lru:6556545 lru_seconds_idle:182
方式三:
memory usage key
方式(4.0版本开始支持)- 分配的内存总字节数
1
2memory usage somekey
(integer) 221方式四:rdbtools方式(需要安装)
- rdb
- redis-memory-for-key
rdbtools使用
安装
- pip安装
pip install rdbtools python-lzf
- 源码安装,省略
- pip安装
使用
rdb --help
- rdb转储json
rdb -c memory /usr/local/redis/dump.rdb -f ~/Desktop/dump.json
- 生成内存报告
rdb -c memory /usr/local/redis/dump.rdb -b 10 -t hash -f ~/Desktop/ddd.csv
1
2
3
4
5
6
7
8database:key在redis的db
type:key类型
key:key值
size_in_bytes:key的内存大小(byte)
encoding:value的存储编码形式
num_elements:key中的value的个数
len_largest_element:key中的value的长度
expiry:key过期时间- rdb转储json
将快照写入csv:
rdb -c memory /usr/local/var/db/redis/dump.rdb > ~/Desktop/dump.csv
根据第四列排序:
sort -t, -k4nr ~/Desktop/dump.csv -o ~/Desktop/sort.csv
统计key占用内存:
awk -F ',' -v sum=0 '{if($3~/^dm/){sum+=$4}}END{print sum}' dump.csv
查看具体某个key的内存情况
redis-memory-for-key -s localhost -p 6379 key_name
- 对于<4.0版本的redis可使用此方法以获得较精确结果
1
2
3
4
5
6Key key_name
Bytes 418836.0
Type hash
Encoding hashtable
Number of Elements 12
Length of Largest Element 73600
参考
四、过期策略和淘汰机制
- 过期策略
- 定时删除:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除
- 优点:保证内存被尽快释放
- 缺点:
- 若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key
- 定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重
- 惰性删除:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期则删除并返回null。
- 优点:删除操作只发生在取出key的时候,而且只删除当前key,对CPU时间的占用是比较少的
- 缺点:若大量的key在超出超时时间后,很久一段时间内都没有被获取过,那么可能发生内存泄露
- 定期删除:每隔一段时间执行一次删除(在redis.conf配置文件设置hz,1s刷新的频率)过期key操作
- 优点:
- 通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用–处理”定时删除”的缺点
- 定期删除过期key–处理”惰性删除”的缺点
- 缺点
- 在内存友好方面,不如”定时删除”
- 在CPU时间友好方面,不如”惰性删除”
- 优点:
- 定时删除:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除
总结
- 定时删除和定期删除为主动删除:Redis会定期主动淘汰一批已过去的key
- 惰性删除为被动删除:用到的时候才会去检验key是不是已过期,过期就删除
- 惰性删除+定期删除为redis服务器内置策略
- 第一、配置redis.conf 的hz选项,默认为10 (即1秒执行10次,100ms一次,值越大说明刷新频率越快,最Redis性能损耗也越大)
- 第二、配置redis.conf的maxmemory最大值,当已用内存超过maxmemory限定时就会触发主动清理策略
- 淘汰机制(主动清理策略)
- noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息
- 大多数写命令都会导致占用更多的内存(有极少数会例外, 如DEL)
- 默认的策略
- allkeys-lru: 所有key通用
- 优先删除最近最少使用(less recently used,LRU)的 key。
- volatile-lru: 只限于设置了 expire 的部分
- 优先删除最近最少使用(less recently used,LRU)的 key。
- allkeys-random: 所有key通用
- 随机删除一部分 key。
- volatile-random: 只限于设置了 expire 的部分
- 随机删除一部分 key。
- volatile-ttl: 只限于设置了 expire 的部分
- 优先删除剩余时间(time to live,TTL) 短的key。
- noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息