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 等存储系统,核心适配"有序+范围查询"场景。

相关推荐
驭渊的小故事8 小时前
简单模板笔记
数据结构·笔记·算法
野犬寒鸦9 小时前
从零起步学习并发编程 || 第七章:ThreadLocal深层解析及常见问题解决方案
java·服务器·开发语言·jvm·后端·学习
陈桴浮海9 小时前
【Linux&Ansible】学习笔记合集二
linux·学习·ansible
xhbaitxl9 小时前
算法学习day39-动态规划
学习·算法·动态规划
智者知已应修善业9 小时前
【洛谷P9975奶牛被病毒传染最少数量推导,导出多样例】2025-2-26
c语言·c++·经验分享·笔记·算法·推荐算法
ZH15455891319 小时前
Flutter for OpenHarmony Python学习助手实战:数据库操作与管理的实现
python·学习·flutter
Junlan279 小时前
Cursor使用入门及连接服务器方法(更新中)
服务器·人工智能·笔记
risc12345610 小时前
如何认识结构?结构 = 要素 + 关系 + 动态
笔记
试着10 小时前
【huawei】机考整理
学习·华为·面试·机试
風清掦10 小时前
【江科大STM32学习笔记-05】EXTI外部中断11
笔记·stm32·学习