redis内存优化

522 阅读5分钟

关于Redis内存优化的一些技巧,官方文档翻译+个人理解,如有错误请指正

当聚合数据类型Hash, List, Set, Sorted Set 全部由Integer组成或者当他们的Size小于一定值时,Redis会以非常高效的内存编码方式进行编码,最多可节省10倍的内存(平均节省5倍内存)。 编码需要占用CPU,这是CPU和内存的一个折中方案

hash-max-ziplist-entries 512
hash-max-ziplist-value 64
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
set-max-intset-entries 512

如果特殊编码的size超过了配置的值,Redis将把特殊编码转换为普通编码。当配置的值非常大时,建议使用基准测试测试下Redis转换编码所耗费的时间

使用32位的Redis

将Redis源码编译为32位程序时,由于指针很小,因此每个键占用的内存要少得多,但是32位最大寻址空间仅为4GB

由于Redis是单线程,如果服务器是多核的且内存较大时,可以考虑在一台服务器上部署多个Redis节点,这样既能充分利用内存又能充分利用CPU

RDBAOF不区分32和64位,所以Redis可以在32位和64位之间进行自由切换

Bit和Byte级别的操作

Redis提供了bit和Byte级别的操作:GETRANGE,SETRANGE,GETBITSETBIT。使用这些命令可以把Redis的字符串类型当做一个byte/bit数组操作。

比如,一个有一亿用户的app,每个用户的id都是连续自增的。此时可以使用类似BitMap(位图)来记录用户的性别(一个bit代表一个用户,根据用户id映射出第几个bit位,然后男对应bit位的1,女对应bit位的0)。使用这种方法记录到Redis中,一亿用户的信息只占12M内存空间

当然如果需要记录的信息比较多,一个bit的0/1无法满足时可以使用多个bit位来记录一个用户的信息或者使用byte来记录

用Hash类型代替多个key-String

n个key-String比key-Hash(包含n个字段的Hash)占用更多的内存,并且key-Hash速度会更快

有一点需要注意,如果是多个key-String,每个key都可以单独过期,如果是key-Hash则Hash中的多个属性只能一起过期或者不过期

key-Hash节省内存?

public class Student{
    private Long id;

    private Short age;

    private String name;

    private String email;

}

当有这样一个学生对象时,把一个学生对象的属性放到一个Hash表中比使用多个key-String来存储一个学生的多个属性要节省很多内存,因为小的Hash会被Redis压缩编码

key-Hash更快?

首先说一个前提,从时间复杂度来讲:当N很小时,O(N)约等于O(1), 正常情况下key-String 和key-Hash的查找时间复杂度都为O(1)

当Hash包含的字段足够少时(小于配置值),Redis会把它编码为时间复杂度为O(N)的数据结构,类似于一定格式的数组。从时间复杂度来讲,因为N足够小,所以影响无关紧要。但是这样的线性键值对数组结构可以很好的被CPU高速缓存预加载并命中,CPU高速缓存要比主内存快很多。

利用Hash的技巧

假如放redis的key有这些特征,前缀一致且以数字结尾,比如:

object:102393
object:1234
object:5

可以把后两位数字作为Hash的属性,前面部分作为Hash的key 比如 object:102393 可以拆成object:102393两部分,前则为key-Hash的key,后者为Hash的属性:

HSET object:1023 93 somevalue

HSET语法补充: HSET KEY_NAME FIELD VALUE

类似于预分片,这样做的好处是每个Hash最多有100个(因为是后两位数字)元素,这是CPU和内存的最佳折中方案

依照这种做法,官方给出的内存优化结果是:在64位Redis中11MB的数据优化到了1.7MB

你可能会问:为什么压缩优化是在Hash中做,而不是把整个Redis当成一个Hash(顶级key)来做压缩?有两个原因:

  • 只在Hash中优化的话,可以在CPU 内存 压缩的最大元素个数 中间做出最佳权衡。而整个Redis中的顶级key太多了会浪费很多cpu资源
  • 还有就是Redis的顶级key有很多东西要处理:过期 ,LRU等,在整个Redis顶级key操作非常复杂不切实际

内存分配

maxmemory可以设置Redis最大内存(可能会稍微超过设置值)

以下是Redis内存管理的注意事项:

  • 当key被删除时,Redis并不会立马归还内存空间给操作系统。这不是因为Redis比较特殊而是和内存空间分配方式有关系:内存空间是按页数分配给进程的,Redis被删除的key可能和有效key占用同一页,只要这一页被数据占用,就不能归还给操作系统。比如Redis存储了5G数据,删除了2G还剩3G,此时Redis进程依旧占用5G内存

  • 前一点意味着你需要根据峰值内存使用量来计算操作系统需要的内存(各个进程的峰值相加)

  • 如果是5G删除了2G还剩3G时,你再添加2G的数据,Redis进程还是占用5G内存,因为它会利用缓存页中的空隙

  • Redis进程占用的内存除以Redis存储的数据量大小(RSS / mem_used)可以得到碎片率,如果峰值很高,那碎片率也会非常高

如果没有设置maxmemory ,Redis进程有可能会把整个操作系统的内存空间全部占用,即便Redis需要存储的数据量非常小(可以想象一下,一个页缓存只分配一个key就可以达到占用的效果,Redis只需要很少的key就能把操作系统的所有缓存页给全部占用,相当于占着茅坑不拉屎,此时的碎片率是真**高)

Work in progress

Work in progress... more tips will be added soon.