C/C++ 中两种结构体 typedef 定义的差异与实践
导言在 C 和 C++ 编程中,结构体(struct)是组织复杂数据的核心工具,而 typedef 则常用于简化类型名、提升代码可读性。实际开发中,我们常会见到两种结构体 + typedef 的定义方式:typedef struct Person{} Person; 与 typedef struct {} Person;。这两种写法看似相似,却因语言特性(C/C++ 差异)和结构体标签(tag)的存在与否,在使用场景、功能限制上有显著区别。 一、基础认知:结构体标签与 typedef 的作用在深入差异前,需先明确两个核心概念: 结构体标签(tag):紧跟 struct 后的标识符(如 struct Person 中的 Person),是结构体的 “原生名称”,仅在 struct 关键字后生效。 typedef 别名:通过 typedef 为类型(包括结构体)定义的简化名称(如 Person),可直接作为类型名使用。 C 和 C++ 对 “结构体标签” 的处理规则不同,这是两种定义方式差异的根源 ——C 语言中,结构体必须通过 struct 标签 或...
Redis RDB 持久化
一、RDB 概念解析Redis 数据库的 RDB(Redis Database)持久化机制,本质上是一种将内存数据以二进制快照形式存储至磁盘的技术方案。该机制通过创建一个名为dump.rdb的文件(默认文件名),将 Redis 内存中的全部数据进行序列化保存。例如,当 Redis 实例包含 10,000 条数据记录时,RDB 机制会将这些数据完整保存至快照文件,在系统重启时通过加载该文件实现数据恢复。 二、核心技术原理RDB 持久化的实现过程主要涉及以下两个关键步骤: 子进程创建(fork 机制):在触发 RDB 快照生成时,Redis 利用操作系统的fork机制创建一个子进程。此时,父进程继续处理客户端的读写请求,确保业务连续性;而子进程则负责将内存数据写入 RDB 文件,这种分离设计有效避免了对业务处理的阻塞。 写时复制(Copy-On-Write,COW):在子进程执行数据写入操作期间,若父进程需要修改内存中的数据,系统会采用写时复制策略。具体而言,父进程将待修改的数据复制一份,在新副本上进行修改操作,而子进程继续读取原始数据写入 RDB 文件。通过这种方式,能够确保...
Redis AOF 持久化机制
导言在分布式计算架构中,Redis 作为内存数据库的典型代表,其数据持久化机制构成了系统容错性与数据一致性保障的核心技术支撑。在 Redis 提供的 RDB(Redis Database)快照持久化与 AOF(Append-Only File)日志追加持久化两种核心策略中,AOF 机制凭借其基于操作日志的增量式数据记录模式,在数据完整性保障方面展现出独特优势。 一、AOF 持久化机制的理论模型与实现架构AOF 持久化机制遵循 "Write-Ahead Logging"(预写日志)设计范式,通过顺序追加的方式记录数据库写操作,从而构建可回溯的历史操作序列。该机制在运行时经历命令缓存、磁盘同步、日志优化三个核心处理阶段,各阶段通过协同工作实现数据可靠性与系统性能的动态平衡。 1. 命令缓存层:基于 RESP 协议的操作记录在 Redis 执行写操作过程中,系统将 SET、HSET、LPUSH 等指令按照 RESP(Redis Serialization Protocol)协议标准进行序列化,并暂存于内存级 AOF 缓冲区。这种设计有效规避了频繁磁盘...
Redis 缓存三大故障 - 缓存穿透
导言在分布式系统中,Redis 作为高性能分布式缓存,可有效减轻数据库压力、提升接口响应速度,但高并发场景下,缓存雪崩、缓存击穿、缓存穿透三类异常易引发系统稳定性问题。 一、缓存穿透 ——“不存在数据” 的持续穿透1. 核心定义缓存穿透是指查询的数据在缓存和数据库中均不存在(如查询不存在的 ID、非法参数),导致每次请求都穿透缓存直接查询数据库,且由于缓存无法存储 “不存在的数据”,后续相同请求会持续穿透,造成数据库无效查询压力激增的现象。 核心特征:区别于前两类异常,其关键在于 “查询无结果数据”,缓存无法拦截,请求持续打向 DB。 2. 触发条件缓存穿透的发生源于两类场景: 非法请求攻击:恶意用户构造非法参数(如负数 ID、超长字符串)发起大量查询,这类数据在缓存和 DB 中均不存在; 业务空数据查询:正常业务场景中,部分查询天然无结果(如查询已删除的用户、未创建的订单),且未做缓存处理。 3. 解决方案对比 解决方案 实现逻辑 优点 缺点 适用场景 空值缓存 查询 DB 无结果时,将 “空值”(如空字符串、默认对象)存入...
Leetcode 1757. 可回收且低脂的产品
1757. 可回收且低脂的产品表:Products 12345678910+-------------+---------+| Column Name | Type |+-------------+---------+| product_id | int || low_fats | enum || recyclable | enum |+-------------+---------+product_id 是该表的主键(具有唯一值的列)。low_fats 是枚举类型,取值为以下两种 ('Y', 'N'),其中 'Y' 表示该产品是低脂产品,'N' 表示不是低脂产品。recyclable 是枚举类型,取值为以下两种 ('Y', 'N'),其中 'Y' 表示该产品可回收,而 'N' 表示不可回收。 编写解决方案找出既是低脂又是可回收的产品编号。 返回结果 无顺序要求...
Leetcode 0503. 下一个更大元素 II
503. 下一个更大元素 II给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。 示例 1: 12345输入: nums = [1,2,1]输出: [2,-1,2]解释: 第一个 1 的下一个更大的数是 2;数字 2 找不到下一个更大的数; 第二个 1 的下一个最大的数需要循环搜索,结果也是 2。 示例 2: 12输入: nums = [1,2,3,4,3]输出: [2,3,4,-1,4] 解题思路核心思路是单调栈 + 数组翻倍模拟循环,通过将数组复制一份拼接在末尾(模拟循环),利用单调栈高效找到每个元素的下一个更大元素: 模拟循环数组: 将原数组 nums 复制一份并拼接在末尾(如 [1,2,1] 变为...
Leetcode 0496. 下一个更大元素 I
496. 下一个更大元素 Inums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。 给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。 对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。 返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。 示例 1: 123456输入:nums1 = [4,1,2], nums2 = [1,3,4,2].输出:[-1,3,-1]解释:nums1 中每个值的下一个更大元素如下所述:- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。- 1 ,用加粗斜体标识,nums2 =...
Redis 缓存三大故障 - 缓存击穿
导言在分布式系统中,Redis 作为高性能分布式缓存,可有效减轻数据库压力、提升接口响应速度,但高并发场景下,缓存雪崩、缓存击穿、缓存穿透三类异常易引发系统稳定性问题。 一、缓存击穿 —— 热点数据 “过期瞬间” 的单点突破1. 核心定义缓存击穿是指某一 “热点数据”(高并发访问的单一数据)的缓存过期失效瞬间,大量并发请求同时查询该数据,由于缓存已无对应数据,所有请求直接穿透至数据库,导致数据库针对该热点数据的查询压力骤增、甚至出现查询超时的现象。 核心特征:区别于缓存雪崩,其关键在于 “单个热点数据失效”,影响范围为 “局部”(仅该热点数据相关查询)。 2. 触发条件缓存击穿的发生需同时满足两个条件: 热点数据缓存过期:某一数据访问量极高(如每秒数千次查询),且其 Redis 缓存恰好过期; 无预热机制的突发热点:突发高并发请求集中访问某一数据(如临时热门内容),而该数据未提前缓存,导致请求直接打向 DB。 3. 解决方案对比 解决方案 实现逻辑 优点 缺点 适用场景 互斥锁(分布式锁) 缓存失效时,仅允许一个请求获取锁并查询...
Redis 缓存三大故障 - 缓存雪崩
导言在分布式系统中,Redis 作为高性能分布式缓存,可有效减轻数据库压力、提升接口响应速度,但高并发场景下,缓存雪崩、缓存击穿、缓存穿透三类异常易引发系统稳定性问题。 一、缓存雪崩 —— 大量缓存 “集体失效” 的连锁反应1. 核心定义缓存雪崩是指某一时间段内,大量缓存数据同时过期失效,或缓存服务(如 Redis 集群)整体不可用,导致原本由缓存承接的高并发请求全部穿透至数据库,引发数据库压力骤增、甚至宕机,进而可能导致整个业务服务雪崩的现象。 核心特征:区别于其他异常,其关键在于 “批量失效 / 整体不可用”,影响范围为 “全局”(而非局部)。 2. 触发条件缓存雪崩的发生通常源于两类场景,满足任一即可触发: 缓存集中过期:大量缓存数据设置了相同或相近的过期时间(如统一设为 24 小时、固定时间点过期),到期后集体失效,请求失去缓存承接; 缓存服务不可用:Redis 集群因硬件故障、网络波动、内存溢出被系统终止等原因,出现整体宕机或无法访问,导致缓存层完全失效。 3....
Leetcode 0084. 柱状图中最大的矩形
84. 柱状图中最大的矩形给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 示例 1: 123输入:heights = [2,1,5,6,2,3]输出:10解释:最大的矩形为图中红色区域,面积为 10 示例 2: 12输入: heights = [2,4]输出: 4 解题思路该解法是对单调栈思路的优化实现,通过在数组末尾添加哨兵元素和栈初始化技巧,将左右边界的计算合并到一次遍历中,代码更简洁且效率更高。 核心优化思路 哨兵元素(Sentinel): 在原数组末尾添加 -1(高度为负数的哨兵),确保遍历结束时栈中所有元素都会被弹出计算(相当于 “大火收汁”); 栈初始时推入 -1(索引哨兵),解决栈空时的边界判断问题,同时自然对应 “左侧无更矮柱子” 的情况(left[i] = -1)。 一次遍历计算左右边界: 遍历每个元素作为右侧边界(right),当当前高度小于栈顶高度时,栈顶元素的左右边界均已确定: 右边界:当前索引...
Leetcode 0402. 移掉 K 位数字
402. 移掉 K 位数字给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。 示例 1 : 123输入:num = "1432219", k = 3输出:"1219"解释:移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219 。 示例 2 : 123输入:num = "10200", k = 1输出:"200"解释:移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。 示例 3 : 123输入:num = "10", k = 2输出:"0"解释:从原数字移除所有的数字,剩余为空就是 0 。 解题思路核心思路是单调栈 + 贪心算法,通过维护一个递增的栈来确保剩余数字最小,同时控制移除的数字数量不超过 k: 单调栈逻辑: 遍历字符串中的每个数字,对于当前数字c: 若栈不为空,且栈顶数字大于 c,且仍有可移除的次数(k >...
Leetcode 0042. 接雨水
42. 接雨水给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 123输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]输出:6解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 示例 2: 12输入:height = [4,2,0,3,2,5]输出:9 解题思路核心思路是双指针边界,通过每个柱子能接住的雨水量取决于其左右两侧的最高柱子中较矮的那个(短板效应): 边界定义: 对于位置 i,左侧最高柱子高度为 left_max[i]; 右侧最高柱子高度为 right_max[i]; 位置 i 能接住的雨水量为 min(left_max[i], right_max[i]) - height[i](若结果为正,否则为 0)。 优化实现: 采用双指针法,无需额外存储 left_max 和 right_max 数组,将空间复杂度降至 O (1); 用 left 和 right...
Redis 哨兵模式(Sentinel)
导言哨兵模式(Sentinel)是 Redis 主从复制架构的 “高可用增强层”,通过分布式共识机制解决了主从复制中 “主节点宕机需手动切换” 的痛点,实现故障自动检测、主节点自动选举、从节点自动同步,是中小规模 Redis 集群(数据量 < 10GB、读多写少)的生产级高可用标准方案。 一、哨兵模式的核心定位与价值1.1 在理解哨兵前,需先明确其与主从复制的关系:主从复制负责 “数据同步”(主写从读、数据备份),但无法自动处理主节点故障; 哨兵负责 “高可用保障”(监控、故障转移、配置自动更新),是主从架构的 “大脑”,二者协同实现 99.99% 服务可用性。 1.2 哨兵模式的核心价值 彻底告别手动运维:主节点宕机后,无需人工干预,10-30 秒内完成故障转移 分布式容错:多哨兵节点共识判断,避免单点误判(如网络抖动导致的假宕机) 客户端透明路由:客户端可通过哨兵获取当前主节点地址,无需硬编码 IP / 端口 配置动态同步:故障转移后,自动通知所有从节点切换新主节点,更新同步关系 二、哨兵的核心功能与工作原理2.1...
Redis 主从复制
导言作为分布式系统中实现数据高可用与读写分离的核心技术,Redis 主从复制(Master-Replica Replication)通过多节点数据同步,解决了单点故障风险与读写负载不均问题。 一、核心原理:全量复制与增量复制双机制Redis 主从复制通过 “全量初始化 + 增量衔接” 的方式实现数据同步,两种复制模式适配不同场景需求,需明确触发条件与执行流程。 1. 全量复制:从节点初始化同步触发场景 从节点首次连接主节点(冷启动场景) 从节点断开时间超过repl_backlog_buffer(复制积压缓冲区)大小 主节点执行flushall/flushdb后,从节点同步时无有效偏移量 关键细节 RDB 传输期间主节点通过复制客户端缓冲区(默认 1GB)缓存新写请求,缓冲区满会导致复制失败,需根据写入量调整client-output-buffer-limit replica 从节点加载 RDB 时会阻塞读请求,可通过replica-lazy-flush yes(默认开启)延迟清空数据,减少阻塞时间 2....
Redis 内存满故障机制
一、Redis 内存分配原理与数据存储特性要解决内存满的问题,首先需理解 Redis 如何占用内存 —— 其内存消耗并非仅用于存储键值对,而是由多模块构成,且数据类型的编码特性直接影响内存效率。 1.1 内存结构组成(按占用比例排序)Redis 的内存消耗主要分为 4 个部分,其中数据区是核心: 数据区(~80%-90%):存储键值对数据,包含键(Key)、值(Value)及数据结构元数据(如哈希表桶、链表节点、跳表索引等)。 例:一个Hash类型键,若采用ziplist编码,会存储字段 / 值的紧凑数组;若转成hashtable,则需额外存储哈希桶、链表指针等元数据,内存占用显著增加。 缓冲区(~5%-15%):包括三类关键缓冲,易被忽视但可能引发内存溢出: 客户端缓冲:每个客户端连接的输入 / 输出缓冲(默认无上限,大量闲置连接会导致缓冲堆积); 复制缓冲:主从同步时的repl-backlog-buffer(默认 1MB,若同步延迟高会扩容); AOF 缓冲:AOF...
Redis 作为 MySQL 缓存选择因素归类
引言:高并发时代下的数据库性能困境与缓存破局在现代应用架构中,高并发读取场景(如电商商品详情页、CMS 文章列表、用户会话查询)已成为系统性能的核心挑战。MySQL 作为主流关系型数据库,其 InnoDB 存储引擎基于磁盘 IO 实现数据持久化,在单机并发读场景下,性能通常局限于 1k-10k QPS(受磁盘寻道时间、页缓存命中率影响),难以满足秒杀、大促等峰值需求。 Redis 作为开源高性能内存数据库,凭借 内存级 IO 特性(读 QPS 可达 10 万 - 100 万,写 QPS 可达 5 万 - 50 万)、丰富的数据结构和灵活的过期策略,成为 MySQL 缓存的首选方案。 一、技术原理:Redis 与 MySQL 的协作核心Redis 作为 MySQL 缓存的本质是 “将热点数据从磁盘迁移到内存”,但需解决数据一致性、缓存命中率、异常场景处理三大核心问题。 要点 1:缓存协作模型选型(4 种经典模式)Redis 与 MySQL...
Leetcode 0316. 去除重复字母
316. 去除重复字母给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。 示例 1: 12输入:s = "bcabc"输出:"abc" 示例 2: 12输入:s = "cbacdcbc"输出:"acdb" 核心思路是单调栈 + 贪心算法,通过维护一个单调递增的栈来确保字典序最小,同时利用计数数组判断字符是否还有剩余,确保每个字符只保留一次: 预处理: 统计字符串中每个字符的出现次数(count 数组); 记录字符是否已在栈中(in_stack 数组),避免重复添加。 单调栈逻辑: 遍历字符串中的每个字符C: 减少 count[c](当前字符已被考虑); 若 c 已在栈中,直接跳过; 若 c 不在栈中,且栈不为空,且栈顶字符大于 c,且栈顶字符在后续还有出现(count[栈顶字符] > 0),则弹出栈顶字符(确保字典序更小); 将 c...
Leetcode 0409. 最长回文串
409. 最长回文串给定一个包含大写字母和小写字母的字符串 s ,返回 通过这些字母构造成的 最长的 回文串 的长度。 在构造过程中,请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串。 示例 1: 1234输入:s = "abccccdd"输出:7解释:我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。 示例 2: 123输入:s = "a"输出:1解释:可以构造的最长回文串是"a",它的长度是 1。 解题思路核心思路是统计字符频率,利用回文串的特性计算最大长度: 回文串特性: 回文串对称位置的字符相同; 偶数长度的回文串:所有字符出现次数均为偶数; 奇数长度的回文串:仅有一个字符出现奇数次(位于中心),其余均为偶数次。 频率统计: 统计字符串中每个字符的出现次数; 累计所有字符的最大偶数次(例如,出现 5 次的字符可贡献 4 次); 若存在出现奇数次的字符,可在结果中加 1(作为中心字符)。 计算逻辑: 初始化结果...
Leetcode 0045. 跳跃游戏 II
45. 跳跃游戏 II给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说,如果你在索引 i 处,你可以跳转到任意 (i + j) 处: 0 <= j <= nums[i] 且 i + j < n 返回到达 n - 1 的最小跳跃次数。测试用例保证可以到达 n - 1。 示例 1: 1234输入: nums = [2,3,1,1,4]输出: 2解释: 跳到最后一个位置的最小跳跃数是 2。 从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。 示例 2: 12输入: nums = [2,3,0,1,4]输出: 2 解题思路核心思路是贪心算法,通过跟踪 “当前跳跃的最远边界” 和 “下一次跳跃的最远可达位置”,在每一步跳跃时选择最优范围,实现最少跳跃次数: 贪心策略: 用 end 表示当前跳跃能到达的最远边界(初始为 0); 用 max_reach 表示从当前位置到 end 之间的所有位置能跳到的最远位置; 用...
通用代码审查清单整理
一、常规功能与可读性(P1+P2) 序号 审查项 判定标准(二元可验证) 优先级 1 功能实现完整性 代码覆盖所有预期需求点(对照需求清单无遗漏,无逻辑错误,如模板特化、重载函数功能符合设计) P1 2 代码易懂性 新接手开发者可在 10 分钟内理解核心逻辑(无过度复杂模板嵌套、晦涩宏定义、无拼音 / 英文混杂命名) P1 3 编码规范符合性 完全匹配团队 C++ 规范(如大括号位置、命名空间使用、const/constexpr正确修饰、缩进无违规) P1 4 冗余代码清理 无重复代码(≥3 行相同逻辑未抽取为函数 / 模板)、无注释掉的无效代码块(如废弃的类成员、未使用的全局函数) P1 5 循环安全性 循环有明确终止条件(无死循环风险),无循环内重计算(如重复获取std::vector长度),无迭代器失效场景(如循环中增删容器元素) P1 6 全局变量 / 对象合理性 无不必要全局变量(可替换为局部变量 /...

