Daily Study
更新: 12/27/2025 字数: 0 字 时长: 0 分钟
Daily Plan
#todo
- [ ]
八股回顾
一些问题
Go 语言基础:
- GMP 模型调度原理(尤其是 P 和 M 的关系,Goroutine 泄漏怎么排查)。
- Channel 的底层实现,有缓冲和无缓冲的区别。
- Context 的使用场景(超时控制、链路追踪)。
数据库与中间件:
- MySQL 索引失效场景、B+树结构、事务隔离级别(MVCC 原理)。
- Redis 跳表(SkipList)、持久化(RDB/AOF)、分布式锁实现(Redlock)。
- Kafka 消息积压怎么处理?如何保证顺序消费?
网络与系统:
- TCP 三次握手/四次挥手状态机(TIME_WAIT 状态过多的原因及危害)。
- HTTP/2 和 HTTP/3 (QUIC) 的区别(因为 PCG 涉及视频业务,QUIC 很重要)。
- Linux 内核态/用户态的问题,eBPF 原理
介绍一下go中的GMP模型
GMP首先代表三个核心结构:
- G:goroutine协程。(协程和线程的区别goroutine 和线程的区别 | Go 程序员面试笔试宝典)
- M:系统/内核线程,负责CPU的调度
- P:处理器,包含了G所需的资源和上下文(主要是一个本地运行队列)
核心调度策略:
- 两个队列:
- 本地队列:每一个P都有一个队列,存放待运行的G。M 优先从绑定的 P 的本地队列获取 G,无锁(或极少锁)速度极快。
- 全局队列:存放所有 P 都能获取的 G。当 P 的本地队列满时,多出来的 G 会放到全局队列(需要加锁)
- 工作窃取:当一个 P 的本地队列空了,且全局队列也空了。M 会尝试从其他 P 的本地队列中偷走一半的 G 到自己的队列中执行。充分利用多核 CPU,避免某些线程忙死,某些线程闲死。
追问1:为什么要有 P?直接 M 和 G 对应不行吗?
- 全局锁竞争(Global Lock Contention):所有 M 都去同一个全局队列取 G,锁竞争极其严重,限制了并发扩展。
- M 的内存缓存差:M 频繁切换 G,导致 CPU Cache 命中率低。
- 线程创建/销毁开销:没有 P 作为缓冲,M 容易频繁创建和销毁。 P 的引入带来了本地队列(减少锁)和 Work Stealing(负载均衡),解决了这些问题。
追问2:Go 的抢占式调度是怎么实现的? 基于信号的抢占:引入了 sysmon(系统监控线程),它独立于 GMP 之外。sysmon 会监控运行时间过长的 G(超过 10ms)。 sysmon 向该 M 发送 SIGURG 信号。M 收到信号后,中断当前的 G,将其放回全局队列,从而调度下一个 G。
