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

配置redis

若是想要运行一个内存高效的redis数据库,首先要理解那些在 redis.conf 配置文件中全部内存相关的指令。node

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

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实例并无特别大的差别:app

对于32位版本的redis列表来讲,在32位限制之下,列表很是适合存储整数和浮点数:dom

对于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

相关文章
相关标签/搜索