12.9 学习笔记

1. Redis 和 MySQL 之间的数据同步与一致性如何保证?

数据同步方案

核心采用「更新数据库 → 消息队列通知 → 更新缓存」的流程:

  1. 数据更新时优先写入 MySQL,确保数据持久化落地;
  2. 通过 RabbitMQ/Kafka 等消息队列发送缓存更新通知(替代 Redis Pub/Sub 的不可靠方案);
  3. Redis 订阅节点接收通知后,主动从 MySQL 拉取最新数据更新缓存,保证缓存数据来源可靠。
一致性保障策略
  • 弱一致性(非核心数据):允许缓存与数据库短时间不一致,依赖缓存过期机制(设置合理 TTL)最终达成数据一致,实现成本低、性能影响小;
  • 强一致性(核心数据)
    ① 分布式事务:引入 Seata TCC 模式,将「更新数据库」和「更新缓存」纳入事务,保证操作原子性;
    ② 缓存操作策略:采用「先删缓存 → 更新数据库 → 延迟双删缓存」,避免并发场景下的缓存脏数据;
    ③ 兜底校验:定时任务对比缓存与数据库数据,发现不一致时自动修复。

2. Go 数组和切片的区别?

特性 数组 切片
长度特性 固定长度,声明时指定且不可修改 动态长度,随元素增删自动变化
底层结构 单一连续内存块,直接存元素 引用类型(指针+len+cap),指向底层数组
扩容机制 无扩容能力,需手动复制到新数组 自动扩容:<256 倍扩容,≥256 按 1.25 倍扩容,生成新底层数组
传参特性 值传递,拷贝整个数组,效率低 引用传递,仅拷贝 3 个底层字段,效率高

3. 如何排查 Goroutine 导致的 CPU 占用过高?

步骤 1:定位高 CPU 进程

Linux 环境下执行 top 命令,筛选出 CPU 占比高的 Go 进程,记录 PID。

步骤 2:采集性能数据
  • CPU 采样:go tool pprof -seconds 10 http://<进程地址>:<端口>/debug/pprof/profile
  • 协程栈信息:go tool pprof http://<进程地址>:<端口>/debug/pprof/goroutine
步骤 3:分析核心问题
  • 在 pprof 交互界面,通过 top 查看高 CPU 函数、list 定位具体代码行、web 生成调用图;
  • 重点排查死循环、高频计算、无阻塞的循环逻辑等异常。
步骤 4:进阶可视化分析
  • go-torch 生成火焰图,直观展示 CPU 占用分布;
  • 通过 trace 工具(go tool trace)分析协程调度轨迹,确认是否存在频繁切换、锁竞争等问题。

4. RESTful API 设计规范 & HTTPS 原理

RESTful API 设计规范
  1. 资源命名:用名词(复数)表示资源,如 /users/projects,禁用动词;
  2. HTTP 方法语义:GET(查询)、POST(创建)、PUT(全量更新)、PATCH(部分更新)、DELETE(删除);
  3. 状态码:遵循语义,如 200(成功)、201(创建成功)、400(参数错误)、401(未授权)、500(服务端错误);
  4. 版本控制:URL 中加版本(/v1/users)或请求头传版本号;
  5. 过滤分页:通过查询参数实现,如 /users?page=1&size=10&role=admin
HTTPS 原理

HTTPS = HTTP + TLS/SSL,核心通过「加密+认证+验完整性」保障通信安全:

  1. TLS 握手阶段
    • 客户端:发送随机数 + 支持的加密套件;
    • 服务端:返回随机数 + 数字证书(含公钥);
    • 客户端:验证证书合法性,生成预主密钥,用服务端公钥加密后发送;
    • 双方:基于 3 个随机数生成会话密钥(对称加密密钥);
  2. 数据传输阶段:HTTP 数据通过会话密钥对称加密传输,同时用消息摘要验完整性、数字证书验身份,防止窃听/篡改/伪造。

5. 脏读与幻读概念

  • 脏读:事务 A 读取了事务 B 未提交的更新数据,若 B 回滚,A 读取的是无效"脏数据",仅发生在「读未提交」隔离级别;
  • 幻读:事务 A 两次查询同一范围数据时,事务 B 插入/删除该范围数据,导致两次查询行数不一致("幻觉");与不可重复读的区别:不可重复读是数据内容修改,幻读是行数变化。

6. Redis ZSet 为何用跳表?

Redis ZSet 选择跳表而非红黑树,核心原因:

  1. 实现复杂度低:跳表的增删查逻辑比红黑树简单,代码维护成本低;
  2. 查询性能相当:时间复杂度均为 O(logN),满足排序查询需求;
  3. 范围查询高效:红黑树范围查询需中序遍历,跳表可通过上层索引快速定位边界,适配 ZRANGE/ZREVRANGE 等高频场景;
  4. 并发友好:跳表更新仅涉及局部节点,锁粒度小;红黑树旋转调整涉及多节点,并发性能受限。

7. 跳表的插入、删除、修改操作(面试结构化回答)

前置:跳表核心特性

跳表是概率型有序数据结构,基于多层索引链表实现(Level 0 是完整有序链表,上层为稀疏索引),平均时间复杂度 O(logN),实现简单且支持高效范围查询。

1. 插入操作

核心:找各层前驱 + 随机生成层高 + 更新指针

① 定位前驱:从最高层向下遍历,用 update 数组记录每一层"值小于目标值"的最后一个节点;

② 校验重复:遍历到 Level 0,若目标值已存在则返回(业务可支持重复值,调整判断逻辑);

③ 随机层高:以 50% 概率停止层高增长,保证索引层稀疏性;

④ 插入节点:若新层高超过跳表最大层高,补充 update 数组高层前驱(指向头节点),将新节点插入各层前驱后继位置,更新跳表最大层高。

2. 删除操作

核心:定位待删节点 + 更新前驱指针 + 清理空索引层

① 定位前驱:同插入逻辑,用 update 数组记录各层前驱;

② 校验存在性:Level 0 确认节点存在,不存在则返回;

③ 移除节点:遍历各层,将前驱后继指针跳过待删节点;

④ 清理层高:若最高层索引无节点,降低跳表最大层高,避免无效遍历。

3. 修改操作

两种实现方式:

  • 高效方式:按查找逻辑定位目标节点,直接修改值(O(logN));
  • 兼容方式:先删除旧值,再插入新值(兼容旧值不存在场景,略增开销)。
关键细节补充

① 随机层高:避免层级退化,是跳表保持 O(logN) 复杂度的核心;

update 数组:一次性记录各层前驱,将多轮遍历合并为一轮,保证性能;

③ 重复值处理:遍历判断改为"值小于等于目标值",Level 0 处理重复节点。

应用场景

跳表因实现简单、并发友好,被用于 Redis ZSet、LevelDB 等存储系统,核心适配"有序+范围查询"场景。

相关推荐
不会代码的小猴9 小时前
C++的第九天笔记
开发语言·c++·笔记
专注于大数据技术栈9 小时前
java学习--枚举(Enum)
java·学习
我命由我1234510 小时前
开发中的英语积累 P19:Inspect、Hint、Feedback、Direction、Compact、Vulnerability
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
老王熬夜敲代码11 小时前
C++中的thread
c++·笔记·面试
qq_124987075311 小时前
基于SpringBoot学生学习历史的选课推荐系统的设计与实现(源码+论文+部署+安装)
java·spring boot·后端·学习·毕业设计·毕设
崇山峻岭之间12 小时前
C++ Prime Plus 学习笔记033
c++·笔记·学习
暗然而日章12 小时前
C++基础:Stanford CS106L学习笔记 7 类
c++·笔记·学习
思成不止于此12 小时前
【MySQL 零基础入门】DDL 核心语法全解析:数据库与表结构操作篇
数据库·笔记·学习·mysql
学编程的闹钟12 小时前
86【CSS的模块化处理】
学习