简介
针对自己的实际情况,让 opus 帮我列了个学习计划,后续一一跟进。
后端开发进阶学习计划
适用对象:Go 应用层熟练,底层原理薄弱的非科班程序员 周期:20周(核心16周 + 云原生4周) 每日投入:工作日 2-3 小时,周末 4-6 小时
目录
- 一、学习方法论
- 二、能力现状分析
- 三、总体规划
- 四、阶段一:数据结构与算法 + MySQL(第1-4周)
- 五、阶段二:Redis + 网络 + Kafka(第5-8周)
- 六、阶段三:操作系统 + Go底层(第9-12周)
- 七、阶段四:分布式 + 系统设计 + 面试冲刺(第13-16周)
- 八、阶段五:云原生 Docker + K8s(第17-20周)
- 九、学习资源汇总
- 十、面试高频问题清单
- 十一、每日执行模板
一、学习方法论
1.1 费曼学习法(核心方法)
第1步:学习概念
↓
第2步:用简单语言教给别人(或写笔记)
↓
第3步:发现卡壳的地方 → 回去重新学
↓
第4步:简化、类比,直到能用大白话讲清楚
实践方式:
- 每学完一个知识点,假装讲给不懂技术的人听
- 写技术笔记(不用发布,写给自己看)
- 能画图就画图,能举例就举例
1.2 学习金字塔
被动学习(效率低)
├── 听讲/看视频 5% 留存率
├── 阅读 10%
└── 看演示 30%
主动学习(效率高)✓
├── 讨论/复述 50%
├── 实践练习 75% ← 刷题、写代码
└── 教给别人 90% ← 写博客、讲给同事
1.3 间隔重复法
对抗遗忘曲线:
| 时间节点 | 动作 |
|---|---|
| 学完当天 | 晚上回顾一次 |
| 第2天 | 快速回顾 |
| 第7天 | 再次复习 |
| 第30天 | 巩固复习 |
工具推荐:
- Anki(记忆卡片)- 记八股文
- Notion/Obsidian - 构建知识体系
1.4 单个知识点学习 SOP
以 “B+树” 为例:
| 维度 | 问题 |
|---|---|
| What | B+树是什么?结构特点? |
| Why | 为什么MySQL选B+树而不是B树/红黑树? |
| How | B+树如何查找、插入、删除? |
| Where | 应用场景:MySQL索引、文件系统 |
| Compare | B树 vs B+树 vs 红黑树 vs 跳表 |
| Practice | 手画B+树结构图,模拟插入过程 |
1.5 每日时间分配
工作日(2-3小时):
早起/午休:算法 1题(30-60分钟)
晚上:理论学习(1-1.5小时)
睡前:复习当天内容(15分钟)
周末(4-6小时):
算法 2-3 题
知识点深入学习
整理笔记、复习
二、能力现状分析
2.1 能力画像
┌─────────────────────────────────────┐
│ ✅ 应用层(强) │
│ Go + Gorm + Redis + 缓存方案 │
│ Single Flight / 布隆过滤器 │
├─────────────────────────────────────┤
│ ⚠️ 中间件原理层(待补) │
│ MySQL索引原理 / Redis数据结构底层 │
│ 消息队列原理 / 存储引擎 │
├─────────────────────────────────────┤
│ ❌ 计算机基础层(弱) │
│ 数据结构与算法 / 操作系统 / 网络 │
│ 编译原理 / 计算机组成 │
└─────────────────────────────────────┘
2.2 学习优先级
| 优先级 | 领域 | 面试权重 | 现状 |
|---|---|---|---|
| P0 | 数据结构与算法 | ⭐⭐⭐⭐⭐ | ❌ 急需补 |
| P0 | MySQL 原理 | ⭐⭐⭐⭐⭐ | ❌ 急需补 |
| P0 | Redis 原理 | ⭐⭐⭐⭐ | ⚠️ 会用不懂底层 |
| P1 | 计算机网络 | ⭐⭐⭐⭐ | ⚠️ 待补 |
| P1 | 操作系统 | ⭐⭐⭐⭐ | ❌ 待补 |
| P1 | Kafka | ⭐⭐⭐ | ❌ 待补 |
| P2 | Go 语言底层 | ⭐⭐⭐ | ⚠️ 待深入 |
| P2 | 分布式系统 | ⭐⭐⭐ | ⚠️ 待系统化 |
| P3 | Docker/K8s | ⭐⭐⭐ | ❌ 待补 |
| P3 | 系统设计 | ⭐⭐⭐ | ⚠️ 经验有,理论欠 |
三、总体规划
┌─────────────────────────────────────────────────────────────┐
│ 阶段一(第1-4周):数据结构与算法 + MySQL │
│ 目标:建立算法思维,理解数据库核心原理 │
├─────────────────────────────────────────────────────────────┤
│ 阶段二(第5-8周):Redis + 网络 + Kafka │
│ 目标:中间件原理,网络协议栈,消息队列 │
├─────────────────────────────────────────────────────────────┤
│ 阶段三(第9-12周):操作系统 + Go底层 │
│ 目标:系统级理解,语言底层原理 │
├─────────────────────────────────────────────────────────────┤
│ 阶段四(第13-16周):分布式 + 系统设计 + 面试冲刺 │
│ 目标:高阶知识,面试模拟 │
├─────────────────────────────────────────────────────────────┤
│ 阶段五(第17-20周):云原生 Docker + K8s │
│ 目标:容器化与编排原理 │
└─────────────────────────────────────────────────────────────┘
四、阶段一:数据结构与算法 + MySQL(第1-4周)
4.1 第1周:数组、链表、哈希表 + MySQL索引
算法部分
| 天 | 主题 | 必做题目 | 学习方法 |
|---|---|---|---|
| Day1 | 数组基础 | 两数之和、移除元素 | 先看题解理解思路,再独立写 |
| Day2 | 双指针 | 三数之和、盛水容器 | 画图模拟指针移动 |
| Day3 | 滑动窗口 | 无重复字符最长子串 | 总结滑动窗口模板 |
| Day4 | 链表基础 | 反转链表、合并链表 | 画图!每一步都画 |
| Day5 | 链表进阶 | 环形链表、相交链表 | 快慢指针原理 |
| Day6-7 | 哈希表 | 字母异位词、两数之和 | 理解Go map底层 |
如何刷算法题:
1. 读题,思考5分钟
2. 没思路就看题解,理解思路
3. 关掉题解,自己写一遍
4. 写不出来再看,直到能独立写出
5. 总结该题型的模板/套路
6. 一周后再做一遍(间隔重复)
MySQL部分
学习内容:
- InnoDB vs MyISAM 区别
- B树 vs B+树(重点!)
- 聚簇索引 vs 非聚簇索引
- 回表与覆盖索引
学习方法:
1. 看视频/文章理解概念(1小时)
2. 手画 B+树结构图
3. 用费曼法写出:为什么MySQL用B+树?
4. 实操:建表建索引,用EXPLAIN分析
输出要求:
- 一篇笔记《B+树为什么适合数据库索引》
- 能画出B+树结构图
- 能说清楚聚簇索引和非聚簇索引的区别
第1周检验标准
- 手写链表反转(不看任何参考)
- 画出B+树结构,标注叶子节点链表
- 说出B树和B+树的3个核心区别
- 解释什么是回表查询
4.2 第2周:栈、队列、二叉树 + MySQL事务
算法部分
| 天 | 主题 | 必做题目 |
|---|---|---|
| Day1 | 栈基础 | 有效括号、最小栈 |
| Day2 | 单调栈 | 每日温度、下一个更大元素 |
| Day3 | 队列/BFS | 岛屿数量、腐烂的橘子 |
| Day4 | 二叉树遍历 | 前中后序遍历(递归+迭代) |
| Day5 | 二叉树属性 | 最大深度、对称二叉树 |
| Day6-7 | 二叉树构造 | 从遍历序列构造树、验证BST |
Go中栈的实现:
// 用slice模拟栈
stack := []int{}
stack = append(stack, 1) // push
top := stack[len(stack)-1] // peek
stack = stack[:len(stack)-1] // pop
理解要点:
- 栈在内存中:函数调用栈、局部变量
- 堆在内存中:动态分配、需要GC
MySQL部分
学习内容:
- ACID特性(每个字母含义)
- 事务隔离级别(4种)
- 脏读、不可重复读、幻读
- MVCC原理(重点!)
学习方法:
1. 先理解问题:没有隔离会出现什么问题?
2. 再理解方案:每种隔离级别解决什么问题?
3. 深入原理:MVCC如何实现可重复读?
4. 实操:开两个MySQL客户端,模拟并发事务
输出要求:
- 画出MVCC的版本链示意图
- 写清楚ReadView的可见性判断规则
第2周检验标准
- 手写二叉树前序遍历(递归和迭代两种)
- 说出4种隔离级别及各自解决的问题
- 讲清楚MVCC的实现原理
4.3 第3周:排序、二分、堆 + MySQL锁
算法部分
| 天 | 主题 | 必做题目 |
|---|---|---|
| Day1 | 快速排序 | 手写快排、数组第K大元素 |
| Day2 | 归并排序 | 手写归并、逆序对 |
| Day3 | 二分基础 | 二分查找、搜索插入位置 |
| Day4 | 二分进阶 | 旋转数组、寻找峰值 |
| Day5 | 堆基础 | TopK问题、数据流中位数 |
| Day6-7 | 堆应用 | 合并K个链表、丑数II |
快排模板(必须能手写):
func quickSort(nums []int, left, right int) {
if left >= right {
return
}
pivot := partition(nums, left, right)
quickSort(nums, left, pivot-1)
quickSort(nums, pivot+1, right)
}
func partition(nums []int, left, right int) int {
pivot := nums[right]
i := left
for j := left; j < right; j++ {
if nums[j] < pivot {
nums[i], nums[j] = nums[j], nums[i]
i++
}
}
nums[i], nums[right] = nums[right], nums[i]
return i
}
二分模板:
func binarySearch(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right {
mid := left + (right-left)/2
if nums[mid] == target {
return mid
} else if nums[mid] < target {
left = mid + 1
} else {
right = mid - 1
}
}
return -1
}
MySQL部分
学习内容:
- 锁的类型:行锁、表锁、意向锁
- 行锁的类型:记录锁、间隙锁、临键锁
- 死锁的产生和处理
- 锁与索引的关系
学习方法:
1. 理解为什么需要锁?(并发控制)
2. 不同锁解决什么问题?
3. 实操:制造死锁,观察MySQL如何处理
第3周检验标准
- 手写快排,讲清楚时间复杂度
- 手写二分查找(基础版和左边界版)
- 说出间隙锁的作用
- 描述一个死锁场景
4.4 第4周:回溯、动态规划、位运算 + MySQL综合
算法部分
| 天 | 主题 | 必做题目 |
|---|---|---|
| Day1 | 回溯基础 | 全排列、子集 |
| Day2 | 回溯剪枝 | 组合总和、N皇后 |
| Day3 | DP入门 | 爬楼梯、斐波那契 |
| Day4 | DP进阶 | 打家劫舍、零钱兑换 |
| Day5 | 二维DP | 不同路径、最小路径和 |
| Day6 | 背包问题 | 01背包、完全背包 |
| Day7 | 位运算 | 只出现一次的数、位1的个数 |
回溯模板:
func backtrack(path []int, choices []int) {
if 满足结束条件 {
result = append(result, copy(path))
return
}
for _, choice := range choices {
if 不合法 {
continue
}
path = append(path, choice) // 做选择
backtrack(path, choices) // 递归
path = path[:len(path)-1] // 撤销选择
}
}
DP思考框架:
1. 定义状态:dp[i] 的含义是什么
2. 状态转移:dp[i] 和 dp[i-1] 的关系
3. 初始条件:dp[0] = ?
4. 遍历顺序:从前往后还是从后往前
位运算技巧:
// n & (n-1) 消除最低位的1
// 应用:统计1的个数
func hammingWeight(n uint32) int {
count := 0
for n != 0 {
n &= n - 1
count++
}
return count
}
// 判断是否是2的幂
func isPowerOfTwo(n int) bool {
return n > 0 && n&(n-1) == 0
}
MySQL部分
学习内容:
- 慢查询优化实战
- EXPLAIN执行计划分析
- 索引优化原则
- 分库分表概念
实战练习:
1. 拿你项目中的SQL,用EXPLAIN分析
2. 找出可以优化的点
3. 加索引、改写SQL,对比效果
第4周检验标准
- 手写回溯解决全排列问题
- 讲清楚动态规划的解题步骤
- 说出 n & (n-1) 的作用
- 分析一条SQL的执行计划
阶段一总检验清单
算法:
- 手写快排,讲清楚时间复杂度
- 手写二分查找(左边界、右边界版本)
- 手写链表反转
- 说出回溯和DFS的关系
- 讲清楚动态规划的解题步骤
- n & (n-1) 有什么用?
MySQL:
- B+树的特点(至少说出4点)
- 为什么用B+树不用B树?
- MVCC的实现原理(版本链+ReadView)
- 4种隔离级别分别解决什么问题
- 什么情况下索引会失效(至少5种)
- 聚簇索引和非聚簇索引的区别
五、阶段二:Redis + 网络 + Kafka(第5-8周)
算法继续每天1题保持手感,主要精力放在中间件和网络
5.1 第5周:Redis数据结构底层
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | String底层 | SDS结构、预分配、惰性释放 |
| Day3-4 | List/Hash底层 | QuickList、ZipList、渐进式rehash |
| Day5-7 | ZSet底层 | 跳表原理(重点!) |
跳表详解
为什么ZSet用跳表不用红黑树?
1. 实现简单:跳表代码比红黑树简单很多
2. 范围查询:跳表天然支持范围查询,O(logN+M)
3. 内存友好:跳表的内存分配更加友好
4. 并发友好:跳表更容易实现无锁并发
跳表结构:
Level 3: 1 -----------------> 9
Level 2: 1 ------> 5 -------> 9
Level 1: 1 -> 3 -> 5 -> 7 -> 9
Level 0: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
学习方法
1. 先理解数据结构本身(跳表是什么)
2. 再理解Redis为什么选择它
3. 对比其他方案(红黑树、B+树)
4. 手画跳表结构图
5. 可选:看Redis源码 t_zset.c
输出要求
- 画出跳表结构图
- 写一篇《Redis ZSet底层为什么用跳表》
第5周检验标准
- SDS相比C字符串的优势(3点以上)
- 跳表的查找过程(画图说明)
- 为什么ZSet用跳表不用红黑树
5.2 第6周:Redis高级特性 + Kafka
Redis部分
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | 持久化 | RDB原理、AOF原理、混合持久化 |
| Day3-4 | 高可用 | 主从复制、Sentinel、Cluster |
持久化对比:
| 特性 | RDB | AOF |
|---|---|---|
| 原理 | 快照 | 追加日志 |
| 恢复速度 | 快 | 慢 |
| 数据安全 | 可能丢失 | 更安全 |
| 文件大小 | 小 | 大 |
Cluster原理要点:
- 16384个槽位
- 节点间Gossip协议通信
- 客户端重定向(MOVED/ASK)
Kafka部分
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day5-7 | Kafka原理 | 架构、生产者、消费者、存储 |
Kafka架构:
Producer --> Broker(Topic/Partition) --> Consumer Group
|
Replica
核心概念:
- Broker:Kafka服务节点
- Topic:消息主题
- Partition:分区(并行度单位)
- Consumer Group:消费组
- Offset:消费位移
为什么Kafka这么快?
1. 顺序写磁盘(比随机写快很多)
2. Page Cache(利用操作系统缓存)
3. 零拷贝(sendfile系统调用)
4. 批量发送 + 压缩
5. 分区并行
消息可靠性保证:
生产者端:acks=all + 重试
Broker端:副本机制 + ISR
消费者端:手动提交offset + 幂等处理
第6周检验标准
- RDB和AOF的区别、优缺点
- Redis Cluster如何处理节点故障
- Kafka为什么吞吐量这么高(4点以上)
- 如何保证Kafka消息不丢失
5.3 第7周:TCP/IP协议栈
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | TCP核心 | 三次握手、四次挥手、可靠传输 |
| Day3-4 | TCP进阶 | 滑动窗口、拥塞控制 |
| Day5-7 | 应用层 | HTTP演进、HTTPS、DNS |
三次握手
Client Server
| |
|------ SYN, seq=x ------------>|
| |
|<---- SYN+ACK, seq=y, ack=x+1 -|
| |
|------ ACK, ack=y+1 ---------->|
| |
为什么是三次?
- 确认双方的发送和接收能力
- 防止历史连接初始化
四次挥手
Client Server
| |
|------ FIN, seq=u ------------>|
| |
|<----- ACK, ack=u+1 -----------|
| |
|<----- FIN, seq=w -------------|
| |
|------ ACK, ack=w+1 ---------->|
| |
|------ TIME_WAIT (2MSL) -------|
为什么要TIME_WAIT?
- 确保最后一个ACK能到达
- 让旧连接的报文在网络中消失
HTTPS握手
1. Client Hello(支持的加密套件)
2. Server Hello(选择的加密套件 + 证书)
3. 客户端验证证书
4. 客户端生成预主密钥,用服务器公钥加密发送
5. 双方计算会话密钥
6. 后续用会话密钥对称加密通信
学习方法
1. 抓包实践:用Wireshark抓TCP握手
2. 画时序图:每个协议都画出交互过程
3. 理解设计:为什么这样设计?解决什么问题?
第7周检验标准
- 画出三次握手过程,解释每一步作用
- 为什么要TIME_WAIT?等多久?
- HTTPS握手过程(简化版能讲清楚)
- HTTP/1.1 和 HTTP/2 的区别
5.4 第8周:IO模型 + Nginx
IO模型
| 模型 | 特点 | 适用场景 |
|---|---|---|
| 阻塞IO | 简单,但效率低 | 连接数少 |
| 非阻塞IO | 轮询,CPU浪费 | 很少使用 |
| IO多路复用 | 一个线程处理多个连接 | 高并发服务器 |
| 信号驱动IO | 信号通知 | 较少使用 |
| 异步IO | 完全异步 | Windows IOCP |
epoll相比select的优势:
1. 没有最大连接数限制(select是1024)
2. 不需要每次都传递fd集合
3. 事件驱动,不需要轮询
4. 使用mmap减少拷贝
Nginx
正向代理 vs 反向代理:
正向代理:代理客户端,隐藏客户端
Client --> Proxy --> Server
反向代理:代理服务器,隐藏服务器
Client --> Nginx --> Backend Servers
负载均衡策略:
- 轮询(默认)
- 加权轮询
- IP Hash
- 最少连接
- 一致性Hash
第8周检验标准
- 说出5种IO模型
- epoll相比select的优势(3点以上)
- 正向代理和反向代理的区别
- Nginx常用的负载均衡策略
阶段二总检验清单
Redis:
- 跳表的查找过程(画图说明)
- ZSet为什么用跳表不用红黑树
- RDB和AOF的区别、优缺点
- Redis Cluster如何处理节点故障
- Redis为什么单线程还这么快
Kafka:
- Kafka架构,各组件作用
- 为什么Kafka吞吐量这么高
- 如何保证消息不丢失
- Consumer Group的Rebalance机制
网络:
- TCP三次握手每一步的作用
- 为什么要TIME_WAIT?等多久?
- HTTPS握手过程
- epoll相比select的优势
六、阶段三:操作系统 + Go底层(第9-12周)
6.1 第9周:进程、线程、内存
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | 进程与线程 | 区别、进程间通信、线程同步 |
| Day3-4 | 内存管理 | 虚拟内存、分页、页面置换 |
| Day5-7 | 堆栈 | 堆 vs 栈、函数调用栈 |
进程 vs 线程 vs 协程
| 特性 | 进程 | 线程 | 协程 |
|---|---|---|---|
| 调度 | 操作系统 | 操作系统 | 用户态 |
| 内存 | 独立地址空间 | 共享地址空间 | 共享 |
| 切换成本 | 高 | 中 | 低 |
| 通信 | IPC | 共享内存 | 共享内存 |
| Go中 | - | M | G |
堆 vs 栈
| 特性 | 栈 | 堆 |
|---|---|---|
| 管理方式 | 自动分配释放 | 手动/GC管理 |
| 分配速度 | 快(移动指针) | 慢(寻找空间) |
| 空间大小 | 小(通常MB级) | 大 |
| 内存布局 | 连续 | 不连续 |
| 生命周期 | 函数结束即释放 | 需要GC回收 |
| Go中 | goroutine栈 | 逃逸后分配 |
学习方法
1. 画内存布局图
2. 结合Go理解:变量什么时候在栈、什么时候在堆
3. 用 go build -gcflags="-m" 分析逃逸
第9周检验标准
- 进程、线程、协程的区别
- 堆和栈的5个核心区别
- 进程间通信有哪些方式
- 什么是死锁?如何预防?
6.2 第10周:Go内存管理与GC
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | 内存分配 | TCMalloc思想、mcache/mcentral/mheap |
| Day3-4 | 逃逸分析 | 什么情况会逃逸 |
| Day5-7 | GC | 三色标记、写屏障 |
Go内存分配
┌─────────────────────────────────────┐
│ mheap │ 全局唯一,管理所有内存
├─────────────────────────────────────┤
│ mcentral mcentral ... │ 每种规格一个,全局
├──────────┬──────────┬───────────────┤
│ mcache │ mcache │ ... │ 每个P一个,本地缓存
├──────────┴──────────┴───────────────┤
│ Goroutine 内存申请 │
└─────────────────────────────────────┘
逃逸分析
常见逃逸场景:
// 1. 返回局部变量指针
func foo() *int {
x := 1
return &x // x逃逸到堆
}
// 2. interface{}类型
func bar(v interface{}) {}
func main() {
x := 1
bar(x) // x逃逸
}
// 3. 闭包引用外部变量
func closure() func() int {
x := 1
return func() int {
return x // x逃逸
}
}
// 4. slice/map扩容
s := make([]int, 0)
s = append(s, 1, 2, 3...) // 可能逃逸
分析命令:
go build -gcflags="-m" main.go
GC三色标记
白色:未被标记的对象(可能是垃圾)
灰色:已被标记,但其引用的对象未被标记
黑色:已被标记,且其引用的对象也已被标记
标记过程:
1. 所有对象初始为白色
2. 从根对象开始,标记为灰色
3. 从灰色对象出发,将其引用的白色对象标记为灰色
4. 将灰色对象标记为黑色
5. 重复3-4,直到没有灰色对象
6. 剩余的白色对象就是垃圾
写屏障:
- 解决并发标记时的对象丢失问题
- Go 1.8+ 使用混合写屏障
第10周检验标准
- Go内存分配的三级结构
- 什么情况会发生逃逸(至少4种)
- 三色标记法的过程
- 写屏障的作用
6.3 第11周:Go调度器GMP
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-3 | GMP模型 | G/M/P的结构和关系 |
| Day4-5 | 调度流程 | 创建、调度、抢占 |
| Day6-7 | 实践与源码 | GODEBUG观察调度 |
GMP模型
┌─────────────────────────────────────────────────┐
│ Global Queue │ 全局队列
└───────────────────────┬─────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│ P │ │ P │ │ P │ Processor
│┌─────┐│ │┌─────┐│ │┌─────┐│
││Local││ ││Local││ ││Local││ 本地队列
││Queue││ ││Queue││ ││Queue││
│└─────┘│ │└─────┘│ │└─────┘│
└───┬───┘ └───┬───┘ └───┬───┘
│ │ │
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│ M │ │ M │ │ M │ Machine(OS线程)
└───────┘ └───────┘ └───────┘
G(Goroutine):
- Go协程,包含栈、状态、调度信息
- 初始栈2KB,可动态扩缩容
M(Machine):
- 操作系统线程
- 执行G的实体
- 有g0(调度栈)和curg(当前执行的G)
P(Processor):
- 逻辑处理器
- 默认等于CPU核数(GOMAXPROCS)
- 维护本地G队列和mcache
调度场景
1. goroutine创建 → 放入P的本地队列
2. M执行完当前G → 从本地队列取下一个G
3. 本地队列空 → 从全局队列取
4. 全局队列也空 → 从其他P偷(work stealing)
5. 系统调用阻塞 → M和G绑定,P交给其他M
6. channel阻塞 → G放入等待队列,M执行其他G
实践命令
# 观察调度
GODEBUG=schedtrace=1000 ./your_program
# 输出示例
SCHED 1000ms: gomaxprocs=8 idleprocs=6 threads=5 runqueue=0 [0 0 0 0 0 0 0 0]
第11周检验标准
- 画出GMP模型图并讲解
- G/M/P分别是什么
- 什么时候会发生work stealing
- Go的抢占式调度是怎么实现的
6.4 第12周:Channel、sync包
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | Channel | hchan结构、发送接收流程 |
| Day3-4 | sync包 | Mutex、RWMutex、WaitGroup、Pool |
| Day5-7 | 综合复习 | 整理笔记、阶段性总结 |
Channel原理
hchan结构:
type hchan struct {
qcount uint // 队列中元素个数
dataqsiz uint // 环形队列大小
buf unsafe.Pointer // 环形队列指针
sendx uint // 发送索引
recvx uint // 接收索引
recvq waitq // 接收等待队列
sendq waitq // 发送等待队列
lock mutex // 锁
}
发送流程:
1. 如果recvq有等待的接收者 → 直接发送
2. 如果buf未满 → 放入buf
3. 否则 → 阻塞,放入sendq
Mutex原理
两种模式:
- 正常模式:新来的goroutine和被唤醒的竞争
- 饥饿模式:锁直接给等待最久的goroutine
正常模式 → 饥饿模式:等待超过1ms
饥饿模式 → 正常模式:等待队列空 或 等待时间<1ms
第12周检验标准
- Channel的发送和接收流程
- 有缓冲和无缓冲Channel的区别
- Mutex的正常模式和饥饿模式
- sync.Pool的作用和使用场景
阶段三总检验清单
操作系统:
- 进程、线程、协程的区别(从内存、调度角度)
- 堆和栈的5个核心区别
- 虚拟内存的作用
- 什么是死锁?产生条件?如何预防?
Go底层:
- 画出GMP模型图并讲解
- 什么情况会发生逃逸(至少4种)
- 三色标记法的过程
- Channel的发送和接收流程
- Mutex的正常模式和饥饿模式
七、阶段四:分布式 + 系统设计 + 面试冲刺(第13-16周)
7.1 第13周:分布式理论
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | 基础理论 | CAP、BASE、一致性模型 |
| Day3-5 | Raft协议 | Leader选举、日志复制 |
| Day6-7 | 分布式事务 | 2PC、TCC、Saga |
CAP理论
C(Consistency):一致性,所有节点看到相同数据
A(Availability):可用性,每个请求都能得到响应
P(Partition tolerance):分区容错,网络分区时仍能工作
三选二?
- 实际上P是必须的(网络分区必然发生)
- 所以是在C和A之间权衡
- CP系统:ZooKeeper、etcd
- AP系统:Cassandra、Eureka
Raft协议
三种角色:
- Leader:处理所有客户端请求
- Follower:被动响应
- Candidate:选举中
Leader选举:
1. Follower超时未收到心跳
2. 转为Candidate,term+1,投票给自己
3. 向其他节点请求投票
4. 获得多数票 → 成为Leader
5. 发送心跳维持领导地位
日志复制:
1. Leader收到客户端请求
2. 追加到本地日志
3. 发送AppendEntries给Followers
4. 多数确认后,提交日志
5. 通知Followers提交
分布式事务
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 2PC | 准备+提交两阶段 | 强一致 | 同步阻塞,单点故障 |
| TCC | Try-Confirm-Cancel | 灵活 | 代码侵入性强 |
| Saga | 本地事务+补偿 | 无锁 | 最终一致 |
| 本地消息表 | 消息+定时任务 | 简单可靠 | 有延迟 |
第13周检验标准
- CAP理论,三者不能同时满足的原因
- Raft的Leader选举过程
- 2PC的两个阶段分别做什么
- TCC的三个阶段分别做什么
7.2 第14周:消息队列 + 微服务
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-3 | Kafka深入 | 副本机制、ISR、Rebalance |
| Day4-5 | 微服务 | 服务发现、熔断降级、限流 |
| Day6-7 | 综合 | 消息队列对比、设计权衡 |
Kafka高可用
副本机制:
Topic
└── Partition 0
├── Leader (Broker 1)
├── Follower (Broker 2) ─┐
└── Follower (Broker 3) ─┴── ISR
ISR(In-Sync Replicas):
- 与Leader保持同步的副本集合
- acks=all 时,消息需要所有ISR确认
- Follower落后太多会被踢出ISR
熔断降级
熔断器状态:
Closed(正常) → 失败率超阈值 → Open(熔断)
│
超时
↓
Half-Open(半开)
│
┌─────────┴─────────┐
↓ ↓
成功 → Closed 失败 → Open
第14周检验标准
- Kafka的ISR机制
- Consumer Rebalance的触发条件
- 熔断器的三种状态
- 常见限流算法(令牌桶、漏桶)
7.3 第15周:系统设计
学习方法
系统设计步骤:
1. 需求澄清(5分钟)
- 功能需求
- 非功能需求(QPS、延迟、可用性)
- 规模估算
2. 高层设计(10分钟)
- 画架构图
- 核心组件
3. 详细设计(20分钟)
- 数据模型
- API设计
- 核心流程
4. 扩展与优化(10分钟)
- 瓶颈分析
- 扩展方案
练习题目
| 系统 | 核心点 |
|---|---|
| 短链服务 | 哈希/自增ID、301/302、缓存 |
| 秒杀系统 | 限流、库存扣减、削峰填谷 |
| Feed流 | 推模式/拉模式、时间线合并 |
| 分布式ID | Snowflake、号段模式 |
| 消息推送 | 长连接、WebSocket、消息可靠性 |
短链服务设计示例
需求:
- 长链 → 短链
- 短链 → 长链(重定向)
- 日活1亿,每天1000万新链接
设计:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │────▶│ Nginx │────▶│ API │
└──────────┘ └──────────┘ └────┬─────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Redis │ │ MySQL │ │ ID │
│ (Cache) │ │ (Store) │ │Generator│
└─────────┘ └─────────┘ └─────────┘
短链生成:
1. 发号器生成唯一ID
2. Base62编码 → 短码
3. 存入MySQL,缓存到Redis
重定向:
1. 短码 → 查Redis
2. 未命中 → 查MySQL → 写入Redis
3. 301/302重定向
第15周检验标准
- 能用5步法分析一个系统设计题
- 画出短链服务架构图
- 秒杀系统如何防止超卖
7.4 第16周:面试冲刺
Day1-2:八股文梳理
整理以下主题的高频问题:
- MySQL:20题
- Redis:15题
- 网络:15题
- 操作系统:10题
- Go底层:15题
- 分布式:10题
- Kafka:10题
Day3-4:项目亮点
STAR法则:
Situation:项目背景
Task:你的任务
Action:你做了什么
Result:结果(量化!)
示例:
背景:电商系统在大促时Redis缓存击穿严重
任务:设计缓存方案,保证大促稳定性
行动:
- 使用SingleFlight合并请求
- 布隆过滤器防止无效查询
- 随机过期时间防雪崩
结果:
- DB QPS下降80%
- 大促期间零故障
Day5-6:模拟面试
1. 找朋友互相mock
2. 录音回听,发现问题
3. 算法:限时做题(45分钟2题)
4. 八股:随机抽问
Day7:查漏补缺
阶段四总检验清单
分布式:
- CAP理论的含义和权衡
- Raft的Leader选举和日志复制
- 2PC/TCC/Saga的区别
- Kafka的ISR机制
系统设计:
- 短链服务设计
- 秒杀系统核心要点
- Feed流推拉模式选择
八、阶段五:云原生 Docker + K8s(第17-20周)
8.1 第17周:Docker原理
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | 底层技术 | Namespace、Cgroups、UnionFS |
| Day3-4 | 核心概念 | 镜像、容器、Dockerfile |
| Day5-7 | 网络与存储 | 网络模式、Volume |
容器 vs 虚拟机
容器架构 虚拟机架构
┌─────────────────┐ ┌─────────────────┐
│ App A App B │ │ App A │ │ App B │
├─────────────────┤ ├───────┤ ├───────┤
│ Container │ │ Guest │ │ Guest │
│ Runtime │ │ OS │ │ OS │
├─────────────────┤ ├───────┴──┴───────┤
│ Host Kernel │ │ Hypervisor │
├─────────────────┤ ├──────────────────┤
│ Host OS │ │ Host OS │
└─────────────────┘ └──────────────────┘
容器:共享内核,秒级启动,MB级
虚拟机:完整OS,分钟级启动,GB级
Docker三大核心技术
Namespace(隔离):
| Namespace | 隔离内容 |
|---|---|
| PID | 进程ID |
| NET | 网络设备、端口 |
| MNT | 文件系统挂载点 |
| UTS | 主机名 |
| IPC | 进程间通信 |
| USER | 用户和组 |
Cgroups(资源限制):
- CPU限制
- 内存限制
- IO限制
- 进程数限制
UnionFS(联合文件系统):
- 镜像分层存储
- Copy-on-Write
- OverlayFS
第17周检验标准
- 容器和虚拟机的本质区别
- Docker如何实现隔离(Namespace)
- Docker如何限制资源(Cgroups)
- 镜像分层的好处
8.2 第18周:Kubernetes基础
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | 架构 | 控制平面、工作节点、各组件作用 |
| Day3-4 | 核心资源 | Pod、Deployment、Service |
| Day5-7 | 实践 | 部署一个应用 |
K8s架构
┌──────────────────────────────────────────────────┐
│ Control Plane │
│ ┌───────────┐ ┌────────┐ ┌───────────────────┐ │
│ │API Server │ │ etcd │ │Controller Manager │ │
│ └─────┬─────┘ └────────┘ └───────────────────┘ │
│ │ ┌──────────┐ │
│ │ │Scheduler │ │
│ │ └──────────┘ │
└────────┼─────────────────────────────────────────┘
│
┌────┴────┬─────────────┐
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│ Node1 │ │ Node2 │ │ Node3 │
│┌─────┐│ │┌─────┐│ │┌─────┐│
││kubel││ ││kubel││ ││kubel││
│└─────┘│ │└─────┘│ │└─────┘│
│┌─────┐│ │┌─────┐│ │┌─────┐│
││proxy││ ││proxy││ ││proxy││
│└─────┘│ │└─────┘│ │└─────┘│
│┌─────┐│ │┌─────┐│ │┌─────┐│
││Pods ││ ││Pods ││ ││Pods ││
│└─────┘│ │└─────┘│ │└─────┘│
└───────┘ └───────┘ └───────┘
核心组件
Control Plane:
| 组件 | 作用 |
|---|---|
| API Server | 所有组件的通信枢纽 |
| etcd | 分布式KV存储,保存集群状态 |
| Scheduler | Pod调度,选择合适的Node |
| Controller Manager | 控制器,维护期望状态 |
Node:
| 组件 | 作用 |
|---|---|
| Kubelet | 管理Pod生命周期 |
| Kube-proxy | 网络代理,实现Service |
| Container Runtime | 容器运行时 |
核心资源
Pod:
- 最小调度单位
- 共享网络和存储
- 包含一个或多个容器
Deployment:
- 声明式管理Pod
- 滚动更新
- 回滚
Service:
- 服务发现
- 负载均衡
- 类型:ClusterIP、NodePort、LoadBalancer
第18周检验标准
- K8s各组件的作用
- Pod是什么,为什么需要Pod
- Deployment和ReplicaSet的关系
- Service的几种类型
8.3 第19周:Kubernetes进阶
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | 调度 | 调度流程、亲和性、污点容忍 |
| Day3-4 | 网络 | 网络模型、CNI、Service实现 |
| Day5-7 | 存储 | PV/PVC、StorageClass |
Pod调度流程
1. 用户创建Pod
2. API Server写入etcd
3. Scheduler监听到新Pod
4. 预选(Predicates):过滤不满足条件的Node
5. 优选(Priorities):对剩余Node打分
6. 选择得分最高的Node
7. 绑定Pod到Node
8. Kubelet创建Pod
Service实现原理
iptables模式:
Client → ClusterIP → iptables规则 → Pod IP
IPVS模式:
Client → ClusterIP → IPVS规则 → Pod IP
IPVS优势:
- 更好的性能
- 更多负载均衡算法
- 连接状态同步
第19周检验标准
- Pod调度流程
- 亲和性和反亲和性的作用
- Service是如何实现负载均衡的
- PV和PVC的关系
8.4 第20周:K8s运维与面试
学习内容
| 天 | 主题 | 重点内容 |
|---|---|---|
| Day1-2 | 监控 | Prometheus + Grafana |
| Day3-4 | 日志 | ELK/Loki |
| Day5-7 | 面试题整理 | 高频问题 |
常见问题排查
Pod处于Pending:
- 资源不足
- 调度约束无法满足
- PVC未绑定
Pod处于CrashLoopBackOff:
- 应用启动失败
- 健康检查失败
- 配置错误
Service无法访问:
- Selector不匹配
- 端口配置错误
- 网络策略限制
第20周检验标准
- K8s常见问题排查思路
- Pod的生命周期
- 滚动更新的原理
- HPA的工作原理
阶段五总检验清单
Docker:
- 容器和虚拟机的本质区别
- Namespace和Cgroups的作用
- 镜像分层的好处
- Docker网络模式
Kubernetes:
- K8s架构,各组件作用
- Pod调度流程
- Service的实现原理
- 滚动更新是怎么实现的
- 如何实现Pod的水平扩缩容
九、学习资源汇总
9.1 书籍
必读:
| 书名 | 领域 | 说明 |
|---|---|---|
| 《labuladong的算法小抄》 | 算法 | 入门最佳 |
| 《MySQL技术内幕:InnoDB存储引擎》 | MySQL | MySQL圣经 |
| 《Redis设计与实现》 | Redis | Redis原理 |
| 《Go语言设计与实现》 | Go | 在线免费 |
选读:
| 书名 | 领域 | 说明 |
|---|---|---|
| 《深入理解计算机系统》 | 计算机基础 | CSAPP |
| 《高性能MySQL》 | MySQL | 进阶 |
| 《图解HTTP》 | 网络 | 入门 |
| 《数据密集型应用系统设计》 | 分布式 | DDIA |
| 《Kubernetes in Action》 | K8s | 入门 |
9.2 视频
| 资源 | 领域 | 平台 |
|---|---|---|
| 代码随想录 | 算法 | B站 |
| MySQL实战45讲 | MySQL | 极客时间 |
| 小林coding | 网络/OS | B站 |
| 湖科大教书匠 | 网络 | B站 |
| 幼麟实验室 | Go | B站 |
9.3 刷题
| 平台 | 说明 |
|---|---|
| LeetCode | Hot 100必刷 |
| 剑指Offer | 国内高频 |
| 牛客网 | 面经、真题 |
9.4 在线资源
| 资源 | 地址 |
|---|---|
| Go语言设计与实现 | https://draveness.me/golang |
| 小林coding | https://xiaolincoding.com |
| CS-Notes | https://github.com/CyC2018/CS-Notes |
十、面试高频问题清单
MySQL
- B树和B+树的区别?为什么MySQL用B+树?
- 聚簇索引和非聚簇索引的区别?
- 什么是回表?如何避免?
- MVCC是什么?如何实现的?
- InnoDB和MyISAM的区别?
- 事务隔离级别有哪些?分别解决什么问题?
- 什么情况下索引会失效?
- 间隙锁的作用?
- 死锁如何产生?如何解决?
- explain执行计划各字段含义?
Redis
- Redis为什么这么快?
- ZSet底层为什么用跳表不用红黑树?
- 缓存穿透/击穿/雪崩的区别和解决方案?
- Redis持久化方式及区别?
- Redis集群的槽位是怎么分配的?
- Redis的过期策略和淘汰策略?
- 分布式锁如何实现?Redlock的争议?
- Redis事务和Lua脚本的区别?
网络
- TCP三次握手/四次挥手?
- 为什么要TIME_WAIT?
- TCP如何保证可靠传输?
- TCP和UDP的区别?
- HTTPS握手过程?
- HTTP/1.1和HTTP/2的区别?
- epoll和select的区别?
- 什么是粘包?如何解决?
操作系统
- 进程和线程的区别?
- 堆和栈的区别?
- 死锁的产生条件?如何预防?
- 虚拟内存的作用?
- 页面置换算法有哪些?
- 进程间通信方式?
- 用户态和内核态的区别?
Go
- GMP调度模型?
- GC是怎么工作的?STW是什么?
- 逃逸分析是什么?什么情况会逃逸?
- Channel的底层实现?
- Map的底层实现?并发安全吗?
- Mutex的实现原理?两种模式?
- defer的执行顺序?
- slice和数组的区别?扩容机制?
Kafka
- Kafka为什么吞吐量这么高?
- 如何保证消息不丢失?
- 如何保证消息顺序?
- Consumer Group和Rebalance?
- Kafka和RabbitMQ的区别?
分布式
- CAP理论?
- Raft协议的Leader选举?
- 2PC的两个阶段?
- 分布式ID生成方案?
- 如何实现分布式锁?
- 一致性哈希是什么?
Docker/K8s
- 容器和虚拟机的区别?
- Docker如何实现隔离?
- K8s各组件的作用?
- Pod调度流程?
- Service是如何实现的?
- 滚动更新的原理?
十一、每日执行模板
## 今日学习计划 - Week X Day Y
### 🎯 今日目标
- [ ] 算法:完成xx题
- [ ] 理论:学习xx知识点
- [ ] 输出:写xx笔记
### ⏰ 时间安排
- 07:00-08:00 算法1题
- 12:30-13:00 复习昨天内容
- 20:00-21:30 理论学习
- 22:00-22:15 整理笔记
### 📝 学习记录
(记录今天学到的关键点)
### ❓ 遗留问题
(记录没搞懂的问题,明天解决)
### ✅ 完成情况
- [ ] 算法
- [ ] 理论
- [ ] 输出
### 📊 本周进度
- 算法题:x/7
- 理论章节:x/x
- 笔记输出:x篇
十二、关键建议
12.1 学习原则
算法是硬门槛
- 每天至少1题,雷打不动
- 不会就看题解,但要自己再写一遍
- 一周后再做一遍(间隔重复)
理解 > 背诵
- 八股文要背,但更要理解
- 面试官一追问就露馅的是死记硬背
- 多问自己"为什么这样设计"
输出倒逼输入
- 每周写1-2篇笔记
- 能给别人讲清楚才是真懂
保持节奏,不要中断
- 每天2-3小时,坚持比冲刺重要
- 一周休息1天,避免burnout
- 状态不好就少学点,但不要完全停
定期检验
- 每周末用检验清单测试自己
- 模拟面试,找人互相问
- 发现短板立即补
12.2 时间分配建议
| 阶段 | 算法占比 | 理论占比 |
|---|---|---|
| 阶段一 | 60% | 40% |
| 阶段二 | 40% | 60% |
| 阶段三 | 30% | 70% |
| 阶段四 | 20% | 80% |
| 阶段五 | 10% | 90% |
12.3 心态调整
- 不要追求完美,先完成再完善
- 遇到难题很正常,跳过继续前进
- 学习是马拉松,不是百米冲刺
- 你的应用层经验是优势,底层原理补上后会非常有竞争力
祝学习顺利,面试成功!🚀
