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

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

mhr18 2024-10-24 11:15 25 浏览 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内存优化的一些方法,抛砖引玉,希望对你有所启发。官方的文档里有很多干货,建议大家多看看。

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: