首页 > 编程知识 正文

redis单个value大小(redis多大key算大key)

时间:2023-05-03 18:00:49 阅读:85265 作者:845

资料来源:公众号转行程序员

作者:王爆

目录

1 )单个简单的密钥存储的值很大

)2:hash、set、zset、list中存在过剩的元素

3 ) 1个集群中保存了数亿个key

4 )大位图或光晕滤波器(Bloom )分割

背景

在商业场景中,通常有各种各样的大密钥多密钥,包括:

1 )单个简单的密钥存储的值很大

2 )2:混合、设定、zset、list中存储了过剩的元素(万个单位)

3 )一个集群中保存了数亿个key,即使key本身太多,也占用了更多的空间

(如果不出意外,文章中提到的hash、set等数据结构指的是redis的数据结构)

由于redis运行在单线程上,因此一次操作的value很可能会对redis的整体响应时间产生负面影响,如果业务上可以分解,请将其分解。 列举几个典型的分解方案。

1 )单个简单的密钥存储的值很大

I :每次都需要对这个对象进行整体缓存

尝试将对象分割为几个key-value,然后使用多获取值。 这个分割的意思是分割一次操作的压力,在多个redis实例中均衡操作的压力,降低IO对一个redis的影响。

)这个对象只需要一次访问一部分数据

可以像第一种方法一样分割为几个密钥值,也可以将其存储在一个散列中。 每个字段代表一个特定的属性。

使用hget、hmget获取部分值,使用hset、hmset更新部分属性

2 )2:value中存储了过剩的要素

可以像场景的第一种方法一样分割这些元素。

以hash为例,原来的通常的访问过程是Hget(HashKey,field ); hset (散列密钥、场、值) ) )。

现在,固定一桶数,例如10000,每次访问时,首先在本地计算字段的哈希值,然后对10000进行模拟,以确定该字段掉在哪个key上。

newhashkey=hashkey(set、zset、list也能够如上所述地进行

但是,也有不恰当的场景。 例如,为了确保lpop的数据确实最初推送到list,这需要附加的属性。 或者,需要对连接key进行一些工作。 例如,list按时间分割。

3 ) 1个集群中保存了数亿个key

过多的key会占用更多的内存空间

i:key自身的占有(每个key都带有Category前缀) () ) ) ) ) () ) ) ) ) ) ) ) (注) ) ) ) ) ) (注) ) ) ) ) ) ) )

ii )在集群模型中,服务端需要建立几个slot2key的映射关系,其中的指针在key多的情况下也浪费巨大的空间

这两个方面很明显,key个数达到亿个时会消耗内存(Redis 3.2以下的版本存在这个问题,在4.0中进行了优化)。

因此,可以通过减少key的数量来减少内存消耗。 值得参考的方案是迁移到Hash结构存储。 也就是说,以前直接使用Redis String的结构存储,现在将多个关键点存储在一个散列结构中。 具体场景的参考如下。

align-justify">一:key 本身就有很强的相关性,比如多个key 代表一个对象,每个key是对象的一个属性,这种可直接按照特定对象的特征来设置一个新Key——Hash结构, 原先的key则作为这个新Hash 的field。

举例说明:

原先存储的三个key

user.zhangsan-id = 123;

user.zhangsan-age = 18;

user.zhangsan-country = china;

这三个key本身就具有很强的相关特性,转成Hash存储就像这样 key = user.zhangsan

field:id = 123;

field:age = 18;

field:country = china;

即redis中存储的是一个key :user.zhangsan, 他有三个 field, 每个field + key 就对应原先的一个key。

二:key 本身没有相关性,预估一下总量,采取和上述第二种场景类似的方案,预分一个固定的桶数量

比如现在预估key 的总数为 2亿,按照一个hash存储 100个field来算,需要 2亿 / 100 = 200W 个桶 (200W 个key占用的空间很少,2亿可能有将近 20G )

原先比如有三个key :

user.123456789

user.987654321

user.678912345

现在按照200W 固定桶分就是先计算出桶的序号 hash(123456789) % 200W , 这里最好保证这个 hash算法的值是个正数,否则需要调整下模除的规则;

这样算出三个key 的桶分别是 1 , 2, 2。 所以存储的时候调用API hset(key, field, value),读取的时候使用 hget (key, field)

注意两个地方:1,hash 取模对负数的处理; 2,预分桶的时候, 一个hash 中存储的值最好不要超过 512 ,100 左右较为合适

4:大Bitmap或布隆过滤器(Bloom )拆分

使用bitmap或布隆过滤器的场景,往往是数据量极大的情况,在这种情况下,Bitmap和布隆过滤器使用空间也比较大,比如用于公司userid匹配的布隆过滤器,就需要512MB的大小,这对redis来说是绝对的大value了。

这种场景下,我们就需要对其进行拆分,拆分为足够小的Bitmap,比如将512MB的大Bitmap拆分为1024个512KB的Bitmap。不过拆分的时候需要注意,要将每个key落在一个Bitmap上。有些业务只是把Bitmap 拆开, 但还是当做一个整体的bitmap看, 所以一个 key 还是落在多个 Bitmap 上,这样就有可能导致一个key请求需要查询多个节点、多个Bitmap。

如下图,被请求的值被hash到多个Bitmap上,也就是redis的多个key上,这些key还有可能在不同节点上,这样拆分显然大大降低了查询的效率。

因此我们所要做的是把所有拆分后的Bitmap当作独立的bitmap,然后通过hash将不同的key分配给不同的bitmap上,而不是把所有的小Bitmap当作一个整体。这样做后每次请求都只要取redis中一个key即可。

有同学可能会问,通过这样拆分后,相当于Bitmap变小了,会不会增加布隆过滤器的误判率?实际上是不会的,布隆过滤器的误判率是哈希函数个数k,集合元素个数n,以及Bitmap大小m所决定的,其约等于

因此如果我们在第一步,也就是在分配key给不同Bitmap时,能够尽可能均匀的拆分,那么n/m的值几乎是一样的,误判率也就不会改变。具体的误判率推导可以参考wiki:Bloom_filter

同时,客户端也提供便利的api (>=2.3.4版本), setBits/ getBits 用于一次操作同一个key的多个bit值 。

建议 :k 取 13 个, 单个bloomfilter控制在 512KB 以下

以上方案仅供参考,欢迎大家提供其他的优秀方案。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。