《深入理解redis》之三:内存管理的建议与技巧

配置redis

如果想要运行一个内存高效的redis数据库,首先要理解那些在 redis.conf 配置文件中所有内存相关的指令。

 redis.conf 为大多数指令提供了丰富的内联文档,大多数redis配置指令可以在运行时通过CONFIG SET进行设置。

rdbchecksum ,默认是yes,将65位循环冗余校验码(CRC64)放置在RDB快照文件的末尾,作为防止文件损坏的一种手段。当redis产生一个子进程将快照保存至磁盘时,对RDB快照进行CRC64校验会增加10%的内存使用。

redis中的主哈希表将主键与对应的值进行关联,若activerehashing=yes,那么该主哈希表每个100毫秒会重新哈希。重新哈希的过程将释放删除了的键占用的操作系统内存。activerehashing 发生在非工作时间,对客户端连接额影响最小。

主从复制

采用主从复制的方式提供高度可靠性、可伸缩性。在集群环境中,通过slave-of 指令将redis实例切换到从模式,此时 从实例 被允许从另一个被指派为主实例的redis中复制数据。内存 和 来自硬件和网络的延迟会直接影响主从实例的性能。

repl-disable-tcp-nodelay 指令可以用来更好地处理redis主从实例间的网络流量拥塞。通过对主从间的密集数据同步和更少的网络流量之间进行权衡,这种复制能够改善高流量情况下的网络性能。

32位redis

在redis.io网站上的官方文档内存优化中,其中有一条建议是在32位模式下编译redis替代默认的64位实例。对于同样小于3GB的数据集,其在32位redis实例中要比在64位版本中的redis中小。

如果应用程序使用整数集合,只要总内存不超过4GB的最大限制,那么32位版本节省的内存是相当可观的。当存入不同大小和类型的值时,redis的32位和64位版本之间的百分比差异意味着节省的内存数量显著降低。对于那些使用集合的应用程序来说,它们较多的使用了字符串,因为64位的redis可能是更好的选择。这是因为在64位版本中的字符串拥有额外的空间及更高效的编码。

对于哈希数据结构来说,32位和64位redis实例并没有特别大的差异:

对于32位版本的redis列表来说,在32位限制之下,列表非常适合存储整数和浮点数:

对于redis集合的测试如下:

1) 32位的redis并未在redis用户群中进行广泛部署和测试,因此相较于64位版本可能会有未发现的bug。

2) 诸如bittop、bitcount这样的位操作基于redis的64位版本进行了优化,因此相较于32位则没有那么高效。

3) 在32位redis中设置maxmemory参数更加困难,如果在32位版本的redis中的maxmemory值设置的过于靠近最大值4GB,那么通信、主从复制、I/O缓存都有可能随时导致redis崩溃。

info meory

10.143.128.165:6379> info memory
# Memory
used_memory:819312
used_memory_human:800.11K
used_memory_rss:1904640
used_memory_peak:2282784
used_memory_peak_human:2.18M
used_memory_lua:36864
mem_fragmentation_ratio:2.32
mem_allocator:jemalloc-3.6.0

used_memory    分配的字节数大小

used_memory_human 将used_memory格式化为人类可读的值

used_memory_rss    常驻集大小(resident set size) 操作系统中看到的内存分配, 

                                以及top显示的结果

used_memory_peak    redis使用的峰值内存

used_memory_peak_human    将used_memory_peak可视化

used_memory_lua                    redis的lua子系统使用的字节数

mem_fragmentation_ratio        used_memory_rss与used_memory的比率

mem_allocator                        在编译器redis使用的分配器

键过期

保证redis数据库不会超过可用内存---为键设置超时时间,一旦过了键的超时时限,键就会被自动驱逐。

当在键上调用EXPIRE命令设置过期时间时,该超时只能通过删除或者替换键的方式清除。之后,任何改变值的命令都是无法更改或者清楚之前设置的超时的。

10.143.128.165:6379> set name yinn
OK
10.143.128.165:6379> expire name 60
(integer) 1
10.143.128.165:6379> get name
"yinn"
10.143.128.165:6379> ttl name
(integer) 53
10.143.128.165:6379> append name nana
(integer) 8
10.143.128.165:6379> get name
"yinnnana"
10.143.128.165:6379> get name
(nil)

如果在设置了超时的键上调用set或者getset(键过期之前),那么超时会被清除,键不会从数据库中驱逐:

10.143.128.165:6379> set name yin
OK
10.143.128.165:6379> expire name 30
(integer) 1
10.143.128.165:6379> set name xian
OK
10.143.128.165:6379> ttl name
(integer) -1

也可以使用 persist 命令清楚键上的超时设定:

10.143.128.165:6379> set name yinn
OK
10.143.128.165:6379> expire name 60
(integer) 1
10.143.128.165:6379> ttl name
(integer) 51
10.143.128.165:6379> persist name
(integer) 1
10.143.128.165:6379> ttl name
(integer) -1

在一个已经设置过超时的键上调用expire命令将会清楚并重新设定超时:

10.143.128.165:6379> expire name 90
(integer) 1
10.143.128.165:6379> ttl name
(integer) 87
10.143.128.165:6379> expire name 20
(integer) 1
10.143.128.165:6379> ttl name
(integer) 17
10.143.128.165:6379> get name
(nil)

LRU键驱逐策略

通过maxmemory指令将最大内存设置为1MB以创建一个小内存的redis实例。maxmemory指令允许设定内存大小的硬性上限,运行时的redis实例受限于此。

10.143.128.165:6379> flushall   ##清楚数据库
OK
10.143.128.165:6379>config set maxmemory 1024
OK

当redis内存耗尽时,默认生效的是永不过期策略(noeviction policy):

默认的maxmemory-policy策略是永不过期。在noeviction策略中,没有键设置为过期。如果redis没有可用内存,任何写操作都会导致redis错误。

10.143.128.165:6379> config get maxmemory
1) "maxmemory"
2) "1024
10.143.128.165:6379>config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"

第一种过期LRU策略名为 volatile-lru,它将最近较少使用的键驱逐出去。这些键必须通过 expire set 命令设置了超时的。当redis内存耗尽时,redis开始删除那些设置了过期时间的键,即便该键仍然有剩余时间。

下一个策略是 allkeys-lru ,会删除redis中任何一个键,而且没有办法限制哪些键被删除。

redis的LRU算法是不准确的,因为redis并不会自动选择最佳的候选键来驱逐,例如最少使用的键或者最早访问的键。相反,redis默认行为是选取5个键的samples,并驱逐当中最少使用的那个。如果想要增加LRU算法的精确性,可以更改redis.conf文件中的maxmemory-samples指令,或者在运行时通过config set maxmemory-samples命令进行设置。

将maxmemory-samples增加到10,从而提升redisLRU算法的性能,效果接近真实LRU算法,但是副作用就是消耗更多的CPU计算能力。将maxmemory-samples降至3,从而减少了redisLRU算法的精确性,不过相应地加快了处理速度。

接下来是两种最大内存驱逐策略:volatile-random和allkeys-random。它们与volatile-lru和allkeys-lru相似,但是不采用LRU算法。volatile-random 基于键上设置的过期状态随机驱逐一个键,需要O(n)时间复杂度的操作来计算创建的这些键是否已被驱逐对于allkeys-random,整个键空间都是开放的。

最后一个最大内存策略是volatile-ttl,redis会尝试根据键的剩余时间(TTL)清除键。

创建内存高效的redis数据结构

小巧的哈希、列表、集合、有序集合

对于哈希、列表、有序集合来说,这种特殊编码方法基于ziplist(压缩列表):

压缩列表是一种特殊的双向链表,专为内存高效进行设计的。它存储了字符串和整数值,其中整数是以真正的整数值而非字符串形式进行存储的。它允许在列表的任意一端以O(1)时间复杂度进行push和pop操作。但是,由于每次操作都需要为ziplist使用的内存进行重新分配,实际上的复杂度与ziplist所使用的内存大小有关。

根据大小、类型以及数据结构的内容,ziplist编码方式为redis数据库极大地节约了内存使用。

redis中ziplist的实现通过为每个节点只存储3份数据实现较小的内存占用:第一份是前一个节点的长度,第二份是当前节点的长度,第三份是存储的值

对于哈希表来说,hash-max-ziplist-entries 指令设置了总共有多少字段是可以被特殊编码为ziplist,默认是512。hash-max-ziplist-value指令设置了从ziplist转变为哈希表所要达到的最大大小,默认64.

把位、字节和redis字符串用作随机访问数组

在集合中通过使用整数代替字符串,可以将集合的大小显著地降低,但是 位图仍然要比集合小一个数量级、更节省内存。

优化哈希,高效存储

 

 

 

待续P90

 

转载于:https://my.oschina.net/u/1862478/blog/1935253

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值