# 字节后端面试模拟（萌芽小店情景 · 套八股式追问）

> **怎么用这份材料**：面试官通常**不会对着你的仓库逐行看**，而是听你说「我做了一个小店后端」之后，用**通用八股 + 业务情景**连续追问。下面 10 题按这种话术写：**先像真面试里的问法**，回答里把**原理（八股）**和**你会怎么落地到项目**揉在一起。  
> **比重**：**Go、MySQL 为主**；**Redis、RabbitMQ 各挑典型一问为辅**（与当前仓库里可选组件对齐，不必假定对方读过源码）。

---

## 项目一句话（自我介绍用）

用 Go 写了小店后端：Gin 提供 API，MySQL + GORM 存商品订单，结账有支付待确认状态，可选 MQ 异步发订单邮件、可选 Redis；认证打外部账户中心。**细节不用背文件路径，按模块说即可。**

---

## 10 道面试题（主 Go / MySQL）

### 1. 情景：下单要把「查库存、写订单」绑在一起 —— 为什么上事务？边界怎么收？

**问法**：你说下单用数据库事务包了一段逻辑。那**事务里放哪些、不放哪些**？如果要把事务缩到最短，你会拆成几步？和以前不用事务只扣库存相比，要解决什么并发问题？

**回答要点**：保证 **ACID 里的一致性**：读到的库存和写入的订单一致，避免并发下 **lost update / 超卖**。事务外适合做参数校验、调外部 HTTP；事务内只做 **行锁下的读写**。缩短边界可降低锁持有时间、减死锁概率；但拆太散会失去原子性，要用 **TCC、本地消息表、幂等单号** 等补偿（视业务接受度）。InnoDB 里常用 **`SELECT … FOR UPDATE` 当前读**锁住商品行再扣减。

---

### 2. 情景：`SELECT … FOR UPDATE` 锁商品 —— 和乐观锁、直接 `UPDATE qty = qty - ?` 有啥区别？

**问法**：库存扣减你说是行锁读的，那**悲观锁和乐观锁**各适用什么场景？`UPDATE … WHERE qty >= ?` 用影响行数判断成败，和你先 `FOR UPDATE` 再改，**语义和性能**上差在哪？

**回答要点**：悲观锁（`FOR UPDATE`）适合**冲突概率高**、逻辑分支多的扣减；乐观锁用 **version 列** 或 CAS 式 `UPDATE`，适合读多写少、重试用业务可接受。**纯 UPDATE 条件扣减**一条 SQL 完成，锁时间短，但复杂流程（预留卡密、多表）仍常要事务配合。RR 下 `FOR UPDATE` 是当前读，和**普通快照读**不同，别混用。

---

### 3. 情景：同一商品只允许一笔「待支付」，不然支付回调不好认 —— 库里怎么表达？和隔离级别有关吗？

**问法**：如果业务上**同一 SKU 同时只能有一单 pending**，你除了应用层判断，**数据库层**还能怎么兜底？MySQL 默认隔离级别下，只靠「先查有没有 pending 再插」会有什么问题？

**回答要点**：应用层在事务里 **FOR UPDATE 商品行 + 查 pending**，串行化冲突。DB 层可 **唯一索引**（如 `(product_id, status)` 对 `pending` 状态——MySQL 里部分场景用**生成列 / 约束**不好表达，更常见是 **部分唯一索引** 8.0+ 或状态机 + 应用保证）。只靠快照读「先查后插」可能 **幻读 / 竞态**，要用当前读或约束。追问隔离级别：默认 **RR + 当前读/锁** 解决多数此类问题。

---

### 4. 情景：支付超时要把预留库存还回去 —— 除了定时扫表还有什么？索引注意啥？

**问法**：待支付订单过期要释放库存，你用定时任务扫「过期 pending」。**拖数据库**怎么优化？延时队列、Redis TTL 通知这类方案你怎么权衡？

**回答要点**：扫表要有 **`status + payment_expires_at`** 等组合索引，避免全表扫。批量处理、限流、**幂等释放**（`UPDATE … WHERE status='pending' AND expires_at < now()` 行数判断）。延时队列 / MQ 延迟消息：**精度与运维成本**更高；Redis keyspace 通知不靠谱作唯一依据。核心是 **DB 仍为权威状态**，定时扫是简单稳妥的基线。

---

### 5. 情景：Go 里起了后台 goroutine（定时任务、MQ 消费）—— 进程要停时你怎么收尾？

**问法**：`main` 外面 `go` 了长时间运行的循环，收到 **SIGTERM** 怎么办？`context.Cancel` 先取消谁？和 `server.Shutdown` 顺序？

**回答要点**：**根 Context** 可取消，`select` 里监听 `<-ctx.Done()` **退出循环**。优雅关停：先 **HTTP Shutdown** 停新流量，再 **cancel 后台任务**，MQ **consumer ack 完再关 channel/connection**；`defer` 释放要防 **Close 后再用**。生产用 **errgroup** 或包装 runnable 统一等待。

---

### 6. 情景：HTTP Handler 里调外部认证、写数据库 —— 超时、错误怎么传？

**问法**：用户请求进来，你要调外部验 Token、再写库。`context` 是传 **`c.Request.Context()`** 还是自己 `WithTimeout`？超时到了 SQL 还会不会执行？

**回答要点**：IO 应带 **可取消的 Context**；HTTP 入口常用 **`c.Request.Context()`** 与用户断开联动，或包一层 **更短的业务超时**。GORM/MySQL driver 若支持 context，**取消后会尝试中断**；未挂 context 的请求无法被动取消。错误：**包装 `fmt.Errorf("%w")`** 保留链；对外**脱敏**，日志里打 `request_id`。

---

### 7. 情景：`database/sql` 连接池 —— 几个参数和 MySQL `wait_timeout` 关系？

**问法**：`SetMaxOpenConns` / `SetMaxIdleConns` / `SetConnMaxLifetime` 各自干啥？`ConnMaxLifetime` 设得比服务端 **`wait_timeout` 长会怎样**？

**回答要点**：**MaxOpen** 限并发连接数保护 DB；**MaxIdle** 平衡复用与内存；**ConnMaxLifetime** 让连接定期重建，避免被 **MySQL 或中间层** 断开后的坏连接。经验上 **Lifetime 略小于 `wait_timeout`**（或云库推荐的 idle 时间），减少 `packets.go broken pipe` 类问题。**MaxIdle=0** 会导致高频建连。

---

### 8. 情景：ORM + 迁移 —— 你怎么看待 `AutoMigrate` 上生产？

**问法**：项目启动时自动 Migrate，大厂里也这么干吗？**危险点**在哪？你更信哪种流程？

**回答要点**：`AutoMigrate` 适合**开发/小项目**，生产常见 **显式迁移**（goose / golang-migrate / Atlas），要 **review、灰度、可回滚**。风险：**不能与复杂 DDL（大表改列、索引）等价**；线上锁表、双写切换需要 **人工方案**。面试答「小型服务可 AutoMigrate，有 SLA 就上迁移工具 + CI」。

---

### 9. 【Redis · 辅】情景：浏览量或去重防刷 —— 单机内存和 Redis 差在哪？

**问法**：你说用过进程内 map 做访问去重，**多实例部署**会怎样？用 Redis 的话 **SET NX + EX**、**HyperLogLog** 分别在什么诉求下选？

**回答要点**：进程内 map **不跨实例**，计数会**偏高**或去重失效。Redis：**SET key NX EX TTL** 做窗口内去重；**INCR** 做简单计数；**HLL** 只要大概 UV、省内存。**热 key** 要本地缓存、拆分 key 或读写分离；**持久化**与**缓存 DB 一致性**按业务容忍度设计（能接受最终一致即可）。

---

### 10. 【RabbitMQ · 辅】情景：订单邮件发 MQ —— 还会丢消息吗？你怎么补？

**问法**：交换机、队列、durable、消息 persistent 都配了，**哪三段**仍可能丢？`at-least-once` 业务要做什么？

**回答要点**：经典三段：**Producer→Broker**（未 confirm、网络闪断）、**Broker**（未刷盘、集群未完成同步——视配置）、**Consumer**（处理完未 ack 或先 ack 后崩）。**Publisher Confirm + 消费者手动 ack** 提高可靠性；语义多为 **至少一次**，消费端 **幂等**（订单号去重、Unique 约束）。**重连重发**可能 duplicate，要和幂等一起看。

---

## 题量分布（自检）

| 方向 | 题号 |
|------|------|
| Go（runtime、context、goroutine、HTTP、错误） | 5、6 |
| MySQL（事务、锁、隔离、索引、迁移、连接池） | 1、2、3、4、7、8 |
| Redis（为辅） | 9 |
| RabbitMQ（为辅） | 10 |

---

## 附：超短技术栈备忘（背 30 秒版）

Go 1.21 + Gin + GORM；MySQL；可选 RabbitMQ（异步邮件）、可选 Redis（健康/扩展）；外部 HTTP 验 Token。下单事务 + 行锁；待支付超时回收；支付回调匹配订单；MQ 失败可降级发信。
