CC-深信服一面
方向: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 或上传凭据直传对象存储;上传成功后,客户端再调用发布接口。
发布接口的并发风险主要是用户重复点击、网络超时重试和客户端重复投递,导致同一视频生成多条记录。处理过程如下:
- 客户端为一次发布生成
request_id,服务端也可生成发布任务 ID。 - 服务端校验登录状态、文件对象和参数,创建
videos、publish_tasks等必要记录。 - 对
author_id + request_id建唯一索引;发生重复请求时查询并返回第一次创建的任务或视频,而不是重复插入。 - 数据库事务只保护元数据、状态和必要关系,不执行转码、截图、审核等耗时工作。
- 后续任务通过消息队列异步处理;消费者完成后用条件更新推进状态,失败则记录错误并允许重试。
这样不需要使用全局锁。全局锁会让不同用户互相阻塞,而唯一约束与幂等键只会约束同一发布请求,吞吐量更高。
4. 多用户高并发发布视频,后端只有一个数据库,如何缓解入库压力?#
答:核心是使用消息队列削峰填谷,而不是把所有发布请求同步写入单个数据库。
客户端直传对象存储
↓
发布服务校验并投递消息
↓
消息队列暂存峰值流量
↓
消费者按受控并发度写入 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 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。
排查接口问题时先分层:
- 用
curl -v查看 DNS、连接、TLS、请求头和响应头; - 关注状态码:
2xx成功、301/302重定向、400参数错误、401未认证、403无权限、404路径不存在、429限流、5xx服务端异常; - 查看应用日志、反向代理日志和数据库/依赖服务耗时;
- 用
ss检查端口监听和连接状态,必要时用抓包工具确认 TCP 握手、重传或超时。
6. 如果实际组装一台电脑,会怎么做?#
答:先明确用途和预算,例如办公、游戏、开发或视频剪辑,再围绕兼容性和性能瓶颈选型。
| 组成原理中的部件 | 实际硬件映射 | 选型关注点 |
|---|---|---|
| 运算器、控制器 | CPU | 插槽、核心数、性能与功耗 |
| 主存储器 | 内存(RAM) | 主板代际、容量、频率与双通道 |
| 外存储器 | SSD / HDD | NVMe 或 SATA、容量与读写需求 |
| 输入/输出设备 | 键盘、鼠标、显示器、网卡、显卡等 | 接口与实际使用场景 |
| 总线与连接 | 主板、PCIe、SATA、USB 等 | CPU 插槽、内存插槽、扩展能力 |
独显、CPU、内存和主板要兼容;电源要预留足够功率并选择可靠品牌;机箱尺寸要容纳主板、显卡和散热器;最后装机、接线、进入 BIOS 检查硬件识别和温度,安装系统并做压力测试。
五、职场软技能与场景问题#
1. 你的抗压能力怎么样?#
答:我认为抗压不是单纯熬时间,而是在压力下保持信息透明、优先级清晰和稳定交付。遇到紧急任务时,我会先确认截止时间、影响范围和验收标准,拆分出最小可交付版本,优先解决阻塞项;对风险及时同步,不等到最后一刻才暴露问题。高强度阶段结束后也会复盘流程和技术债,减少同类压力反复出现。
2. 任务只给有限上下文和大方向,会如何推进?有类似实习经历吗?#
答:我会先把模糊需求转成可验证的问题:目标用户是谁、要解决什么问题、输入输出是什么、优先级和验收标准是什么。然后阅读现有代码、文档、接口和监控数据,画出涉及的模块与依赖;在不确定点上主动向负责人确认,不会在关键假设上长期闭门开发。
接着将任务拆成调研、方案、最小实现、测试和发布观察几个阶段,先交付可运行的最小版本,再逐步完善。若有真实实习或项目案例,应按“背景—任务—行动—结果”说明;没有实习时不要虚构,可以用课程项目、开源贡献或团队项目举例。
3. 同时有多个任务,如何处理和排期?#
答:先按紧急程度、业务影响、依赖关系和工作量排序。存在外部依赖或会阻塞他人的事项优先推进;重要但不紧急的任务拆到迭代计划中。每天明确一到两个最重要目标,预留处理线上问题和沟通的缓冲时间。若资源和时间确实冲突,会尽早说明各方案的交付风险,请负责人确认优先级,而不是默认所有任务都能按原计划完成。
4. 技术热情怎么样?是否确定未来做研发岗位?#
答:我希望长期从事研发,尤其倾向后端与基础服务方向。吸引我的不仅是写出一个功能,更是通过设计、性能优化、稳定性建设和自动化让系统长期可靠运行。我会持续学习 Go、数据库、分布式系统和云原生相关知识,也愿意在实际业务里根据团队需要拓展其他技术栈。