Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 192 additions & 0 deletions content/docs/interview/zhongchang/CC-Sangfor-First-Interview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
title: "CC-深信服一面"
weight: 100
---

# 深信服一面

> 方向:Go 后端 / 短视频后端项目 / 计算机基础 / 场景题。
>
> 本文记录面试真题,并整理了复盘要点。参考答案应结合自己的技术栈和真实经历调整,避免照搬或虚构项目、实习经历。

## 一、项目经历:短视频后端系统

### 1. 介绍一下这个项目:为什么要做?中间遇到了哪些困难?如何解决?

答:这是一个短视频后端服务,主要实现用户、视频上传与发布、视频流、点赞、评论和关注等能力。做这个项目是为了把 Go Web 开发、数据库设计、对象存储、并发控制和容器化部署串成一次完整的工程实践;短视频场景不仅有常规 CRUD,还涉及大文件上传、异步处理和高并发写入。

项目中的主要难点和处理方式:

- **大文件上传**:不把视频二进制直接存入 MySQL。客户端上传至对象存储,数据库只记录对象 key、封面地址、时长、大小和格式等元数据,降低数据库容量与 I/O 压力。
- **发布链路一致性**:将上传和发布拆开。发布接口只校验文件、创建视频元数据与发布任务;转码、封面生成、审核和通知等耗时操作异步执行。视频状态按 `draft -> processing -> published/failed` 流转。
- **重复发布与并发写入**:为每次发布加入 `request_id`,并在数据库建立 `author_id + request_id` 唯一约束;重复请求直接返回已有结果。事务只包住必要的元数据和状态更新,避免长事务。
- **查询性能**:对视频流、评论和关系表的高频查询字段建立联合索引,例如 `videos(author_id, published_at)`、`comments(video_id, created_at)`。

### 2. 底层框架是纯手写的,还是 AI 辅助搭建的?

答:架构设计和核心实现由自己完成,包括模块划分、接口设计、数据库表结构、认证流程、发布状态流转与并发控制。开发时会使用 AI 辅助完成部分脚手架、查询库的用法、补充测试思路或检查边界条件,但不会直接不加理解地复制。AI 生成的代码会结合项目结构改写、运行测试并自己定位问题。

面试时应如实回答使用范围。重点是能解释核心设计:为什么文件放对象存储、为什么发布要幂等、为什么耗时任务要异步化,以及异常时如何恢复。

### 3. 视频发布功能中的并发问题,能详细讲讲吗?

答:视频发布不能把“上传文件”和“写发布记录”混在一个长请求、长事务中。通常先让客户端通过预签名 URL 或上传凭据直传对象存储;上传成功后,客户端再调用发布接口。

发布接口的并发风险主要是用户重复点击、网络超时重试和客户端重复投递,导致同一视频生成多条记录。处理过程如下:

1. 客户端为一次发布生成 `request_id`,服务端也可生成发布任务 ID。
2. 服务端校验登录状态、文件对象和参数,创建 `videos`、`publish_tasks` 等必要记录。
3. 对 `author_id + request_id` 建唯一索引;发生重复请求时查询并返回第一次创建的任务或视频,而不是重复插入。
4. 数据库事务只保护元数据、状态和必要关系,不执行转码、截图、审核等耗时工作。
5. 后续任务通过消息队列异步处理;消费者完成后用条件更新推进状态,失败则记录错误并允许重试。

这样不需要使用全局锁。全局锁会让不同用户互相阻塞,而唯一约束与幂等键只会约束同一发布请求,吞吐量更高。

### 4. 多用户高并发发布视频,后端只有一个数据库,如何缓解入库压力?

答:核心是使用消息队列削峰填谷,而不是把所有发布请求同步写入单个数据库。

```text
客户端直传对象存储
发布服务校验并投递消息
消息队列暂存峰值流量
消费者按受控并发度写入 MySQL
更新发布状态,客户端轮询或订阅结果
```

- 接口成功投递消息后可返回“处理中”,避免请求一直等待数据库写入完成。
- 消费者数量和批量大小按数据库承受能力设置,防止连接池耗尽、磁盘 I/O 饱和或锁竞争恶化。
- 消息应持久化;消费者成功写库后再确认,失败时重试,多次失败转入死信队列。
- 消息可能重复投递或重复消费,因此数据库仍需使用 `request_id` 唯一约束保证幂等。
- 如果业务要求“写库成功”和“发消息成功”严格一致,可使用事务 Outbox:同一事务内写业务数据和 `outbox_event`,后台任务再可靠投递 MQ。

### 5. 数据库表如何设计?包含哪些表?

核心表可以按领域划分:

| 表 | 关键字段 | 作用 |
| --- | --- | --- |
| `users` | `id`、`username`、`password_hash`、`avatar_url` | 用户账户和资料 |
| `videos` | `id`、`author_id`、`title`、`description`、`status`、`cover_url`、`published_at` | 视频核心元数据和发布状态 |
| `video_assets` | `id`、`video_id`、`object_key`、`file_size`、`duration`、`format` | 视频文件、封面和转码产物信息 |
| `publish_tasks` | `id`、`author_id`、`request_id`、`video_id`、`status`、`error_message` | 发布幂等、异步处理和失败追踪 |
| `user_video_likes` | `user_id`、`video_id`、`created_at` | 用户点赞关系 |
| `comments` | `id`、`video_id`、`user_id`、`parent_id`、`content` | 视频评论和回复 |
| `user_follows` | `follower_id`、`followee_id`、`created_at` | 用户关注关系 |
| `favorites` | `id`、`user_id`、`name` | 用户收藏夹 |
| `favorite_videos` | `favorite_id`、`video_id` | 收藏夹和视频的关联 |
| `categories`、`video_categories` | 分类 ID、视频 ID | 视频分类及多对多关联 |

## 二、数据库表之间如何关联?

答:每个实体表通常使用自增 `BIGINT` 或雪花 ID 作为主键,例如 `users.id`、`videos.id`。关联关系按业务建模:

- **一对多**:一个用户可以发布多个视频,`videos.author_id` 关联 `users.id`;一个视频有多条评论,`comments.video_id` 关联 `videos.id`。
- **一对一或一对多资源**:`video_assets.video_id` 关联 `videos.id`。若一条视频只有一个主文件可做一对一;若包含原视频、封面和多种清晰度转码文件,则是一对多。
- **多对多**:用户和视频的点赞关系通过 `user_video_likes` 中间表实现;视频和分类通过 `video_categories` 实现。中间表通常用 `(user_id, video_id)` 或 `(video_id, category_id)` 作为联合主键或联合唯一索引。
- **自关联**:`user_follows` 的 `follower_id` 和 `followee_id` 都关联 `users.id`,表示关注者与被关注者。
- **评论回复**:`comments.parent_id` 指向同表的 `comments.id`,根评论的 `parent_id` 为 `NULL` 或 `0`。

在 MySQL 中,外键能保证引用完整性,例如避免为不存在的用户创建视频。实际高并发互联网业务有时不在数据库层显式建立外键,而是只建立普通索引并在应用层校验,以减少级联操作和跨表写入的限制;无论是否建立物理外键,逻辑上的主外键关系、唯一约束和索引都必须清晰。

## 三、Docker 主要用来做什么?

答:Docker 用于将服务及其运行环境一起打包,解决“本地能运行、服务器不能运行”的环境差异问题。

- 使用 `Dockerfile` 构建 Go 二进制和运行镜像,固定 Go 版本、依赖和启动命令。
- 使用 Docker Compose 一键拉起 API、MySQL、Redis、对象存储或消息队列,便于本地开发、联调和测试。
- 通过环境变量或挂载配置文件注入数据库地址、密钥、日志级别等配置,不把敏感信息写死在镜像内。
- 容器化后可以统一发布、回滚和扩容;生产上可配合 Kubernetes 做健康检查和滚动更新。

## 四、计算机专业基础知识

### 1. Go 和 C 的相似与差别

相似点:二者都偏系统编程,支持指针、结构体、函数和编译为本地机器码,性能与资源控制能力较强。

差异:

- C 更接近硬件和操作系统,需要手动管理内存,指针操作自由但更容易出现越界、悬垂指针和内存泄漏。
- Go 具有垃圾回收、切片、map、接口和反射等运行时能力,开发效率和安全性更高,但运行时和 GC 会带来一定开销。
- C 的并发通常依赖 pthread、锁和条件变量;Go 原生提供 Goroutine、Channel 和 `select`,并通过 GMP 调度模型实现轻量并发。
- C 头文件、预处理器和链接过程更直接;Go 使用 package、模块系统和统一的格式化、测试工具链。

### 2. Go 是否区分全局变量与局部变量?如何创建 Goroutine?

答:有区分。包级变量定义在函数外,生命周期通常覆盖整个程序运行期,可被同包代码访问,首字母大写时可被其他包导出;局部变量定义在函数、代码块或参数列表内,只在对应作用域内有效。局部变量是否分配到栈上由编译器逃逸分析决定,不能简单认为“局部变量一定在栈上”。

创建 Goroutine 使用 `go` 关键字:

```go
go func() {
// 并发执行的任务
}()
```

Goroutine 间可通过 Channel 通信,也可用 `sync.WaitGroup` 等待任务结束;共享数据时仍要注意互斥锁、原子操作和竞态条件。

### 3. 了解 Python、Linux 命令、Bash 或 Python 脚本吗?

答:了解 Python,常用于数据处理、自动化和调用 HTTP 接口;也写过 Bash/Python 脚本做日志处理、批量文件操作、接口巡检或开发环境初始化。常用 Linux 命令包括:

- 文件与文本:`ls`、`cd`、`cp`、`mv`、`find`、`grep`、`sed`、`awk`、`tail -f`;
- 进程与资源:`ps`、`top`、`htop`、`kill`、`free`、`df -h`、`du -sh`;
- 网络与排查:`curl`、`wget`、`ss -lntp`、`ping`、`traceroute`、`nslookup`;
- 权限与服务:`chmod`、`chown`、`systemctl`、`journalctl`。

面试时最好补充一个真实脚本例子,例如“用 Python 定时读取日志并统计错误码,超过阈值通过企业通知机器人告警”。

### 4. 多线程、多进程、协程的区别

答:进程是资源分配的基本单位,拥有独立地址空间;进程间隔离强,但创建和通信成本较高。线程是 CPU 调度的基本单位,同一进程的线程共享地址空间,创建切换较轻,但共享内存需要锁保护。协程是用户态的轻量执行单元,由语言运行时调度,创建成本低,适合大量 I/O 并发;遇到阻塞 I/O 时,运行时可以切换到其他协程。

在 Go 中,Goroutine 不是直接等于 OS 线程,而是由 GMP 调度器把大量 G 调度到少量 M(系统线程)上运行;P 提供运行 G 所需的上下文和本地队列。

### 5. HTTP、TCP、IP 与网络问题排查

答:IP 负责网络层寻址和路由,本身是无连接、尽力而为的协议;TCP 在 IP 之上提供面向连接、可靠、有序的字节流,通过三次握手建立连接、序号确认与重传保证可靠性、滑动窗口和拥塞控制调节发送速度;HTTP 是应用层协议,定义请求方法、头、状态码和消息语义,通常运行在 TCP 之上,HTTPS 则在 HTTP 与 TCP 之间加入 TLS。

排查接口问题时先分层:

1. 用 `curl -v` 查看 DNS、连接、TLS、请求头和响应头;
2. 关注状态码:`2xx` 成功、`301/302` 重定向、`400` 参数错误、`401` 未认证、`403` 无权限、`404` 路径不存在、`429` 限流、`5xx` 服务端异常;
3. 查看应用日志、反向代理日志和数据库/依赖服务耗时;
4. 用 `ss` 检查端口监听和连接状态,必要时用抓包工具确认 TCP 握手、重传或超时。

### 6. 如果实际组装一台电脑,会怎么做?

答:先明确用途和预算,例如办公、游戏、开发或视频剪辑,再围绕兼容性和性能瓶颈选型。

| 组成原理中的部件 | 实际硬件映射 | 选型关注点 |
| --- | --- | --- |
| 运算器、控制器 | CPU | 插槽、核心数、性能与功耗 |
| 主存储器 | 内存(RAM) | 主板代际、容量、频率与双通道 |
| 外存储器 | SSD / HDD | NVMe 或 SATA、容量与读写需求 |
| 输入/输出设备 | 键盘、鼠标、显示器、网卡、显卡等 | 接口与实际使用场景 |
| 总线与连接 | 主板、PCIe、SATA、USB 等 | CPU 插槽、内存插槽、扩展能力 |

独显、CPU、内存和主板要兼容;电源要预留足够功率并选择可靠品牌;机箱尺寸要容纳主板、显卡和散热器;最后装机、接线、进入 BIOS 检查硬件识别和温度,安装系统并做压力测试。

## 五、职场软技能与场景问题

### 1. 你的抗压能力怎么样?

答:我认为抗压不是单纯熬时间,而是在压力下保持信息透明、优先级清晰和稳定交付。遇到紧急任务时,我会先确认截止时间、影响范围和验收标准,拆分出最小可交付版本,优先解决阻塞项;对风险及时同步,不等到最后一刻才暴露问题。高强度阶段结束后也会复盘流程和技术债,减少同类压力反复出现。

### 2. 任务只给有限上下文和大方向,会如何推进?有类似实习经历吗?

答:我会先把模糊需求转成可验证的问题:目标用户是谁、要解决什么问题、输入输出是什么、优先级和验收标准是什么。然后阅读现有代码、文档、接口和监控数据,画出涉及的模块与依赖;在不确定点上主动向负责人确认,不会在关键假设上长期闭门开发。

接着将任务拆成调研、方案、最小实现、测试和发布观察几个阶段,先交付可运行的最小版本,再逐步完善。若有真实实习或项目案例,应按“背景—任务—行动—结果”说明;没有实习时不要虚构,可以用课程项目、开源贡献或团队项目举例。

### 3. 同时有多个任务,如何处理和排期?

答:先按紧急程度、业务影响、依赖关系和工作量排序。存在外部依赖或会阻塞他人的事项优先推进;重要但不紧急的任务拆到迭代计划中。每天明确一到两个最重要目标,预留处理线上问题和沟通的缓冲时间。若资源和时间确实冲突,会尽早说明各方案的交付风险,请负责人确认优先级,而不是默认所有任务都能按原计划完成。

### 4. 技术热情怎么样?是否确定未来做研发岗位?

答:我希望长期从事研发,尤其倾向后端与基础服务方向。吸引我的不仅是写出一个功能,更是通过设计、性能优化、稳定性建设和自动化让系统长期可靠运行。我会持续学习 Go、数据库、分布式系统和云原生相关知识,也愿意在实际业务里根据团队需要拓展其他技术栈。
20 changes: 12 additions & 8 deletions content/docs/interview/zhongchang/_index.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
---
title: "中厂面试"
weight: 2
bookCollapseSection: true
---

# 中厂面试

---
title: "中厂面试"
weight: 2
bookCollapseSection: true
---
# 中厂面试
这里收录中厂相关面试真题。

适合对比不同中厂岗位的题型分布、项目追问方式和手撕题方向。

## 本目录新增

- [CC-深信服一面](CC-Sangfor-First-Interview/)
Loading