百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

如何以最节省内存的方式在Redis中缓存百亿数据?

mhr18 2024-10-24 11:15 19 浏览 0 评论

先讲一个曾经处理过的真实案例!

业务场景:根据人群标签进行互联网广告的定向投放,我们期望每个请求都能足够快地获取到用户的标签信息,尽快完成处理逻辑并响应。

缓存方案:由于数据量是10亿级别,将来可能会是百亿级别,进程内缓存直接不考虑。全都是key-value数据,所以决定采用redis集群作为缓存。

方案很符合常理!于是我们开始向测试的redis集群疯狂输出,很快写了3000万条数据进去,以为一切都会很顺利。但天有不测风云!监控显示内存不足!测试集群三台(4核8G)机器,每台机器上一主一从,这才存了3000万数据而已,真存百亿数据的话内存成本就太高了。

怎么办呢?显然key太多,内存膨胀明显!改用Hash类型存储这些数据,使用一致性哈希取余的方法,将海量用户分配到2^n个Hash对象上,把用户标签存储在Hash对象的field中。于是key的数量有了数量级的下降,且由于Redis对Hash数据的压缩编码,实际节省内存将近80%。

以上是Redis内存优化的方法之一,也是业界很多人都会用的方案。Redis本身提供了很多内存优化方法,继续往下看,我结合官方文章来做一个全面介绍!

1.针对聚合数据类型进行的特殊编码

Redis从2.2版本开始,对许多数据类型都进行了优化,小于一定大小的情况下可以使用更少的存储空间,包括Hash、List、仅由整数组成的Set以及Sorted Set。当不超过最大值时,会以内存高效的方式对数据进行编码,最高可以减少90%的内存占用(平均节省80%),这对于用户和API来说是完全透明的。

马克思主义哲学告诉我们一个真理:任何事物总是存在矛盾的。所以我们经常面临着权衡的问题,不能两全其美的时候,就要寻求平衡。Redis的这种优化就是在内存和CPU之间的权衡,用户可以使用redis.conf中的配置去调整可支持特殊编码的元素最大数量和最大大小,下面是几个相关的配置项:

hash-max-ziplist-entries 512 //hash中最大的field数量
hash-max-ziplist-value 64    //hash中的每个field-name和field-value最大不超过64 bytes
list-max-ziplist-size -2     // -2 8kb,-1 4kb
zset-max-ziplist-entries 128 //zset类型支持压缩编码的最大元素数量
zset-max-ziplist-value 64    //zset中每个元素最大不超过64 bytes
set-max-intset-entries 512   //由64位有符号int组成的set 支持压缩编码的最大元素数量

以上配置在redis官方的redis.conf文件模板中有详细的说明,有兴趣可以去看看。

如果超过了配置的上限,Redis会自动将其转换为普通编码。对于较小的值,这种转换是非常快的。但要是想通过修改配置,对更大的值进行特殊编码,建议做好基准测试,确认转换的耗时,以免影响服务稳定性。

2.使用32位Redis实例

使用32位目标编译的Redis,每个键使用的内存非常少,因为指针比较小。但是,32位Redis实例的内存使用量上限是4GB(指的是key使用的内存,寻址空间只有2的32次方,也就是4GB)。RDB和AOF文件在32位和64位Redis实例上是兼容的(而且也兼容高位字节序和低位字节序),你可以从32位切换为64位,或者相反,都没有问题。

3.位和字节级操作

从Redis2.2开始,引入了一些新的位、字节级操作:GETRANGE、SETRANGE、GETBIT和SETBIT。使用这些命令,你可以将Redis的string类型当成可以随机访问的数组。

假设你有一个应用,使用递增的唯一整数来标识用户,你可以使用bitmap(位图)来保存某个邮件列表的用户订阅情况,每一个bit都代表一个用户的订阅状态。设置指定位代表订阅,清除指定位代表取消订阅。在一个Redis实例中,1亿用户的订阅信息只需要12M内存空间。

4.官方建议:尽可能使用Hash

将数据抽象成内存高效的Hash结构存储在Redis中,这也是上面案例中使用的方法。

对于较小的Hash数据,在编码后会占用很少的空间。所以,尽可能把数据组织成Hash。例如:如果在web应用程序中有表示用户的对象,那么不要为名称、姓氏、电子邮件、密码使用不同的键,而是使用一个包含所有必需字段的Hash。

在Redis中,一定数量的key占用的内存要大于单个key包含一定数量的Hash字段。

如何做到的呢?为了保证查询操作是常数时间,Redis使用了常数时间复杂度的数据结构,比如Hash Table。大多时候Hash只包含少数的几个字段,比较小,此时使用O(N)复杂度的数据结构,就像以键值对组成的线性数组。因为只有N比较小的时候才会这样做,所以HGET和HSET操作所花费的时间平摊下来也是O(1),线性数组可以比哈希表更好地利用CPU缓存(如果不太明白,那你需要复习下计算机原理了)。当Hash所包含元素的数量增长太大,超过最大限制时(可以在redis.conf中修改这个限制),它会被转换成一个真正的哈希表。这里再次体现了两个字:权衡!

不过,Hash的字段并不是拥有完整特性的Redis对象,无法像一个真正的key一样设置过期时间,而且值只能是字符串。但这并没有什么问题,简单比特性丰富更重要,这是Redis官方的设计意图和哲学。

5.关于内存分配

为了保存用户的key,Redis最多分配maxmemory设置所允许的内存,但实际上可能会有少量的额外内存。关于Redis的内存管理,以下几点值得关注:

  • 当某些key被删除后,Redis并不总是把内存返还给操作系统,并不是因为Redis故意这样做,这是大部分内存分配函数的实现方式所导致的。例如一个被5GB数据填充的Redis实例,当我们删除2GB的数据后,RSS(Resident Set Size,驻留集大小,即进程消费的内存页大小,这涉及到操作系统的页式虚拟内存管理)可能依然是5GB左右,即使Redis显示用户使用的内存有3GB左右。这是因为底层操作系统的内存分配器无法轻易地释放内存,被删除的key有可能和其它未被删除的key位于同一个内存页。操作系统是以页为单位给进程分配内存的。
  • 基于上一点,我们需要根据内存使用的峰值来分配内存,假设大部分时间只用5GB左右数据,偶尔需要10GB内存,那也要按照10GB来提供。
  • 内存分配器是很机智的,可以有效利用空闲的内存块,因此当你释放5GB数据中的2GB后,再添加更多key时,会发现RSS是保持稳定的,并不会增长太多。分配器会尝试复用之前被逻辑上释放的2GB内存。

如果maxmemory没有设置,Redis将会在找到合适的内存时继续进行分配,这样会逐步消耗掉所有空闲内存,建议配置一下这个上限。当内存达到上限时再执行写命令,Redis会返回一个内存不足的错误。这可能会导致应用程序的错误,但不会因为内存不足导致整个机器宕机。

以上就是Redis内存优化的一些方法,抛砖引玉,希望对你有所启发。官方的文档里有很多干货,建议大家多看看。

相关推荐

Redis合集-使用benchmark性能测试

采用开源Redis的redis-benchmark工具进行压测,它是Redis官方的性能测试工具,可以有效地测试Redis服务的性能。本次测试使用Redis官方最新的代码进行编译,详情请参见Redis...

Java简历总被已读不回?面试挂到怀疑人生?这几点你可能真没做好

最近看了几十份简历,发现大部分人不是技术差,而是不会“卖自己”——一、简历死穴:你写的不是经验,是岗位说明书!反面教材:ד使用SpringBoot开发项目”ד负责用户模块功能实现”救命写法:...

redission YYDS(redission官网)

每天分享一个架构知识Redission是一个基于Redis的分布式Java锁框架,它提供了各种锁实现,包括可重入锁、公平锁、读写锁等。使用Redission可以方便地实现分布式锁。red...

从数据库行锁到分布式事务:电商库存防超卖的九重劫难与破局之道

2023年6月18日我们维护的电商平台在零点刚过3秒就遭遇了严重事故。监控大屏显示某爆款手机SKU_IPHONE13_PRO_MAX在库存仅剩500台时,订单系统却产生了1200笔有效订单。事故复盘发...

SpringBoot系列——实战11:接口幂等性的形而上思...

欢迎关注、点赞、收藏。幂等性不仅是一种技术需求,更是数字文明对确定性追求的体现。在充满不确定性的网络世界中,它为我们建立起可依赖的存在秩序,这或许正是技术哲学最深刻的价值所在。幂等性的本质困境在支付系...

如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享

如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享在高流量场景下。首先,我需要回忆一下常见的优化策略,比如负载均衡、缓存、数据库优化、微服务拆分这些。不过,可能还需要考虑用户的具体情况,比...

Java面试题: 项目开发中的有哪些成长?该如何回答

在Java面试中,当被问到“项目中的成长点”时,面试官不仅想了解你的技术能力,更希望看到你的问题解决能力、学习迭代意识以及对项目的深度思考。以下是回答的策略和示例,帮助你清晰、有说服力地展示成长点:一...

互联网大厂后端必看!Spring Boot 如何实现高并发抢券逻辑?

你有没有遇到过这样的情况?在电商大促时,系统上线了抢券活动,结果活动刚一开始,服务器就不堪重负,出现超卖、系统崩溃等问题。又或者用户疯狂点击抢券按钮,最后却被告知无券可抢,体验极差。作为互联网大厂的后...

每日一题 |10W QPS高并发限流方案设计(含真实代码)

面试场景还原面试官:“如果系统要承载10WQPS的高并发流量,你会如何设计限流方案?”你:“(稳住,我要从限流算法到分布式架构全盘分析)…”一、为什么需要限流?核心矛盾:系统资源(CPU/内存/数据...

Java面试题:服务雪崩如何解决?90%人栽了

服务雪崩是指微服务架构中,由于某个服务出现故障,导致故障在服务之间不断传递和扩散,最终造成整个系统崩溃的现象。以下是一些解决服务雪崩问题的常见方法:限流限制请求速率:通过限流算法(如令牌桶算法、漏桶算...

面试题官:高并发经验有吗,并发量多少,如何回复?

一、有实际高并发经验(建议结构)直接量化"在XX项目中,系统日活用户约XX万,核心接口峰值QPS达到XX,TPS处理能力为XX/秒。通过压力测试验证过XX并发线程下的稳定性。"技术方案...

瞬时流量高并发“保命指南”:这样做系统稳如泰山,老板跪求加薪

“系统崩了,用户骂了,年终奖飞了!”——这是多少程序员在瞬时大流量下的真实噩梦?双11秒杀、春运抢票、直播带货……每秒百万请求的冲击,你的代码扛得住吗?2025年了,为什么你的系统一遇高并发就“躺平”...

其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。

其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。比如上周有个小伙伴找我,五年经验但简历全是'参与系统设计''优化接口性能'这种空话。我就问他:你做的秒杀...

PHP技能评测(php等级考试)

公司出了一些自我评测的PHP题目,现将题目和答案记录于此,以方便记忆。1.魔术函数有哪些,分别在什么时候调用?__construct(),类的构造函数__destruct(),类的析构函数__cal...

你的简历在HR眼里是青铜还是王者?

你的简历在HR眼里是青铜还是王者?兄弟,简历投了100份没反应?面试总在第三轮被刷?别急着怀疑人生,你可能只是踩了这些"隐形求职雷"。帮3630+程序员改简历+面试指导和处理空窗期时间...

取消回复欢迎 发表评论: