当前位置:首页 > 问答 > 正文

Redis缓存到底是怎么存数据的?那些你可能没注意到的存储秘密揭秘

Redis缓存到底是怎么存数据的?那些你可能没注意到的存储秘密揭秘

我们平时用Redis,都知道它快,像一个大号的内存Map,set、get命令用起来很简单,但你有没有想过,当你执行一个set name "张三"后,Redis在内部到底做了什么?这个“张三”是怎么被存起来,又是怎么被飞快找到的?今天我们就来揭开这些平时被封装好的秘密。

第一层秘密:不仅仅是简单的键值对,而是“对象”

你以为Redis存的就是简单的字符串?不是的,根据Redis官方文档和多位资深开发者的解读,Redis为了效率和功能,将所有数据都封装成了“对象”,当你存入一个字符串“张三”时,Redis会创建一个叫做“RedisObject”的东西。

这个对象就像是一个包装盒,里面有几个非常重要的信息:

  • type(类型): 标明盒子里装的是什么类型的值,比如是字符串(string)、列表(list)、哈希(hash)等,这解释了为什么Redis能区分不同的数据类型。
  • encoding(编码): 这是最核心的秘密之一!它标明了这个值在底层真正是用哪种数据结构存储的,同样是字符串,可能用一种叫“embstr”的简单方式存,也可能用“raw”方式存,一个列表,可能用链表(linkedlist),也可能用压缩列表(ziplist)来存。
  • ptr(指针): 这是一个指向实际存储数据的内存地址的指针,它指向了存有“张三”这两个字的内存位置。

这种设计的好处非常巧妙:它让Redis变得极其灵活,比如当一个哈希表中的元素很少时,Redis会用更节省内存的“压缩列表”来存;当元素变多时,它会自动转换成效率更高的“哈希表”结构,而这一切对使用者是完全透明的,你用的还是同一个hset和hget命令,这种动态切换编码的能力,是Redis在速度和空间之间取得完美平衡的关键。

Redis缓存到底是怎么存数据的?那些你可能没注意到的存储秘密揭秘

第二层秘密:键和值是如何被管理起来的?——数据库字典

现在我们知道每个值都是一个“对象”了,Redis是如何管理成千上万个键和这些“对象”的对应关系的呢?答案是:一个巨大的“字典”(或者说哈希表)。

你可以把这个字典想象成一本书的目录,这本书就是Redis的整个内存数据空间。

  • 键(Key): name”,它就是目录里的一个词条。
  • 值(Value): 也就是我们上面说的那个“RedisObject”,它是词条对应的页码和具体内容。

当你查询get name时,Redis就相当于在翻这本字典的目录,它会对键“name”计算一个哈希值,然后直接定位到这本书的大概位置,再稍微查找一下就能找到对应的“页码”(也就是指向“RedisObject”的指针),整个过程非常快,几乎是瞬间完成,这个全局的字典,就是Redis存储所有数据的入口和核心索引。

Redis缓存到底是怎么存数据的?那些你可能没注意到的存储秘密揭秘

第三层秘密:内存是如何重复利用的?——过期策略与内存淘汰

Redis是内存数据库,内存是有限的,如果只存不删,内存很快就会爆满,所以Redis有两个重要的守护机制:过期策略和内存淘汰机制。

过期策略负责清理那些设置了过期时间的键(比如短信验证码,5分钟后失效),它有两种方式协同工作:

  1. 惰性删除: 当你去访问一个键时,Redis才会检查它是否过期了,如果过期,就删除并返回空,这是一种“懒”办法,节省了CPU,但可能导致很多过期键长期占用内存,如果一直不被访问的话。
  2. 定期删除: Redis会每隔一段时间(默认100毫秒)随机抽取一部分设置了过期时间的键进行检查,并删除其中已过期的,通过这种方式,它主动地、分批地清理垃圾,弥补了惰性删除的不足。

内存淘汰机制则是当Redis内存使用达到上限时的最后手段,这时,如果有新数据要写入,就必须踢掉一些老数据,根据你设置的淘汰策略(maxmemory-policy),Redis会做出不同决定,

Redis缓存到底是怎么存数据的?那些你可能没注意到的存储秘密揭秘

  • allkeys-lru:淘汰“最近最少使用”的键,不管它有没有设置过期时间,这是最常用的策略。
  • volatile-ttl:从设置了过期时间的键中,淘汰剩余寿命最短的那个。
  • noeviction:直接报错,不写入新数据,这是默认策略。

这些机制保证了Redis在有限的内存下能够持续、稳定地提供服务。

第四层秘密:数据真的只在内存里吗?——持久化时的存储变化

我们常说Redis是内存数据库,但为了防止重启后数据丢失,它也需要把数据存到硬盘上,这个过程叫持久化,持久化发生时,数据在硬盘上的存储格式和在内存中是完全不同的。

  • RDB(快照): 类似于给当前的内存数据拍一张全景照片,然后整个保存成一个压缩的二进制文件(dump.rdb),这个文件里的数据是某个时间点的完整备份,存储结构非常紧凑,利于备份和恢复。
  • AOF(追加日志): 它不存数据本身,而是像写日记一样,把每一次修改数据的命令(比如set name 张三)按顺序追加到一个日志文件的末尾,当需要恢复时,Redis会重新按顺序执行一遍这些命令,从而重建出内存数据。

值得注意的是,在AOF重写(为了压缩AOF文件大小)时,Redis会fork一个子进程,基于当前的内存数据快照来重建一个更简洁的AOF文件,这个过程又用到了类似RDB的原理。

总结一下

Redis存储数据的秘密远不止“放在内存里”那么简单,它是一个多层级的精密系统:

  1. RedisObject对象封装数据,并通过灵活的编码动态选择最合适的底层数据结构。
  2. 用一个全局的字典(哈希表) 来高效管理所有键值对的映射关系。
  3. 通过过期策略和内存淘汰机制像清洁工一样,智能地管理和回收宝贵的内存空间。
  4. 在需要持久化时,将内存中的数据转换成RDB快照AOF日志的格式写入硬盘。

正是这些内部机制的协同工作,才造就了Redis高性能、高可用的特性,下次当你轻松地用着get和set命令时,或许就能想到背后这个复杂而优雅的世界了。