当前位置: 首页 > article >正文

Redis设计与实现——单机Redis实现

RedisDB

RedisDB的核心结构
  • 键空间(dict*dict)

    • 结构:哈希表(字典),键为字符串对象(SDS),值为 Redis 对象(字符串、列表、哈希等)。

    • 功能:存储所有持久化数据,支持增删改查操作。

  • 过期字典(dict*expires)

    • 结构:哈希表,键与键空间共享(指针引用),值为 long long 类型的时间戳(毫秒精度)。

    • 功能:记录键的过期时间,过期自动删除。

  • 阻塞键(blocking_keys):用于 BLPOP 等阻塞操作。

  • 解锁键(ready_keys):解除阻塞的键(与阻塞键配合使用)。

  • 监控键(watched_keys):被 WATCH 监控的键(用于事务)。

键的过期与删除策略
  • 惰性删除(被动删除)

    • 触发时机:访问键时检查是否过期(如 GETHGET 等命令)。

    • 流程:执行命令前检查键是否存在于 expires;若存在且当前时间 ≥ 过期时间,删除键及其值;返回空值或错误。

    • 优点:对 CPU 友好,仅在实际访问时处理。

    • 缺点:若键长期不被访问,可能内存泄漏。

  • 定期删除(主动删除)

    • 触发时机:Redis 周期性执行 serverCron 任务(默认每秒 10 次)。

    • 流程:每次从 expires 中随机抽取 20 个键;若发现过期键,立即删除;当本轮扫描中过期键比例 ≤ 25% 时停止。

    • 优点:减少内存泄漏风险。

    • 缺点:需平衡扫描频率与 CPU 开销。

  • 内存淘汰策略

    • volatile-ttl:优先淘汰即将过期的键。

    • allkeys-lru:淘汰最近最少使用的键。

    • noeviction:不淘汰,返回错误(默认策略)。

核心操作流程
  • 键的增删改查

    • 写入操作(如 SET:将键值对插入键空间(dict);若设置过期时间,同步更新 expires

    • 读取操作(如 GET:从 dict 查找键,若不存在返回 nil;若存在,检查 expires 是否过期,过期则删除并返回 nil

    • 删除操作(如 DEL:从 dictexpires 中移除键;释放键对象和值对象的内存(引用计数减 1)。

  • 数据库切换(SELECT 命令):客户端通过 redisClient.db 指针指向当前数据库(redisDb 数组的索引)。

  • 键的阻塞与事务

    • 阻塞键(blocking_keys:记录因 BLPOP 等命令被阻塞的客户端。

    • 事务监控(watched_keys:执行 WATCH 时记录键,事务提交前检查键是否被修改,实现 CAS(Check-and-Set)。

持久化关联
  • RDB 快照:保存时遍历 dict 中的所有键值对,写入二进制文件。
  • AOF 日志:记录写命令,重写时根据 dict 当前状态生成最小命令集。
  • 过期键处理
    • RDB:持久化时忽略过期键。
    • AOF:键过期后追加 DEL 命令。
性能优化
  • 控制数据库数量:非必要不使用多个数据库,避免切换开销。
  • 合理设置过期时间:避免大量键同时过期(导致 CPU 突增)。
  • 监控内存使用:通过 INFO memory 跟踪 used_memoryevicted_keys

RDB持久化

RDB 持久化的触发机制
  • 手动触发

    • SAVE 命令:阻塞 Redis 主进程直至 RDB 文件创建完成,期间拒绝所有客户端请求(仅调试或内存极小的情况)。

    • BGSAVE 命令:主进程 fork 子进程异步生成 RDB 文件,主进程继续处理请求,非阻塞,生产环境首选。

  • 自动触发:通过 save 配置项设置触发条件,满足任一条件即触发 BGSAVE

  • 主从复制:主节点首次与从节点同步时触发 RDB 生成。

  • SHUTDOWN 命令:正常关闭服务器时自动执行 SAVE(若未开启 AOF)。

RDB 文件生成流程
  • 子进程创建

    • 写时复制(Copy-on-Write)BGSAVE 通过 fork() 创建子进程,子进程共享主进程内存页。
    • 当主进程修改数据时,内核复制被修改的页,确保子进程的数据视图冻结在 fork 瞬间。
  • 数据序列化:子进程遍历当前数据库的所有键值对,按 RDB 格式序列化数据。

    • 键空间遍历:按字典顺序扫描数据库的 dict

    • 数据编码:根据数据类型选择最优编码(如字符串直接存储,列表转为压缩列表等)。

  • 文件写入

    • 临时文件:数据先写入临时文件(默认 temp-<pid>.rdb),完成后原子替换旧 RDB 文件。

    • 刷盘策略:依赖操作系统缓冲区,可通过 rdb-save-incremental-fsync 配置增量刷盘。

  • 错误处理

    • 子进程崩溃:主进程记录日志,RDB 文件保留旧版本。

    • 磁盘满:写入失败,删除临时文件,保留旧 RDB。

RDB 文件结构
  • 魔数:5字节,固定为 REDIS,标识文件类型。
  • RDB 版本号:4字节,如 0009 表示 Redis 6.x 的 RDB 版本。
  • 数据库数据:按数据库编号顺序存储各库的键值对。
  • 键值对编码:类型标识(1字节)+ 键长度 + 键内容 + 值编码(如字符串长度 + 内容)。
  • 过期时间:可选字段,记录键的毫秒级过期时间戳。
  • 结束标识:1字节 0xFF
  • CRC64 校验和:8字节,用于验证文件完整性。
RDB 文件加载流程
  • 文件校验:检查魔数和 CRC64 校验和。
  • 按序解析:逐个读取数据库数据,重建键空间和过期字典。
  • 阻塞加载:加载期间拒绝所有客户端请求,直至完成。
RDB 与 AOF 的对比
特性RDBAOF
持久化粒度时间点快照记录每次写操作(日志追加)
数据安全性可能丢失数分钟数据通常最多丢失1秒数据(默认配置)
恢复速度慢(需重放日志)
文件体积大(需定期重写优化)
适用场景备份、快速重启、容忍数据丢失高数据安全性要求

AOF持久化

AOF 持久化的工作机制
  • 命令追加(Append)

    • 客户端执行写命令(如 SETHSET)后,Redis 将该命令转换为 Redis 协议格式(RESP)。
    • 命令追加到 aof_buf 缓冲区(内存)。
    • 根据配置的同步策略(appendfsync),将 aof_buf 写入并同步到 AOF 文件。
  • 文件同步策略(appendfsync

    策略行为数据安全性性能
    always每个写命令都同步到磁盘(调用 fsync最高(零丢失)最差(频繁 IO)
    everysec每秒批量同步一次(后台线程执行)最多丢失1秒数据平衡(默认配置)
    no由操作系统决定同步时机(通常约30秒)最低最佳
AOF 文件重写(Rewrite)
  • 触发条件

    • 手动触发:执行 BGREWRITEAOF 命令。

    • 自动触发:根据配置的阈值自动触发。

  • 重写流程

    • 创建子进程:主进程 fork 子进程执行重写(利用写时复制保证数据一致性)。

    • 遍历数据库:子进程遍历所有数据库的键空间,生成重建数据的最小命令集。

    • 优化策略:合并多个命令(如用 HMSET 替代多次 HSET);忽略过期键和已删除键的历史操作。

    • 写入临时文件:新 AOF 命令写入临时文件(避免影响原文件)。

    • 替换旧文件:重写完成后,用临时文件原子替换旧 AOF 文件。

  • 重写期间的写入处理

    • 双缓冲区机制:主进程将重写期间的写命令同时写入 aof_buf(原AOF文件)和 aof_rewrite_buf(重写缓冲区)。

    • 同步阶段:重写完成后,主进程将 aof_rewrite_buf 追加到新 AOF 文件,确保数据一致性。

AOF 文件加载与数据恢复
  • 创建伪客户端:模拟客户端执行 AOF 文件中的命令。
  • 逐条执行命令:按顺序重放所有写操作,重建内存数据库。
  • 错误处理:若 AOF 文件损坏(如末尾不完整),可用 redis-check-aof --fix 工具修复;Redis 4.0+ 支持加载截断的 AOF 文件。
混合持久化(RDB + AOF)
  • 文件结构:重写后的 AOF 文件前半段为 RDB 格式快照,后半段为增量 AOF 命令。
  • 优点:快速加载(RDB 部分)+ 低数据丢失风险(AOF 部分);文件体积小于纯 AOF。
  • 启用方式:设置 aof-use-rdb-preamble yes
生产环境建议
  • 启用 AOF + 混合持久化:兼顾安全性与恢复速度。
  • 监控 AOF 状态:通过 INFO persistence 查看 aof_current_sizeaof_rewrite_in_progress
  • 合理配置同步策略:对数据安全性要求极高:appendfsync always;平衡场景:appendfsync everysec
  • 定期备份 AOF 文件:与 RDB 文件一同存储至异地。

事件处理

  • Redis 采用 单线程事件驱动模型,通过高效的事件循环处理网络 I/O 和定时任务,实现高并发与低延迟。
文件事件(File Events)
  • 功能:处理客户端请求、命令读取与响应等网络 I/O 操作。
  • 实现:基于 I/O 多路复用(如 epollkqueue)监听多个套接字,实现非阻塞通信。
    • 连接处理:客户端发起连接时触发 accept 事件,创建连接对象。
    • 命令处理:客户端发送命令后触发 read 事件,解析并执行命令,结果写入输出缓冲区,触发 write 事件返回响应。
  • 特点:单线程按序处理,避免竞态条件,依赖高效的数据结构与协议解析。
时间事件(Time Events)
  • 功能:执行定时或周期性任务(如清理过期键、持久化、统计等)。
  • 类型
    • 周期性事件:如 serverCron(默认每秒 10 次),负责键过期检查、AOF 重写触发、集群心跳等。
    • 一次性事件:如延迟执行的任务。
  • 调度:事件按执行时间戳排序,每次事件循环选择最近的事件执行。
事件循环(Event Loop)
  • 等待事件:调用 I/O 多路复用 API(如 epoll_wait),阻塞至文件事件就绪或最近时间事件到达。
  • 处理文件事件:执行就绪的读写事件(如接受连接、读取命令、返回响应)。
  • 处理时间事件:执行所有到期的时间事件(如 serverCron)。
  • 循环重复:进入下一轮事件等待。
  • 优先级:优先处理文件事件(保证响应速度),时间事件可能被延迟但不会丢弃。
设计优势
  • 高效单线程:避免多线程上下文切换与锁竞争,内存操作快速。
  • 无阻塞:I/O 多路复用 + 非阻塞套接字,充分利用 CPU。
  • 可扩展性:通过合理拆分任务(如后台持久化使用子进程),避免主线程阻塞。

Redis客户端

客户端生命周期与核心流程
  • 连接建立

    • 客户端通过 TCP 连接服务器,触发文件事件 accept
    • 创建 client 对象,初始化 fddb(默认选库 0)、querybuf 等字段。
    • 将客户端加入服务器的全局客户端列表 clients
  • 命令处理

    • 读取请求:客户端发送的命令数据被读入 querybuf,按 Redis 协议(RESP)解析为 argvargc

    • 查找命令:根据 argv[0](命令名)在命令表 commandTable 中查找对应的 redisCommand 结构(含函数指针)。

    • 执行命令:调用 cmd->proc(client) 执行命令,结果写入 reply 缓冲区。

    • 返回响应:将 reply 中的数据通过套接字发送给客户端,清空缓冲区。

  • 连接关闭

    • 主动关闭:客户端发送 QUIT 命令或断开连接。

    • 被动关闭:服务器因超时(timeout 配置)、协议错误或内存限制强制断开。

    • 资源释放:释放 client 对象、缓冲区内存,关闭套接字。

客户端状态与特性
  • 阻塞操作

    • 场景:执行 BLPOPBRPOPSUBSCRIBE 等阻塞命令。

    • 处理:将客户端标记 CLIENT_BLOCKED,移入 blocked_clients 列表;阻塞期间暂停读取新命令,直到数据到达或超时。

  • 事务支持(MULTI模式):命令缓存在客户端的 mstate 队列,直到 EXECDISCARD;通过 WATCH 监控键,被修改时事务中止。

  • Pub/Sub 订阅

    • 订阅状态:客户端订阅频道后,只能执行订阅相关命令,其他命令被拒绝。

    • 数据推送:发布的消息直接写入客户端的 reply 缓冲区。

  • 输出缓冲区管理(限制配置):超出限制时,服务器断开连接以防止内存耗尽。

客户端的限制与优化
  • 最大连接数maxclients 10000(默认 10000),超出限制时,新连接被拒绝。

  • 超时控制timeout 300(默认 300 秒无交互后断开连接)。

  • 客户端列表查询CLIENT LIST 查看所有客户端信息(IP、状态、内存使用等)。

Redis服务器

服务器启动流程
  • 初始化配置:加载 redis.conf 配置(端口、持久化策略、内存限制等);创建默认的 数据库,初始化键空间字典与过期字典。
  • 启动事件循环:创建 事件循环器aeEventLoop),绑定文件事件(网络 I/O)和时间事件;监听客户端连接请求。
  • 加载持久化数据:若存在 dump.rdbappendonly.aof,恢复数据到内存。
  • 运行就绪:进入事件循环(aeMain),开始处理客户端请求与后台任务。
请求处理流程
  • 接收连接:客户端发起连接,触发文件事件,调用 accept 创建 client 对象,加入全局客户端列表。
  • 读取命令:将客户端发送的数据存入 querybuf,按 RESP 协议解析为命令参数(argvargc)。
  • 执行命令:查找命令表获取对应的处理函数;执行函数,操作数据库(如更新键空间),结果写入 reply 输出缓冲区。
  • 返回响应:将 reply 中的数据通过套接字返回客户端,清空缓冲区。
多数据库管理
  • 数据库切换:通过 SELECT 命令切换当前数据库(修改 client.db 指针)。
  • 键空间操作:所有数据操作基于 redisDb.dict(键空间字典)和 redisDb.expires(过期字典)。
  • 键过期策略:结合惰性删除(访问时检查)和定期删除(serverCron 任务扫描)。
后台任务与扩展功能
  • 持久化RDB—通过 BGSAVE fork 子进程生成快照;AOF—追加写命令,定期重写优化文件。
  • 过期键清理serverCron 每秒执行 10 次,随机抽查并删除过期键。
  • 内存管理:根据 maxmemory 策略(LRU、LFU 等)淘汰键,防止内存溢出。
  • 慢查询日志:记录执行超时的命令(slowlog-log-slower-than 配置)。
单线程模型的核心优势
  • 无锁设计:避免多线程竞争,简化实现,内存操作原子化。
  • 高效 I/O:通过 I/O 多路复用(如 epoll)处理高并发连接。
  • CPU 亲和性:单线程绑定 CPU 缓存,减少上下文切换开销。

相关文章:

Redis设计与实现——单机Redis实现

RedisDB RedisDB的核心结构 键空间&#xff08;dict*dict&#xff09; 结构&#xff1a;哈希表&#xff08;字典&#xff09;&#xff0c;键为字符串对象&#xff08;SDS&#xff09;&#xff0c;值为 Redis 对象&#xff08;字符串、列表、哈希等&#xff09;。 功能&#x…...

neo4j官方示例

目录 一、准备数据 1.执行查看结果 二、操作 1.find 单个节点 2.同上&#xff0c;已某个属性去查询 3. 指定查询个数 4.条件查询 5.查询某个人出演的电影汇总 6.查询tom出演的电影中&#xff0c;还有其他演员的信息。 7.查询跟电影(Cloud Atlas)有关的演员&#xff0…...

探讨关于智能体(Agent)结合 Dify、大语言模型(LLM)以及 Qwen-3 模型的项目或概念

1. Dify 的作用 Dify 是一个开源的 AI 框架&#xff0c;它可以帮助开发者快速搭建和部署 AI 应用。它可以作为一个基础架构&#xff0c;为智能体提供以下支持&#xff1a; 应用开发与部署&#xff1a;Dify 可以帮助开发者快速搭建智能体的前端和后端架构&#xff0c;包括用户界…...

前端自学入门:HTML 基础详解与学习路线指引

在互联网的浪潮中&#xff0c;前端开发如同构建数字世界的基石&#xff0c;而 HTML 则是前端开发的 “入场券”。对于许多渴望踏入前端领域的初学者而言&#xff0c;HTML 入门是首要挑战。本指南将以清晰易懂的方式&#xff0c;带大家深入了解 HTML 基础&#xff0c;并梳理前端…...

C++.Windows图形

Windows图形 1. 基础知识1.1 Windows图形编程基础1.2 GDI与GDI+1.3 窗口消息处理2.1 注册窗口类2.2 创建窗口2.3 显示窗口3.1 创建按钮3.2 按钮消息处理4.1 设置窗口透明度4.2 透明窗口示例5.1 使用区域创建异形窗口5.2 异形窗口示例6.1 GDI+抗锯齿设置6.2 抗锯齿绘图示例7.1 D…...

vue实现与后台springboot传递数据【传值/取值 Axios 】

vue实现与后台springboot传递数据【传值/取值】 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是node.js和vue的使用。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系列文章】&#xff1a;每…...

【英语笔记(三)】介绍谓语动词的分类,初步讲解四种基本状态:一般、进行、完成、完成进行

1. 五大类谓语动词 2. 谓语动词分类 3. 动词时间 过去--------------------------现在-----------------------未来 3. 动词状态 3.1 进行状态 3.2 完成状态 3.3 完成进行状态 3.4 一般状态 4. 时间 状态 名称说明例句现在现在现在现在进行时态现在某物正在做什么事情一只…...

【Python】让Selenium 像Beautifulsoup一样,用解析HTML 结构的方式提取元素!

我在使用selenium的find_element的方式去获取网页元素&#xff0c;一般通过xpath、css_selector、class_name的方式去获取元素的绝对位置。 但是有时候如果网页多了一些弹窗或者啥之类的&#xff0c;绝对位置会发生变化&#xff0c;使用xpath等方法&#xff0c;需要经常变动。…...

2025 后端自学UNIAPP【项目实战:旅游项目】3、API接口请求封装,封装后的简单测试以及实际使用

一、创建请求封装目录 选中自己的项目&#xff0c;右键鼠标---->新建---->目录---->名字自定义【我的是api】 二、创建两个js封装文件 选中封装的目录&#xff0c;右键鼠标---->新建---->js文件---->名字自定义【我的两个js文件分别是my_http和my_api】 三…...

Mysql--基础知识点--91.2--processlist

在 MySQL 中&#xff0c;SHOW PROCESSLIST 是一个常用命令&#xff0c;用于查看当前数据库服务器上所有正在运行的线程&#xff08;进程&#xff09;信息。以下是关键点说明&#xff1a; 1. 命令用法 SHOW FULL PROCESSLIST;输出字段&#xff1a; 列名含义Id线程唯一标识符&am…...

C#中程序集的详解一

程序集&#xff08;Assembly&#xff09;是 .NET 平台中的一个重要概念&#xff0c;它是代码和资源的逻辑单元&#xff0c;也是应用程序的部署、版本控制和安全权限的最小单位。下面详细介绍其定义和常见用法&#xff1a; 1. 程序集的定义 程序集是 .NET 应用程序的构建块&am…...

解决LangChain4j报错HTTP/1.1 header parser received no bytes

问题描述 当使用langchain4j-open-ai调用自己部署的大模型服务时报错&#xff1a; public static void main(String[] args) {OpenAiChatModel model OpenAiChatModel.builder().apiKey("none").modelName("qwen2.5-instruct").baseUrl("http://19…...

Ascend的aclgraph(二)_npu_backend中还有些什么秘密?

1 _npu_backend 文章还是从代码开始 import torch_npu, torchair config torchair.CompilerConfig() # 设置图下沉执行模式 config.mode "reduce-overhead" npu_backend torchair.get_npu_backend(compiler_configconfig) opt_model torch.compile(model, back…...

ventoy安全启动怎么选_ventoy安全启动支持是开还是关

ventoy安全启动怎么选&#xff1f;Ventoy新一代多系统启动U盘解决方案。国产开源U盘启动制作工具&#xff0c;支持Legacy BIOS和UEFI模式&#xff0c;理论上几乎支持任何ISO镜像文件&#xff0c;支持加载多个不同类型的ISO文件启动&#xff0c;无需反复地格式化U盘&#xff0c;…...

MySQL文章总结,简单整理和详细整理

这篇博客文章《MySQL 有这一篇就够&#xff08;呕心狂敲37k字&#xff0c;只为博君一点赞&#xff01;&#xff01;&#xff01;&#xff09;》是一篇非常全面的MySQL基础教程&#xff0c;适合初学者和需要复习MySQL知识的开发者。以下是文章的核心内容整理&#xff1a; 一、SQ…...

CC53.【C++ Cont】二分查找的普通模版

目录 1.知识回顾 2.关键点 特点 三个模版 普通的模版(有局限) 以LeetCode上的一道题为例:704. 二分查找 分析 引入二段性:分两段,舍一段,操作另一段(这个是二分查找的本质!) 代码 提交结果 当然也可以使用随机数来分两段 普通模版总结 1.知识回顾 之前在C语言专栏…...

泛型加持的策略模式:打造高扩展的通用策略工具类

一、传统策略模式的痛点与突破 1.1 传统策略实现回顾 // 传统支付策略接口 public interface PaymentStrategy {void pay(BigDecimal amount); }// 具体策略实现 public class AlipayStrategy implements PaymentStrategy {public void pay(BigDecimal amount) { /* 支付宝支…...

【优选算法 | 链表】链表操作技巧:常见算法

算法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;双指针滑动窗口二分查找前缀和位运算模拟 链表是一种灵活的数据结构&#xff0c;广泛用于需要频繁插入和删除的场景。掌握链表的常见操作技巧&#xff0c;如插入、删除、翻转和合并等&#xff0c;能帮助开发者更…...

HTTP:十三.HTTP日志

日志记录 日志记录了跟踪使用情况、安全性、计费、错误检验。记录事务的基本信息。通常会记录下来的几个字段示例为: HTTP方法:主要记录事务用了什么方法客户端和服务器的HTTP版本:给出客户端和服务器有关的提示,比如兼容性提示什么的所请求资源的URL:记录Web站点某个资源…...

web 自动化之 selenium 元素四大操作三大切换等待

文章目录 一、元素的四大操作二、三大切换&等待1、切换窗口:当定位的元素不在当前窗口&#xff0c;则需要切换窗口2、切换iframe&#xff1a;当定位的元素在frame/iframe&#xff0c;则需要切换3、切换弹出窗口 一、元素的四大操作 1、输入 2、点击 3、获取文本 4、获取属…...

FEKO许可证的安全与合规性

在电磁仿真领域&#xff0c;FEKO软件因其出类拔萃的性能和广泛的应用场景&#xff0c;赢得了全球用户的广泛赞誉。但在这背后&#xff0c;是什么让FEKO在众多竞争者中脱颖而出&#xff1f;答案是其许可证的安全与合规性。它们不仅为用户提供了坚固的保障&#xff0c;更确保了用…...

w~大模型~合集30

我自己的原文哦~ https://blog.51cto.com/whaosoft/13284996 #VideoMamba 视频理解因大量时空冗余和复杂时空依赖&#xff0c;同时克服两个问题难度巨大&#xff0c;CNN 和 Transformer 及 Uniformer 都难以胜任&#xff0c;Mamba 是个好思路&#xff0c;让我们看看本文是…...

PBR材质-Unity/Blender/UE

目录 前言&#xff1a; 一、Unity&#xff1a; 二、Blender&#xff1a; 三、UE&#xff1a; 四、全家福&#xff1a; 五、后记&#xff1a; 前言&#xff1a; PBR流程作为表达物理效果的经典方式&#xff0c;很值得一学。纹理贴图使用的是上一期的Textures | cgbookcas…...

websocketpp 安装及使用

介绍 WebSocket 是从 HTML5 开始支持的一种网页端和服务端保持长连接的消息推送机制。 传统的 web 程序都是属于 "一问一答" 的形式&#xff0c;即客户端给服务器发送了一个 HTTP 请求&#xff0c;服务器给客户端返回一个 HTTP 响应。这种情况下服务器是属于被动…...

web:InfiniteScroll 无限滚动

InfiniteScroll 无限滚动 分页加载 <div class"data-box" v-infinite-scroll"loadMore"> <li v-fori in dataList></li> </div>form: {current: 1,size: 10,}loadMore(){console.log(this.dataList.length, this.total ,8888)if…...

LeetCode[101]对称二叉树

思路&#xff1a; 对称二叉树是左右子树对称&#xff0c;而不是左右子树相等&#xff0c;所以假设一个树只有3个节点&#xff0c;那么判断这个数是否是对称二叉树&#xff0c;肯定是先判断左右两个树&#xff0c;然后再看根节点&#xff0c;这样递归顺序我们就确认了&#xff0…...

c/c++爬虫总结

GitHub 开源 C/C 网页爬虫探究&#xff1a;协议、实现与测试 网页爬虫&#xff0c;作为一种自动化获取网络信息的强大工具&#xff0c;在搜索引擎、数据挖掘、市场分析等领域扮演着至关重要的角色。对于希望深入理解网络工作原理和数据提取技术的 C/C 开发者&#xff0c;尤其是…...

js fetch流式请求 AI动态生成文本,实现逐字生成渲染效果

开启流式请求&#xff1a;向后端接口发起普通的 fetch&#xff0c;它会返回一个包含 ReadableStream 的 Response 对象获取流式读取器&#xff1a;调用 response.body.getReader() 获取一个 ReadableStreamDefaultReader 实例循环读取数据块&#xff1a;在 while(true) 循环或 …...

第8章-2 查询执行的基础

上一篇&#xff1a;《第8章-1 查询性能优化-优化数据访问》&#xff0c;接着来了解查询执行的过程&#xff0c;这个对sql执行有个更直观的了解。 查询执行的基础 当希望MySQL能够以更高的性能运行查询时&#xff0c;最好的办法就是弄清楚MySQL是如何优化和执行查询的。一旦理解…...

java面试OOM汇总

在正式 Minor GC 前&#xff0c;JVM 会先检查新生代中对象&#xff0c;是比老年代中剩余空间大还是小。假如 Minor GC之后 Survivor 区放不下剩余对象&#xff0c;这些对象就要进入老年代 老年代剩余空间大于新生代中的对象大小&#xff0c;那就直接 Minor GC&#xff0c; GC 完…...