如何以最节省内存的方式在Redis中缓存百亿数据?
nanshan 2024-12-10 18:56 12 浏览 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内存优化的一些方法,抛砖引玉,希望对你有所启发。官方的文档里有很多干货,建议大家多看看。
相关推荐
- 0722-6.2.0-如何在RedHat7.2使用rpm安装CDH(无CM)
-
文档编写目的在前面的文档中,介绍了在有CM和无CM两种情况下使用rpm方式安装CDH5.10.0,本文档将介绍如何在无CM的情况下使用rpm方式安装CDH6.2.0,与之前安装C5进行对比。环境介绍:...
- ARM64 平台基于 openEuler + iSula 环境部署 Kubernetes
-
为什么要在arm64平台上部署Kubernetes,而且还是鲲鹏920的架构。说来话长。。。此处省略5000字。介绍下系统信息;o架构:鲲鹏920(Kunpeng920)oOS:ope...
- 生产环境starrocks 3.1存算一体集群部署
-
集群规划FE:节点主要负责元数据管理、客户端连接管理、查询计划和查询调度。>3节点。BE:节点负责数据存储和SQL执行。>3节点。CN:无存储功能能的BE。环境准备CPU检查JDK...
- 在CentOS上添加swap虚拟内存并设置优先级
-
现如今很多云服务器都会自己配置好虚拟内存,当然也有很多没有配置虚拟内存的,虚拟内存可以让我们的低配服务器使用更多的内存,可以减少很多硬件成本,比如我们运行很多服务的时候,内存常常会满,当配置了虚拟内存...
- 国产深度(deepin)操作系统优化指南
-
1.升级内核随着deepin版本的更新,会自动升级系统内核,但是我们依旧可以通过命令行手动升级内核,以获取更好的性能和更多的硬件支持。具体操作:-添加PPAs使用以下命令添加PPAs:```...
- postgresql-15.4 多节点主从(读写分离)
-
1、下载软件[root@TX-CN-PostgreSQL01-252software]#wgethttps://ftp.postgresql.org/pub/source/v15.4/postg...
- Docker 容器 Java 服务内存与 GC 优化实施方案
-
一、设置Docker容器内存限制(生产环境建议)1.查看宿主机可用内存bashfree-h#示例输出(假设宿主机剩余16GB可用内存)#Mem:64G...
- 虚拟内存设置、解决linux内存不够问题
-
虚拟内存设置(解决linux内存不够情况)背景介绍 Memory指机器物理内存,读写速度低于CPU一个量级,但是高于磁盘不止一个量级。所以,程序和数据如果在内存的话,会有非常快的读写速度。但是,内存...
- Elasticsearch性能调优(5):服务器配置选择
-
在选择elasticsearch服务器时,要尽可能地选择与当前业务量相匹配的服务器。如果服务器配置太低,则意味着需要更多的节点来满足需求,一个集群的节点太多时会增加集群管理的成本。如果服务器配置太高,...
- Es如何落地
-
一、配置准备节点类型CPU内存硬盘网络机器数操作系统data节点16C64G2000G本地SSD所有es同一可用区3(ecs)Centos7master节点2C8G200G云SSD所有es同一可用区...
- 针对Linux内存管理知识学习总结
-
现在的服务器大部分都是运行在Linux上面的,所以,作为一个程序员有必要简单地了解一下系统是如何运行的。对于内存部分需要知道:地址映射内存管理的方式缺页异常先来看一些基本的知识,在进程看来,内存分为内...
- MySQL进阶之性能优化
-
概述MySQL的性能优化,包括了服务器硬件优化、操作系统的优化、MySQL数据库配置优化、数据库表设计的优化、SQL语句优化等5个方面的优化。在进行优化之前,需要先掌握性能分析的思路和方法,找出问题,...
- Linux Cgroups(Control Groups)原理
-
LinuxCgroups(ControlGroups)是内核提供的资源分配、限制和监控机制,通过层级化进程分组实现资源的精细化控制。以下从核心原理、操作示例和版本演进三方面详细分析:一、核心原理与...
- linux 常用性能优化参数及理解
-
1.优化内核相关参数配置文件/etc/sysctl.conf配置方法直接将参数添加进文件每条一行.sysctl-a可以查看默认配置sysctl-p执行并检测是否有错误例如设置错了参数:[roo...
- 如何在 Linux 中使用 Sysctl 命令?
-
sysctl是一个用于配置和查询Linux内核参数的命令行工具。它通过与/proc/sys虚拟文件系统交互,允许用户在运行时动态修改内核参数。这些参数控制着系统的各种行为,包括网络设置、文件...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)