一、数据库核心考点
1. MySQL 事务(ACID + 项目落地)
- ACID 特性:原子性、一致性、隔离性、持久性,是数据一致性的核心保障。
- 项目落地 :
- 原子性:科研项目协作平台中"用户角色变更 + 权限同步",事务确保两者全成/全回滚,避免数据不一致;
- 一致性:校园社交平台"点赞操作",事务内同步"新增点赞记录 + 更新帖子点赞数",保证计数与记录匹配;
- 隔离性:默认 InnoDB 可重复读(RR)隔离级别,避免脏读/不可重复读,高并发写场景用行锁保证隔离;
- 持久性:依赖 redo log,事务提交后数据不丢失,数据库崩溃可恢复。
- 优化:缩小事务范围,将非数据库操作(如 HTTP 请求)移出事务,减少锁占用时间。
2. MySQL 长读取 + 写入 + MVCC 机制
- 长读取遇写入:RR 隔离级别下,读取生成一致性快照(Read View),写入创建数据新版本并记录 undo log,读写互不阻塞,读取仍返回快照旧版本;
- 快照本质:仅记录活跃事务 ID/最大事务 ID,不拷贝全表数据,通过 undo log 链关联多版本数据;
- undo log 回放:仅当数据当前版本不可见时回溯,同一事务内重复读取缓存结果,无需重复回放。
二、Go 语言核心考点
1. GMP 调度模型
- 核心组件 :
- G(Goroutine):用户态轻量级线程,初始栈 2KB,支持动态扩缩;
- M(Machine):绑定内核线程,同一时间仅执行一个 G;
- P(Processor):逻辑处理器,维护本地 G 队列,数量默认等于 CPU 核心数(GOMAXPROCS)。
- 调度逻辑 :
- P 管理本地 G 队列,M 从 P 取 G 执行;
- G 阻塞时,M 释放 P,P 绑定其他 M 继续执行;
- 本地队列为空时,P 从全局队列/其他 P 偷取 G,实现负载均衡。
2. Go 垃圾回收(GC)
- 核心流程(Go 1.19+) :
- 标记准备(STW):暂停 G,开启写屏障,扫描根对象(微秒级);
- 并发标记:恢复 G,后台线程标记存活对象,写屏障记录引用变更;
- 标记终止(STW):处理剩余引用(微秒级);
- 并发清除:回收未标记对象,归还内存;
- 可选并发整理:优化内存碎片。
- 核心优化:三色标记法、混合写屏障、分代回收(Go 1.20+),STW 时间微秒级,低延迟适配高并发。
3. Go 程序 Linux 执行的系统调用
| 阶段 | 核心系统调用 |
|---|---|
| 启动阶段 | execve(加载二进制)、mmap(内存映射)、brk/sbrk(堆初始化)、open(标准流) |
| 运行阶段 | socket/bind/listen/accept(网络)、open/read/write(文件)、clone(创建 M)、sigaction(信号) |
| 退出阶段 | exit_group(终止进程) |
- 延伸:网络操作底层封装 epoll(epoll_create/ctl/wait)实现 IO 多路复用。
4. Go 内存泄露排查
- 遇过场景:校园社交平台协程因 Chan 未关闭阻塞,内存占用持续上升。
- 排查流程 :
- 定位:
go tool pprof分析堆内存/协程数,发现 goroutine 指标异常; - 分析:
go tool pprof goroutine?debug=2查看阻塞协程栈,定位未关闭 Chan; - 验证:测试环境关闭 Chan 后协程正常退出,内存泄露解决;
- 监控:Prometheus+Grafana 监控协程数/内存,设置告警阈值。
- 定位:
- 常见场景:sync.Pool 误用、全局 map 未清理、time.Ticker 未停止。
三、中间件核心考点
Redis 高性能原因
- 纯内存存储:读写无磁盘 IO,响应微秒级;
- 单线程模型:避免线程切换/锁竞争,简化并发;
- 高效数据结构:SDS(字符串)、字典(哈希)、跳表(ZSet),操作效率高;
- IO 多路复用:epoll/kqueue 管理多连接,高并发处理;
- 其他优化:批量操作(MSET/MGET)、Pipeline 减少网络往返。
四、网络核心考点
1. TCP 三次握手异常(客户端未发最后 ACK)
- 过程:服务器进入 SYN_RCVD 状态,重发 SYN+ACK(默认 5 次,指数退避),超时(约 30 秒)后释放连接;
- 影响:客户端连接失败,服务器大量异常会触发 SYN Flood,可通过 SYN Cookie 防御。
2. CDN 原理与访问流程
- 核心作用:静态资源缓存到边缘节点,降低延迟、减轻源服务器压力;
- 访问边缘服务器流程 :
- DNS 解析:智能调度 DNS 按用户 IP/运营商/节点负载,返回最优边缘节点 IP;
- 资源访问:边缘节点有缓存则直接返回,无缓存则拉取源服务器资源并缓存。
- 调度策略:地理就近、负载均衡、运营商匹配。
3. IO 模型与多路复用
| IO 方式 | 特点 |
|---|---|
| 阻塞 IO | 等待 IO 完成,简单但效率低 |
| 非阻塞 IO | 轮询判断 IO 状态,CPU 开销大 |
| 信号驱动 IO | IO 完成后系统发信号,减少轮询 |
| IO 多路复用 | 单进程管理多 IO 通道,epoll 性能最优 |
| 异步 IO | 完全无阻塞,IO 完成后通知进程 |
- IO 多路复用底层 :
- select:FD 数量限制(1024),轮询效率低;
- poll:无 FD 限制,仍轮询;
- epoll:事件驱动,红黑树存储 FD,LT/ET 触发,高并发最优。
- Go 实现:netpoller 跨平台适配(epoll/kqueue/IOCP),与 GMP 整合,IO 阻塞时 G 释放 P,提升并发。
4. HTTP 协议演进
(1)HTTP vs HTTPS
| 维度 | HTTP | HTTPS |
|---|---|---|
| 传输 | 明文 | HTTP+TLS/SSL 加密 |
| 端口 | 80 | 443 |
| 安全性 | 无认证/完整性保障 | 加密、身份认证、完整性校验 |
| 握手 | TCP 三次握手 | TCP 三次握手 + TLS 四次握手 |
- HTTPS 安全保障 :
- 加密:非对称加密(握手)+ 对称加密(传输);
- 认证:CA 签发证书,验证服务器身份;
- 完整性:HMAC 校验,防止数据篡改。
- HTTPS 握手流程 :
- 客户端发 Client Hello(TLS 版本/加密套件/随机数);
- 服务器发 Server Hello(确认参数)+ 证书;
- 客户端验证证书,加密预主密钥发送给服务器;
- 双方生成会话密钥,Finished 消息确认握手;
- 数据传输:会话密钥对称加密 HTTP 数据。
(2)HTTP/1.1 vs HTTP/2
| 优化点 | HTTP/1.1 | HTTP/2 |
|---|---|---|
| 并发机制 | 串行传输,队头阻塞 | 帧多路复用,并行传输 |
| 头部传输 | 重复传输,开销大 | HPACK 压缩,静态+动态字典 |
| 传输格式 | 文本,解析易出错 | 二进制帧,解析高效 |
| 资源获取 | 客户端主动请求 | 服务器推送关联资源 |
| 流量控制 | 无 | 基于流的精细化控制 |
- 实战价值:HTTP/2 使多静态资源页面加载时间降低 40%+,如文档列表页从 1.2s 降至 0.7s。
(3)HTTP/2 vs HTTP/3
| 维度 | HTTP/2 | HTTP/3 |
|---|---|---|
| 传输层 | TCP | UDP(QUIC) |
| 队头阻塞 | 连接级阻塞 | 流级隔离,无阻塞 |
| 握手延迟 | 7RTT(TCP+TLS) | 1-2RTT(QUIC 合并 TLS) |
| 连接迁移 | 绑定 IP+端口 | 基于连接 ID,支持网络切换 |
- HTTP/3 可靠性保障(QUIC) :
- 可靠交付:序列号+ACK 重传、滑动窗口流量控制;
- 有序交付:独立流机制,流内有序、流间隔离;
- 拥塞控制:CUBIC/BBR 算法,动态调整速率;
- 其他:全量加密、零 RTT 握手、连接迁移。
5. 进程通信方式(IPC)
| 方式 | 核心特点 | 适用场景 |
|---|---|---|
| 管道(Pipe) | 半双工,父子进程通信 | 本地简单数据传递 |
| 命名管道 | 无亲缘关系进程通信 | 本地低吞吐场景 |
| 消息队列 | 按类型排序,非阻塞 | 分布式系统数据传递 |
| 共享内存 | 最快 IPC,需信号量同步 | 高吞吐数据共享 |
| 信号量 | 同步互斥,非数据传递 | 共享资源访问控制 |
| 信号 | 异步通知 | 进程终止/异常处理 |
| Socket | 跨主机通信 | 网络分布式系统(C/S) |
6. 多进程程序必要性
- 隔离性:多租户系统独立进程,避免单点故障影响全局;
- 多核利用:进程绑定 CPU 核心,减少资源竞争;
- 容错性:核心业务拆分进程,单个崩溃不影响整体,支持自动重启。
五、算法核心考点
1. 二叉树的右视图(BFS)
go
package main
import (
"container/list"
"fmt"
)
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func rightSideView(root *TreeNode) []int {
var res []int
if root == nil {
return res
}
queue := list.New()
queue.PushBack(root)
for queue.Len() > 0 {
levelSize := queue.Len()
for i := 0; i < levelSize; i++ {
node := queue.Remove(queue.Front()).(*TreeNode)
if i == levelSize-1 {
res = append(res, node.Val)
}
if node.Left != nil {
queue.PushBack(node.Left)
}
if node.Right != nil {
queue.PushBack(node.Right)
}
}
}
return res
}
- 复杂度:时间 O(n),空间 O(n);
- 边界:空树返回空,左子树存在时取最右侧节点。
2. 两数之和(哈希表 + 延伸)
基础实现(O(n))
go
func twoSum(nums []int, target int) []int {
numMap := make(map[int]int)
for i, num := range nums {
if idx, ok := numMap[target-num]; ok {
return []int{idx, i}
}
numMap[num] = i
}
return nil
}
延伸追问
| 问题 | 核心答案 |
|---|---|
| 为什么用哈希表 | 空间换时间,O(n) 对比暴力 O(n²),效率提升显著 |
| 千万级数据哈希表装得下吗 | 1000 万数据约 160MB,普通服务器可容纳;亿级需 1.6GB,内存紧张则需优化 |
| 内存放不下怎么办 | 分块落盘:拆分为 100 万/块,逐块用哈希表匹配,跨块记录索引 |
| 更高效方案 | 排序+双指针(O(nlogn),空间 O(1)),无需原索引时最优 |
| 多组解处理 | 哈希表存"值→索引列表",遍历收集所有匹配项 |
| O(n) 优化空间 | 预分配哈希表容量、早停、重复值跳过 |
3. 子数组和为 K(前缀和 + 哈希表)
go
func subarraySum(nums []int, k int) bool {
prefixMap := make(map[int]bool)
prefixSum := 0
prefixMap[0] = true
for _, num := range nums {
prefixSum += num
if prefixMap[prefixSum-k] {
return true
}
prefixMap[prefixSum] = true
}
return false
}
- 复杂度:时间 O(n),空间 O(n);
- 边界:处理子数组从索引 0 开始的情况(初始化 prefixMap[0]=true)。