Redis中一个String类型引发的惨案

机器学习系统鲁棒性:从模型上线到生产稳定的全链路工程实践 机器学习系统鲁棒性是指模型在真实生产环境中持续、稳定、可解释地输出可靠决策的能力,其核心在于数据管道韧性、服务契约一致性与在线行为可预测性。不同于离线评估指标,鲁棒性依赖特征时效性保障、输入/输出漂移检测、熔断降级机制及端到端延迟控制等工程能力。它直接支撑金融反欺诈、实时信贷、个性化推荐等对SLA敏感的关键场景,是MLOps落地的本质要求。本文聚焦于企业级ML系统中高频发生的特征断裂、类型漂移、Fallback失控与监控盲区等典型问题,结合银行与电商实战经验,系统拆解如何构建具备弹性集成、可观测性与自动归因 阅读详情

Redis中一个String类型引发的惨案

曾经看到这么一个案例,有一个团队需要开发一个图片存储系统,要求这个系统能快速记录图片ID和图片存储对象ID,同时还需要能够根据图片的ID快速找到图片存储对象ID。我们假设用10位数来表示图片ID和图片存储对象ID,例如图片的ID为1101021043,它所对应的图片存储对象的ID为2301010051,可以看到图片ID和图片存储ID正好是一一对应的,是典型的key-value形式,所以首先会想到直接使用String类型来保存数据。把图片ID和图片存储ID分别作为键值对的key和value来保存。但是随着存储的数据量越来越大,Redis的内存的使用量也快速上升,结果遇到了大内存Redis实例因为生成RDB而响应变慢的问题。很显然String类型并不是一种好的选择,

那有什么办法可以降低内存消耗吗?

String类型的数据结构

首先我们得先了解为什么String保存数据时所消耗的内存空间较大。在刚才的案例中,由于图片ID和图片存储对象ID都是10位数,我们可以用两个8字节的Long类型来表示这两个ID。所以一组图片ID及其存储对象ID的记录,实际只需要16字节就可以了。但是通过对Redis内存分析,一组图片ID及其存储对象ID却占用了64字节,那为什么String类型会用64字节呢。其实,除了要记录实际的数据,String类型还需要额外的内存空间来记录数据的长度、空间使用信息等,这些信息也叫做元数据。当实际保存的数据较小时,元数据的空间开销就显的比较大了。我们先来看一下String类型是如何保存数据的。当你保存64位有符号的整数时,String类型会把它保存为一个8字节的Long类型整数,这种保存方式通常也叫作int编码方式。但是,当你保存的数据中包含字符时,String类型就会用简单动态字符串结构体(SDS)来保存。如下图所示:

  • len:4个字节,表示buf的已用长度。
  • alloc:4个字节,表示buf分配的长度,一般大于len。
  • buf:字节数组,保存实际数据。为了表示数组的结尾,Redis会自动在数组最后添加一个”\0"。

可以看到,在SDS结构体中,除了有保存实际数据的buf,还有len和alloc的额外元数据的开销。另外对于String类型来说,除了SDS的额外开销外,还有一个叫做RedisObject结构体的开销。因为Redis的数据类型有很多,不同的数据类型都有相同的元数据要记录(例如最后一次访问时间),所以Redis会采用一个叫做RedisObject结构体来统一记录这些元数据。一个RedisObject包含了一个8字节的元数据和一个8字节的指针,这个指针指向具体数据所在,例如String类型的SDS结构体所在的内存地址。如下图所示:

为了节省内存空间,Redis对Long类型整数和SDS的内存布局做了专门的设计。一方面,当保存的是 Long 类型整数时,RedisObject 中的指针就直接赋值为整数数据了,这样就不用额外的指针再指向整数了,节省了指针的空间开销。另一方面,当保存的是字符串数据,并且字符串小于等于 44 字节时,RedisObject 中的元数据、指针和 SDS 是一块连续的内存区域,这样就可以避免内存碎片。这种布局方式也被称为 embstr 编码方式。当字符串大于44字节时,SDS的数据量就开始变多了,Redis 就不再把SDS 和 RedisObject 布局在一起了,而是会给 SDS 分配独立的空间,并用指针指向 SDS 结构。这种布局方式被称为 raw 编码模式。如下图所示:

现在我们来计算一下一对图片ID和图片存储对象ID的内存的使用量。由于10位数的图片ID和图片存储对象ID是Long类型整数,所以可以直接用int编码的RedisObject保存。相对应的RedisObject元数据部分占8字节,指针部分被直接赋值为8字节的整数了。此时,每个ID会使用16字节,加起来一共是32字节。但是,另外的 32 字节去哪儿了呢?

由于Redis是使用全局哈希表来保存所有的键值对,哈希表的每一项是一个dictEntity的结构体来指向一个键值对。dictEntity由三个8字节的指针组成,分别来指向key、value以及下一个dictEntity。如下图所示。

由于Redis使用的内存分配库为jemalloc,jemalloc在分配内存时,会根据申请的字节数N,找一个比N大的,最接近N的2的幂次数作为分配的空间。所以申请一个24字节的dictEntity,实际会分配32个字节。到目前位置,你应该明白了为什么String类型来保存图片ID和图片存储对象ID会占用64个字节了。一个有效信息只有16个字节,在使用String类型保存时,却要占用64个字节内存空间,有48个字节用来保存元数据信息了,这是不是极大的浪费了内存空间。那么有没有更加节省内存的方法呢?

用压缩列表节省内存

Redis里有一种叫做压缩列表的结构,非常节省内存。我们先回顾一下压缩列表的构成。表头有三个字段zlbytes、zllen和zltail,分别表示列表的长度、列表尾的偏移量以及列表中entry的个数。压缩列表表尾有一个zlend,表示列表结束。如下图所示。

由于压缩列表采用一系列的entry保存数据,这些entry会挨个儿放置在内存中,不需要再用额外的指针进行连接,这样就可以节省指针所占用的空间。每个entry由以下几部分组成。

  • pre_len:表示前一个entry的长度。prev_len有两种取值情况:1 字节或 5 字节。当上一个 entry 长度小于 254 字节时,prev_len 取值为 1 字节,否则,就取值为 5 字节。
  • len:表示自身的长度,占4个字节。
  • encoding:表示编码方式,占1个字节。
  • content:保存实际数据。

假设我们使用entry来保存图片存储对象ID(占8个字节),此时,每个entry的prev_len占用1个字节就行,因为每一个entry的前一个entry的长度小于264字节。这样一来,一个图片对象ID所占用的内存大小是14(1+4+1+8)个字节,实际上会分配16个字节。

Redis里基于压缩列表实现了List、Hash和Sorted Set集合类型,这样做的最大好处就是节省了dictEntity的内存开销。对于String类型来说,一个键值对就有一个dictEntity,占用32个字节。对于集合类型来说,一个key对应了很多数据,却只是占用了一个dictEntity,这样就节省了内存空间。

如何用集合类型存储单值的键值对的数据

在保存单值键值对的数据时,我们可以使用基于Hash类型的二级编码方式。这里所说的二级编码,是指把单值的数据拆成两部分,前一部分作为Hash的key,后一部分作为Hash的value。     以图片的ID为1101021043,它所对应的图片存储对象的ID为2301010051为例,我们将图片的ID的前7位(1101021)作为Hash类型的键,后3位(043)和图片存储对象ID为2301010051作为Hash类型的key和value。我们按照这种设计,在Redis中插入一条记录,只占用了16字节,所以和使用String类型占用64字节对比,节省了很多空间。

最后,我们再思考一个问题,为什么要把图片ID的前7位作为Hash类型的键,后3位作为Hash类型的key呢。我们在Redis存储结构里介绍过Redis的Hash类型的两种底层实现结构,分别是压缩列表和哈希表。Hash 类型设置了用压缩列表保存数据时的两个阈值,一旦超过了阈值,Hash 类型就会用哈希表来保存数据了。这两个阈值分别对应以下两个配置项:

  • hash-max-ziplist-entries:表示用压缩列表保存时哈希集合中的最大元素个数。
  • hash-max-ziplist-value:表示用压缩列表保存时哈希集合中单个元素的最大长度。

在内存节省空间方面,哈希表就没有压缩列表那么高效。我们只用后3位作为Hash类型的key,也就保证哈希集合中元素的个数不会超过1000,同时我们通过设置hash-max-ziplist-entries=1000,来确保Hash类型底层使用的是压缩列表这种数据结构。

【redis】Redis中一个String类型引发的惨案 我们在Redis存储结构里介绍过Redis的Hash类型的两种底层实现结构,分别是压缩列表和哈希表。Hash 类型设置了用压缩列表保存数据时的两个阈值,一旦超过了阈值,Hash 类型就会用哈希表来保存数据了。 阅读详情

相关推荐

SAP 移动类型简述

SAP系统中用于标志物料移动(收货/发货/转储/转储记帐)的三位的数字/字母 通常分几大类:收货 发货 转储 库房调整 常用的有: 收货类 (MB1C) 101 采购订单收货 103 采购订单收货至冻结状态 122 无采购订单退货 161 按退货采购订单退货 131 从生产订单 501 无采购订单收货 511 免费收货 发货类(MB1A) 201 成本中心消耗

forever_crazy的专栏 7203

Redis数据结构为字符串String的实践 之 分别存储字符串和存储对象(下)

一.项目用redis-learn参考文章,存储字符串可以看上文 本文我们实际操作下 数据结构字符串String 分别存储字符串和存储对象 二. 存储对象: 一般情况下,我们做redis会和数据库合起来使用,如果在redis中查不到,就到数据库中查,有查到再保存到redis中。 2.1 配置数据库,用的是mysql和mybatis-plus,添加依赖,代码如下: <depe...

weixin_40991408的博客 471

KEIL5编译报错L6200E:__stdout重定义问题分析与解决

本文深入分析KEIL5编译报错L6200E中__stdout重定义问题的根源,指出因未完整定义__stderr导致与标准库stdio_streams.o冲突。提供完整重定义标准流、启用MicroLIB库等解决方案,帮助开发者彻底解决Symbol multiply defined错误,提升嵌入式开发效率。

uran的专栏 957

Redis使用String类型中遇到的问题

string是redis最基本的类型

java_zhangfei的博客 696

libopus 实现pcm 编码到opus

opus 是一种音频格式,常用于语音通话、视频会议中。最近做了个pcm 到opus 的编码,踩了不少坑,特此记录一下。

sinat_27720649的博客 9476

(UE4 4.27) HISM性能优化:从空间剔除到合批渲染的实战解析

本文深入解析了UE4 4.27中HISM(UHierarchicalInstancedStaticMesh)组件的性能优化原理与实战技巧。核心在于利用其N叉树空间结构实现高效的CPU视锥剔除,并结合GPU实例化进行合批渲染。文章详细探讨了从控制叶子节点粒度、平衡剔除精度与渲染效率,到结合LOD系统等关键参数的调优方法,为开放世界或大场景的植被性能瓶颈提供了系统解决方案。

weixin_29015051的博客 328

Redis:string类型带来的空间消耗过大的问题。

案例:使用Redis的string类型保存图像ID和图片存储对象 ID,图像ID为Key、图片存储对象ID为Value。两者均为10位纯数字。保存1亿对键值消耗空间6.4GB,平均1个键值对消耗64字节。 问题:按理来说8个字节可以表示2的64次方-1,超过了10位数字,案例中键值对加起来消耗16字节也就罢了,为什么消耗了64字节? 回答:除了记录实际数据,String 类型还需要额外的内存空间记录数据长度、空间使用等信息,这些信息也叫作元数据。当实际保存的数据较小时,元数据的空间开销就显得比较大了。 St

weixin_40108561的博客 1862

Redis的数据结构之字符串 0

五种数据类型: 字符串(String) 字符串列表(list) 有序字符串集合(sorted set) 哈希(hash) 字符串集合(set) Key定义的注意点: 不要过长, 不要过短, 统一的命名规范 存储String 二进制安全的,存入和获取的数据相同 Value最多可以容纳的数据长度是512M

弹吉他的ZTT的博客 356

ESP32-S3开发环境避坑指南:VS Code+ESP-IDF离线安装全流程(附国内镜像加速)

本文提供了一份详细的ESP32-S3开发环境离线安装指南,重点解决网络依赖问题。通过整合国内镜像加速,手把手教你完成VS Code、ESP-IDF框架、工具链及Python依赖的本地化部署,实现稳定、可重复的开发环境搭建,大幅提升IoT开发效率。

peach的博客 962

uniapp [全端兼容] - 实现接入使用天地图及卫星视图切换功能,在国家天地图上自由切换地图显示类型(卫星视图/地形视图/卫星混合/地形混合/普通视图)天地图切换视图显示改成卫星风格,切换地图图层

uni-app,h5移动端网页,微信小程序,支付宝小程序,Android安卓,苹果ios,app,nvue,天地图,视图,卫星,地图类型切换,uniAPP天地图,改成卫星视角,在线地图切换,来回切换卫星图,天地图如何实现来回切换卫星图,地形图,卫星视图,VUE3引入天地图卫星版,详细教程示例源码代码,天地图切换卫星用什么api,卫星视角API方法,vue3卫星天地图,能改地图的显示方式,vue3中使用天地图自定义样式切换地图图层,切换报错,加载出错,卫星图太卡,地图图层更换,vue3安装集成天地图详细示例源

🏆 前端领域优质创作者 1万+

一个由String、StringBuilder、StringBuffer 引发的面试惨案

前言: String,StringBuilder,StringBuffer的区别是啥?这个面试题估计每个程序员都应该碰到过吧。依稀记得第一次面试的时候,面试官问我这个问题时,心想着这不是很简单吗。深入了解这个问题后,发现这里面并不简单,面试官的套路还是深啊! 面试官:你好,欢迎来面试,介绍一下自己吧。 你好,我是**, 来自 **,毕业于拖拉机学院,目前工作了2年,在 ** 公司做了一年的开发,做过的项目有。。。布拉布拉 然后双方对项目进行深入的讨论。。。 然后进行基础问答环节 下面

Javaesandyou的博客 507

块存储:AIO的直接写流程注释

提交io流程中aio_write之前函数的注释,可参考“块存储:AIO的直接读流程注释”。

yiyeguzhou100的专栏 1335

由String、String Builder、String Buffer 引发的面试惨案,面试套路深回答需谨慎

前言: String,StringBuilder,StringBuffer的区别是啥?这个面试题估计每个程序员都应该碰到过吧。依稀记得第一次面试的时候,面试官问我这个问题时,心想着这不是很简单吗。深入了解这个问题后,发现这里面并不简单,面试官的套路还是深啊! 面试官:你好,欢迎来面试,介绍一下自己吧。 你好,我是**, 来自 **,毕业于拖拉机学院,目前工作了2年,在 ** 公司做了一年的开发,做过的项目有。。。布拉布拉 然后双方对项目进行深入的讨论。。。 然后进行基础问答环节 下面挖坑套路来了

banzhuanhu的博客 5095

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Redis数据结构底层编码原理与性能优化实战

在分布式缓存与数据存储领域,数据结构是系统性能与效率的核心基础。其底层编码实现原理直接决定了内存使用效率与操作性能,是进行系统调优和问题排查的关键。Redis作为高性能键值数据库,其核心价值在于通过智能的编码转换机制,在内存占用与操作复杂度之间取得最佳平衡,从而支撑高并发、低延迟的应用场景。例如,对于Hash结构,当字段较少且值较短时,会采用内存紧凑的ziplist编码;而当数据规模增长时,则会自动转换为查询更快的hashtable编码。理解这些编码的转换阈值与行为,例如通过调整hash-max-zipli

weixin_33812433的博客 392

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

分布式锁原理与实现:Redis、Zookeeper方案对比

分布式锁是解决分布式系统中资源竞争问题的关键技术,其核心在于保证互斥性、避免死锁和具备容错性。从实现原理来看,分布式锁通过全局可见的标记机制协调多节点访问,常见的技术方案包括基于Redis的SETNX命令、Zookeeper的临时节点等。在电商秒杀、定时任务调度等高并发场景中,合理的分布式锁设计能有效防止超卖、重复执行等问题。本文重点对比了Redis与Zookeeper两种主流实现方案:Redis凭借高性能和Redisson框架的看门狗机制适合大多数场景,而Zookeeper则通过临时顺序节点和Watche

weixin_30776545的博客 303

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Java面试高频考点:JUC并发与Spring三级缓存实战解析

Java并发编程中,生产者-消费者模型是经典的多线程协作案例,其核心在于通过wait/notify机制或JUC工具(如ReentrantLock、Condition)实现线程间通信。理解这些并发原语的工作原理,能够帮助开发者构建高性能、线程安全的系统。Spring框架的三级缓存机制则是解决循环依赖问题的关键技术,通过提前暴露Bean引用(ObjectFactory)确保依赖注入的正确性。掌握这些底层原理,不仅能应对技术面试中的深度追问,更能提升分布式系统开发能力。本文结合大厂真实面试案例,详解JUC并发工具

weixin_34293141的博客 324

分布式系统高可用性陷阱:从配置漂移到服务雪崩的实战防御

在分布式系统架构中,服务雪崩和配置漂移是导致系统不可用的核心风险。服务雪崩描述了当某个关键服务故障时,故障会沿调用链向上游扩散,最终导致整个系统瘫痪的链式反应过程。配置漂移则指生产环境配置逐渐偏离标准定义,常由手动修改未回滚或环境同步错误引发,成为系统稳定性的隐形杀手。这些问题的技术价值在于,它们直接关系到系统的韧性与业务连续性。在电商秒杀、在线竞赛等高并发场景下,一个微小的配置错误或服务超时就可能触发全链路崩溃。本文通过一个典型的线上事故案例,深入剖析了配置管理疏忽如何演变为服务雪崩,并提供了从配置中心化

weixin_33919950的博客 299

WordPress 500错误排查:从NGINX、PHP到MySQL的全链路诊断

500 Internal Server Error 是 Web 应用最典型的服务器端错误,本质是后端执行流程在某环节中断后返回的 HTTP 状态码。其底层原理涉及请求生命周期中的多层协作:Web 服务器(如 NGINX)接收请求并转发,应用服务器(如 PHP-FPM)解析执行脚本,框架(如 WordPress)加载逻辑,最终与数据库(MySQL)交互。技术价值在于它不掩盖问题,而是精准暴露执行链中的薄弱点——可能是 PHP 语法错误、内存耗尽、MySQL 连接失败或 NGINX 配置失当。典型应用场景包括

weixin_30787531的博客 369

API限流技术解析:从算法原理到生产实践

API限流是保障系统稳定性的关键技术,通过控制请求流量防止服务过载。其核心原理包括令牌桶、漏桶等算法,令牌桶允许突发流量处理,漏桶则强制恒定速率。在分布式系统中,需结合Redis等中间件实现原子计数。典型应用场景包括电商秒杀防御、AI服务成本控制等,其中电商大促场景常面临每秒10万+请求的挑战。现代限流方案还需考虑多维度规则(用户等级、接口权重)和动态配置,配合熔断降级机制形成完整保护体系。生产环境中,合理的限流策略能降低78%异常请求率,是构建高可用架构的重要组件。

379

机器学习模型生产化部署:FastAPI+Docker+K8s工程实践

机器学习模型部署不是简单运行predict函数,而是将算法能力转化为高可用、可观测、可维护的在线服务。其核心在于解决开发态与生产态之间的断层——包括并发承载力不足、环境不一致、缺乏可观测性等典型问题。FastAPI凭借异步支持、Pydantic契约校验和OpenAPI自动文档,成为现代ML服务首选框架;Docker镜像精简(如distroless基座)保障安全与启动效率;Kubernetes则提供弹性扩缩、灰度发布与健康治理能力。本文聚焦真实落地场景,覆盖API设计、模型热加载、压测调优、K8s YAML工

397

(11)Redis之海量数据下String结构与存储的缺点

文章目录一.背景二.String的底层数据结构1.String怎么进行数据保存的:RedisObjectint编码embstr编码raw编码jemalloc分配2.String缺点三.解决String占用大量内存的方法1.压缩列表 ziplist2.需要解决的问题3.二级编码4.注意关于压缩列表升级为哈希表问题 一.背景 当我们要存储“单值”数据时(例如0123456789:0987654321,可以看作一串id对应着另一串id)这样的数据,我们第一个想到的就是是使用String类型来进行存储。这样我们就

qq_56769991的博客 1797

关于redis 取出数据转换失败的问题(invalid stream header: 7B226163)

redis 序列化问题

BaoYongMeng的博客 5097
上一篇: 【SpringBoot】多种环境自由切换,只需要配置文件这么写
下一篇: 三分钟掌握共享内存 & Actor并发模型
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值