Redis内存优化之: 小体量聚集类型数据的编码优化(译)

栏目: 数据库 · 发布时间: 6年前

内容简介:从Redis2.2开始,许多数据类型都被优化到一定大小,从而减少了占用内存空间。从用户和API的角度来看,这些优化是完全透明的。因为这是CPU/内存的这种,所以我们可以在当一个特殊优化的数据超过配置的最大值时,Redis会自动将其编码类型转为该类型数据的常规编码类型。

Redis 2.2开始,许多数据类型都被优化到一定大小,从而减少了占用内存空间。 Hash , List ,由整数组成的 Set ,和 Sorted set ,当他们的元素数量和单个元素所占内存分别小于给定值时,这些聚集类型会被以非常搞笑利用内存的方式存储,最高可以节省10倍内存(平均可以节省5倍内存)。

从用户和API的角度来看,这些优化是完全透明的。因为这是CPU/内存的这种,所以我们可以在 redis.conf 配置文件中如下指令,通过修改某个特定类型下元素最大数量和元素自身最大内存的值来调优。

hash-max-zipmap-entries 512 (hash-max-ziplist-entries for Redis >= 2.6)
hash-max-zipmap-value 64  (hash-max-ziplist-value for Redis >= 2.6)
list-max-ziplist-entries 512
list-max-ziplist-value 64
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
set-max-intset-entries 512
复制代码

当一个特殊优化的数据超过配置的最大值时,Redis会自动将其编码类型转为该类型数据的常规编码类型。

使用32位实例

使用32位target编译的redis下,每个key能少用很多内存,因为指针会小很多,但是最大使用内存也会被限制在4G。要想编译成32位二进制的redis,可以使用 make 32bitRDBAOF 在32位和64位实例上是兼容的(当然在大端和小段之间上也是兼容的),所以你可以从32位切换到64位,反过来也一样。

位和字节级别的操作

Redis 2.2 引进了新的字节和位级别的操作: GETRANGE , SETRANGE , GETBIT , SETBIT 。使用这些指令,你可以把string类型当做可以随机访问的数组。比如说,你有一个应用,应用里用户通过一个唯一的渐进整数来识别,你可以使用位图来保存用户的性别信息,可以在位上女性置1,男性置0,或者用其他方式。在Redis实例里,10亿用户的性别数据只需要12M RAM 。你可以使用 GETRANGE , SETRANGE 来存储一个字节长度的每个用户信息。这里只是个例子,但是我们有可能使用这些新指令解决很多小空间时面临的问题。

如有可能,尽量使用Hash

小的hash会使用占用很小空间的编码格式,所以你应该可能的使用 hash 来存储你的数据。比如说你有一个网站用户对象,你应该使用单个hash来存储所有的字段,而非把用户的名字,性别,年龄等都存储成不同的k-v。

在redis上使用哈希抽象出一个非常节省内存的键值存储

我理解这节的话题有点吓人,但是我会详细解释它。

基本上,可以使用redis对普通的键值存储进行建模,其中的值可以只是字符串,这不仅比redis普通的键值更节省内存,而且比 memcached 更节省内存。

让我们从一个事实开始:少量的键比包含少量字段的哈希的单个键使用更多的内存。这怎么可能?我们使用了一个技巧。从理论上讲,为了保证我们在常数时间内执行查找(在大O符号中也称为O(1)),需要在平均情况下使用具有常数时间复杂度的数据结构,例如哈希表。

但是很多时候,哈希只包含很少的字段。当哈希比较小的时候,我们可以使用 O(N) 复杂度的数据结构对其编码,比如带有长度前缀的键值对线性数组。因为我们只在 N 比较小的时候才这么做, HGETHSET 的均摊时间仍然是 O(1) ,当哈希包含的元素个数增长到足够大时 (你可以在redis.conf里配置这个限制),它将被转换为真正的哈希表。

从时间复杂度的角度看,这并不能很好的工作,但从常量时间的角度来看却相反,因为通过CPU缓存,一个键值对的线性数组恰好能够工作的很好。

但是,因为哈希的字段( field )和值( value )(通常)不能像Redis对象那样表现出所有的特性,哈希的字段没有类似Redis键那样与之关联的生存时间(过期时间),仅仅包含一个字符串。但这对我们来说是没问题的,这就是设计哈希数据结构API时的意图(我们相信简单优于特性,所以不运行内嵌数据结构,单个字段的过期属性也是不支持的)。

所以,哈希是内存高效的。所以,在表示对象或者对包含一组相关字段的问题建模时使用哈希是很有用的。但是对于只有普通键值对的业务又该如何呢?

比如我们使用Redis存储一些小对象,可以是json编码的对象、小的HTML块,简单的键->布尔值对等等。本质上,都是小型键和小型值的字符串->字符串的映射。

现在我们假设要缓存的对象是数字的,比如:

Ojbect:1234
Ojbect:12
34

所以我们用处理最后两个字符以外的字符作为 key ,而最后两个字符作为哈希的字段名,为了设置我们的键,可以使用如下命令: HSET object:12 34 somevalue

正如你看到的,所有的哈希最终都能包含100个(00-99)字段( field ),这是CPU和内存之间的最佳折中。

还有另外一个很重要的事需要注意,使用这个模式时,无论我们有多少数量的对象要缓存,我们都只能缓存100个左右的字段。这是因为,我们的对象最终都是以数字结尾,而不是随机字符串。在某种程度上,最后的数字可以看作是隐式预分片( pre-sharding )的一种方式。

那么小数字呢?比如 Object:2 ? 我们可以把 Object: 当作键名,整个数字当作哈希的字段名。所以 Object:10Object:2 都以 Ojbect: 为键名,但是前者以``0 当作字段名,后者则是 2`。

这种方式下我们能节省多少内存?

我使用下面的 Ruby 程序来测试这是如何工作的:

require 'rubygems'
require 'redis'

UseOptimization = true

def hash_get_key_field(key)
    s = key.split(":")
    if s[1].length > 2
        {:key => s[0]+":"+s[1][0..-3], :field => s[1][-2..-1]}
    else
        {:key => s[0]+":", :field => s[1]}
    end
end

def hash_set(r,key,value)
    kf = hash_get_key_field(key)
    r.hset(kf[:key],kf[:field],value)
end

def hash_get(r,key,value)
    kf = hash_get_key_field(key)
    r.hget(kf[:key],kf[:field],value)
end

r = Redis.new
(0..100000).each{|id|
    key = "object:#{id}"
    if UseOptimization
        hash_set(r,key,"val")
    else
        r.set(key,"val")
    end
}
复制代码

这是在一个Redis2.2版本的64位实例上运行的结果:

  • UseOptimization 选项为 true 的时候: 使用了1.7 MB的内存
  • UseOptimization 选项为 false 的时候: 使用了11 MB的内存

这是一个数量级(an order of magnitude)的差距,我认为这或多或少的使Redis成为最省内存的普通键值存储。

警告 为了使其工作,你需要保证在redis.conf文件里存在如下设置:

hash-max-zipmap-entries 256

还要记得根据你键值对的最大值来设置下面的字段:

hash-max-zipmap-value 1024

每当哈希超出设置的最大数量,或者值超出了设置的最大体量,哈希都会被转化为真正的哈希表,内存节省的特性也就随之丢失。

你也许会问,为什么不在普通键空间上显示的做这些,有两个原因:一个是我们倾向于显式地权衡,而这里正是一个显式地权衡:CPU,内存,最大元素大小。另一个原因就是顶级的键空间需要支持许多有趣的特性,比如过期时间,LRU数据等等,所以普世地使用这种方法并不实用。

但是redis的方式是用户必须理解Reids是如何工作的,从而能够更好的选择折衷方案,以及能够更好的了解系统确切行为方式。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

图形程序开发人员指南

图形程序开发人员指南

Michael Abrash / 前导工作室 / 机械工业出版社 / 1998 / 128

Michael Abrash's classic Graphics Programming Black Book is a compilation of Michael's previous writings on assembly language and graphics programming (including from his "Graphics Programming" column......一起来看看 《图形程序开发人员指南》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具