avatar
文章
342
标签
52
分类
23

Home
Archives
Tags
Categories
List
  • Music
  • Movie
Link
About
Southblock'Blog
Home
Archives
Tags
Categories
List
  • Music
  • Movie
Link
About

Southblock'Blog

无题
发表于2026-09-15
在上一节中,我们详细分析了 IO 线程的一些内容以及 readQueryFromClient() 函数的逻辑,这些都是我们理解 Redis 多线程模式下读取客户端请求核心所在。 在 readQueryFromClient() 函数中读取到 client->querybuf 缓冲区的都是一个个的字节,Redis Server 接下来要做的就是,把这个 byte 数组中的内容,按照一定的规则,解析成 Redis Server 能够理解的命令。这部分逻辑就是在 readQueryFromClient() 函数最后调用的 processInputBuffer() 函数中完成的。 RESP 协议基础知识不过,在开始 processInputBuffer() 函数的介绍之前,我们需要先说一些 Redis 命令解析的基础知识。 第一个基础知识点是 Redis 客户端的请求类型,对应的是 client->reqtype 字段,它有两个可选值 PROTO_REQ_INLINE、PROTO_REQ_MULTIBULK。其中,INLINE 是内联请求类型,一般是 Telnet 这种客户端发出来的 ...
无题
发表于2026-09-15
通过前面几节的介绍我们知道,Redis 中的事件驱动中,除了网络事件之外,还有时间事件,但是在前文的介绍中,我们完全没有提及到这部分内容。因此,在这一节中,我们就来补齐 Redis 时间事件的相关内容。 不过,在这之前,我们先一起来回顾一下 Redis 是如何处理时间事件的。你可以把 Redis 中的时间事件,理解成定时任务,正如第 28 讲《内核解析篇:Redis 事件驱动核心框架解析》所说,这些时间事件与维护在 aeEventLoop->timeEventHead 链表中的 aeTimeEvent 实例一一对应。 在第 28 讲《内核解析篇:Redis 事件驱动核心框架解析》介绍 aeProcessEvents() 函数的时候我们看到,在它最后,会调用 processTimeEvents() 函数去处理时间事件,其核心逻辑就是遍历 aeEventLoop->timeEventHead 链表。在遍历过程中,先会检查每个 aeTimeEvent 元素的 id 值是否为 AE_DELETED_EVENT_ID(-1),以及 refcount 是否为 0。如果满足这两个条件,表 ...
无题
发表于2026-09-15
在使用 Redis 做缓存之类的非持久化存储时,我们一般会给 Key 设置一个过期时间,在 Key 到期之后,Redis 就会把这个 Key 自动删除掉,我们之后就再也拿不到这个 KV 数据了。 那 Redis 是如何将过期 Key 清理掉的呢?常见的过期 Key 清理方式(也被称为“过期策略”)有三种:定时过期、惰性过期以及定期过期,我们简单介绍一下三者的核心区别以及 Redis 采用的策略。 定时过期策略:该策略需要为每个 Key 关联一个定时器(或是一个全局定时器)记录 Key 的过期时间,当 Key 到期时由定时器触发过期事件,触发执行 Key 的清理逻辑。定时过期策略可以立刻清理过期 Key,释放内存,但是需要额外维护定时器这种复杂的结构。 惰性过期策略:该策略是在客户端访问一个 Key 的时候,判断目标 Key 是否已经到期,如果到期了,就会将其删除,并且返回给客户端 Key 不存在。除此之外的其他时间不会主动去清理 Key。惰性过期策略实现比较简单,不会占用单独的 CPU 时间去执行 Key 过期的操作,而是平摊到了每次 Key 的访问中,但是,如果客户端长时间不访问 ...
无题
发表于2026-09-15
在上一节中,我们详细介绍了 Redis 在 IO 多线程模式下,命令解析和命令执行的核心逻辑。小伙伴们可能会产生这样一个疑问:我们调用的 redisCommand->proc() 函数的时候,是没有返回值的,那命令执行产生的返回值是怎么返回给客户端的呢? 下面我们就来详细分析下这个问题。 数据返回通过前面的介绍我们知道,Redis 在 IO 多线程模型下,命令产生的返回值是通过 IO 线程写回给客户端的,那既然 redisCommand->proc() 函数没有返回值,我们就会猜测 proc() 函数里面会把返回值写入到某个指定的地方,然后 IO 线程会去这个地方取该结果值,然后返回给客户端。 这里我们以 GET 命令为例进行分析,GET 命令对应的 proc 处理函数是 getGenericCommand() 函数,其核心逻辑如下: 123456789int getGenericCommand(client *c) { robj *o; // 从Redis DB里面中查找value值 if ((o = lookupKeyReadOrReply( ...
无题
发表于2026-09-15
在上一模块中,我们重点介绍了一个单机 Redis 是如何运行的,着重分析了单机 Redis 的线程模型、事件模型、整个请求-响应的处理流程等内容。但是,没有涉及到请求中命令的具体情况。这一模块我们就来重点介绍一下 Redis 是如何在底层数据结构之上,实现我们常用的命令的。 这一模块中,我们根据 Redis 命令底层实现的相关性,分成了下面几篇进行介绍: 通用命令在 Redis 命令分类里面, Generic 分类中的命令并不与任意一种数据结构对应,而是可以操作所有类型的结构。下图对 Generice 分类中的命令,做了进一步的分类: 这里我们就展开介绍实践中比较常用的 Generic 分类中的命令。 查看 Key 信息OBJECT 命令是 Redis 中用来查看一个 Key 元信息的命令,它有几个子命令。 最常用的就是 OBJECT ENCODING 命令,它会返回指定 Key 的编码方式,获取的是对应 redisObject 对象的 encoding 字段值。 OBJECT REFCOUNT 命令会返回指定 Key 被引用的次数,其实就是对应 redisObject 对象的 ...
无题
发表于2026-09-15
Redis 最常见的应用场景就是缓存,我们在使用缓存的时候,一般不会存储 DB 里面全量的数据,而只用于缓存一部分 DB 热点数据,对于非热点数据,需要进行定期删除,防止 Redis 内存被撑爆,也就是我们常说“内存淘汰”机制。 在前面第 33 讲《内核解析篇:Redis 时间事件的二三事》介绍 serverCron() 函数的时候提到,其中会更新 LRU 时钟,使用 LRU 时钟的地方有两个: 一个是在客户端访问一个 Key 时,会使用 Value 值中的 lru 字段记录当前的 LRU 时钟; 另一个是在 estimateObjectIdleTime() 函数中,会通过前面记录的 LRU 值,推算该 Key 空闲了多久,Redis 会按照一定的淘汰算法将最久没被访问的 Key 删除掉,防止 Redis 内存超过 maxmemory 指定上限。 内存淘汰策略这里我们先来看 redisServer 中的 maxmemory 字段(对应 redis.conf 中的 maxmemory 配置项),它指定了 Redis 的最大内存,单位是 byte,当 Redis 内存占用达到这个值的时 ...
无题
发表于2026-09-15
在上一节中,我们详细介绍了 dict 和 dictType 这两个与 Redis 哈希表紧密相关的结构体实现。 在这一节中,我们将紧接上一节的内容,展开介绍 Redis 操作 dict 的核心方法,主要包括创建 dict、向 dict 中添加数据、从 dict 中查找数据、修改 dict 中的数据以及删除数据,其中还会展开介绍 dict 的扩容和渐进式 rehash 的内容。 创建 dict要使用 dict,第一步,就是创建一个 dict 实例,来看 dictCreate() 函数,它里面会调一下 malloc 函数,申请一个 dict 实例的空间,创建 dict 的事情就完成了。在 _dictInit() 这个函数中,会初始化 dict 实例里面各个字段的值,例如,dict->ht_table 中的两个 dictEntry 指针都会被初始化为 NULL,感兴趣的小伙伴可以展开看看这些字段的初始值到底是什么。 123456dict *dictCreate(dictType *type){ dict *d = zmalloc(sizeof(*d)); // 申请空间 ...
无题
发表于2026-09-15
在上一节中,我们详细分析了哈希表基础操作的实现,主要涉及如何增删改查哈希表中的数据以及渐进式 rehash 的核心原理。 在这一节,我们来分析一下 dict 的迭代器实现,HSCAN 命令底层就直接依赖 dict 迭代器进行实现。先来考虑一下 dict 迭代器出现的原因,是不是和 quicklist 的情况类似呢?dict 里面有两个 ht_table,而且每个 ht_table 是“数组 + 链表”的结构,所以加个迭代器,屏蔽一下这些底层复杂结构,也是非常合理的选择。 dict 迭代器如果要我们自己实现一个迭代器的话,需要维护什么信息呢? 我们至少需要知道四个值:自己迭代的是哪个 dict 对象,迭代哪个 ht_table,迭代哪个槽位,迭代到了哪个节点。来看一下 dictIterator 结构体,里面的 d、table、index、entry 就是我们需要的四个值。 1234567891011typedef struct dictIterator { dict *d; // 当前迭代的dict实例 long index; // 当前迭代到的槽位 // ...
无题
发表于2026-09-15
在 Java 里面,HashSet 底层是用 HashMap 实现的。在 Redis 里面也是类似的,Redis 里面的 Hash 底层结构是 dict,Set 底层的结构也是 dict。但是,在元素都是整数值的时候,Set 可以用一种更省空间的方式来存数据,这种省空间的方式就是这一节要说的 intset。 一个 Set 要用 intset 作为底层存储的话,需要满足两个条件: 一个就是前面说的,元素都是整数类型; 另一个条件是这个 Set 里面的元素个数,要少于 set-max-intset-entries 配置指定的这个值,这个值默认是 512。 一旦这个 Set 集合不满足这两个条件,就会切换成 dict 作为底层存储。Redis 之所以使用 intset 结构来进行优化,主要是为了减少内存碎片,提高查询效率,这也体现了 Redis 在空间占用和耗时等方面的折中和思考。 intset 结构体从名字就可以看出,intset 结构体是用来存储整数类型的集合,不仅如此,intset 中存储的整数还是有序的,这样我们就可以非常方便地使用二分查找来查找一个元素。下面来看 intse ...
无题
发表于2026-09-15
在前面详细分析了 Redis 中的核心结构体以及底层数据结构,这些结构体类似于 Java 中的 domain 类,我们要想让这些结构体发挥作用,还缺少两块拼图:一个 Redis 如何使用这些 domain 类,就类似 Java 中的 Service 层;另一个是 Redis 的线程模型。 这一节,我们主要介绍一下 Redis 的线程模型。 Redis 线程模型演进史我们常说的“Redis 是一个单线程应用”指的是 Redis 在处理客户端的请求时,都是由唯一的主线程进行处理的,其中包括了请求的读取和解析、命令的执行以及响应的返回。这个描述在 Redis 4.0 版本之前,是比较准确的。 从 4.0 版本开始,Redis 就已经不是纯粹的单线程应用了。除了主线程外,Redis 开始使用后台线程处理一些比较耗时的操作,例如,清理脏数据、释放超时连接、删除大 key 等,但是网络读写、执行命令还是只使用单线程来处理。Redis 4.0 以及之前版本的核心线程模型如下图所示: Redis 之所以使用单线程是因为 Redis 执行的是纯内存的操作,Redis 服务的瓶颈不在 CPU,而是在网络 ...
1…345…35
avatar
Southblock
个人博客Blog
文章
342
标签
52
分类
23
Follow Me
公告
欢迎来到Southblock' Blog
最新文章
无题2026-09-15
无题2026-09-15
无题2026-09-15
无题2026-09-15
无题2026-09-15
分类
  • AI Agent147
    • Agent23
    • AgentScope14
    • Function Call5
    • Harness & Loop5
    • LangChain4j6
    • MCP16
    • Skills13
标签
Bat脚本 跨域 Claude Code Harness Engineering 微服务 Mem0 LLM JWT 长期记忆 提示词工程 课程介绍 AgentScope 前端 LangGraph MCP 工具调用 CSS3 MySQL 是怎样运行的:从根儿上理解 MySQL Prompt 环境搭建 LangChain4j 大模型 基础概念 Loop Engineering 智能体 工作流 Github Action 快速开始 架构设计 协议 Memory 后端开发 Obsidian Spring AI AI Coding Blog 项目结构 Workflow Dify Skills
归档
  • 九月 2026275
  • 八月 20264
  • 二月 202625
  • 六月 20255
  • 五月 20253
  • 八月 20242
  • 七月 20241
  • 六月 20242
网站资讯
文章数目 :
342
本站访客数 :
本站总访问量 :
最后更新时间 :
©2020 - 2026 By Southblock
框架 Hexo|主题 Butterfly