1. Redis 和 MySQL 之间的数据同步与一致性如何保证?
数据同步方案
核心采用「更新数据库 → 消息队列通知 → 更新缓存」的流程:
- 数据更新时优先写入 MySQL,确保数据持久化落地;
- 通过 RabbitMQ/Kafka 等消息队列发送缓存更新通知(替代 Redis Pub/Sub 的不可靠方案);
- 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 设计规范
- 资源命名:用名词(复数)表示资源,如
/users、/projects,禁用动词; - HTTP 方法语义:GET(查询)、POST(创建)、PUT(全量更新)、PATCH(部分更新)、DELETE(删除);
- 状态码:遵循语义,如 200(成功)、201(创建成功)、400(参数错误)、401(未授权)、500(服务端错误);
- 版本控制:URL 中加版本(
/v1/users)或请求头传版本号; - 过滤分页:通过查询参数实现,如
/users?page=1&size=10&role=admin。
HTTPS 原理
HTTPS = HTTP + TLS/SSL,核心通过「加密+认证+验完整性」保障通信安全:
- TLS 握手阶段 :
- 客户端:发送随机数 + 支持的加密套件;
- 服务端:返回随机数 + 数字证书(含公钥);
- 客户端:验证证书合法性,生成预主密钥,用服务端公钥加密后发送;
- 双方:基于 3 个随机数生成会话密钥(对称加密密钥);
- 数据传输阶段:HTTP 数据通过会话密钥对称加密传输,同时用消息摘要验完整性、数字证书验身份,防止窃听/篡改/伪造。
5. 脏读与幻读概念
- 脏读:事务 A 读取了事务 B 未提交的更新数据,若 B 回滚,A 读取的是无效"脏数据",仅发生在「读未提交」隔离级别;
- 幻读:事务 A 两次查询同一范围数据时,事务 B 插入/删除该范围数据,导致两次查询行数不一致("幻觉");与不可重复读的区别:不可重复读是数据内容修改,幻读是行数变化。
6. Redis ZSet 为何用跳表?
Redis ZSet 选择跳表而非红黑树,核心原因:
- 实现复杂度低:跳表的增删查逻辑比红黑树简单,代码维护成本低;
- 查询性能相当:时间复杂度均为 O(logN),满足排序查询需求;
- 范围查询高效:红黑树范围查询需中序遍历,跳表可通过上层索引快速定位边界,适配 ZRANGE/ZREVRANGE 等高频场景;
- 并发友好:跳表更新仅涉及局部节点,锁粒度小;红黑树旋转调整涉及多节点,并发性能受限。
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 等存储系统,核心适配"有序+范围查询"场景。