首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >字节一面,问得有点深!

字节一面,问得有点深!

作者头像
王中阳AI编程
发布2026-03-17 20:31:12
发布2026-03-17 20:31:12
1550
举报
文章被收录于专栏:Go语言学习专栏Go语言学习专栏

大家好,最近咱们训练营又有同学去挑战了字节跳动,这次是一面。

众所周知,字节的面试风格一向是“重基础、抠细节”,尤其是对语言底层原理(GMP、Map 机制)和中间件(Redis 数据结构)考察得非常细致。

今天我就把这次的面试题整理出来,去掉了个人项目相关的部分,只保留了通用的技术干货。如果你也想冲大厂,这份作业记得抄好!


1. Go 切片和数组的区别?

【怎么回答】这题是开胃菜,但要答在点子上:

  • 数组(Array):值类型,长度固定。赋值或传参时会发生整个数组的拷贝,内存开销大。
  • 切片(Slice):引用类型,长度动态。本质是一个结构体(Header),包含三个字段:指向底层数组的指针长度(len)容量(cap)。传参时只拷贝这个轻量级的结构体,性能很高。

2. Go 协程和进程、线程的区别?

【怎么回答】主要对比开销和调度方式:

  • 进程/线程:由操作系统内核调度,切换开销大(微秒级),栈内存较大(MB 级)。
  • 协程(Goroutine):Go 特有的用户态线程。
    • 更轻量:启动只需几 KB 栈内存。
    • 更高效:由 Go Runtime(运行时)调度,不需要切换到内核态,切换成本极低(纳秒级)。
    • 通信便利:原生支持 Channel 进行通信。

3. 用过 Select 吗?用来干嘛?

【怎么回答】select 类似于操作系统的 IO 多路复用(poll/epoll),但在 Go 里是用来处理多个 Channel 的操作。

  • 作用:它能监听多个 channel 的读写状态。
  • 机制:哪个 channel 准备好了(即能读或能写),就执行对应的 case。如果多个同时准备好,会随机选择一个执行(避免饥饿)。
  • 超时控制:常配合 time.After 实现超时处理。

4. Channel 是做什么用的?

【怎么回答】记住 Go 的名言:“不要通过共享内存来通信,而要通过通信来共享内存。”Channel 就是这个“通信”的桥梁。它用于在不同的 Goroutine 之间安全地传递数据、同步状态以及进行任务编排(比如控制并发数、优雅退出等)。

5. Go 的互斥机制?有哪些并发原语?

【怎么回答】面试时如果一时想不起来“原语”这个词,就说 sync 包里的东西。

  • 互斥锁sync.Mutex(互斥锁)和 sync.RWMutex(读写锁)。
  • 并发原语(面试官追问点):
    • sync.WaitGroup:等待一组协程完成。
    • sync.Once:确保代码只执行一次(单例模式常用)。
    • sync.Cond:条件变量,用于复杂的通知机制。
    • sync.Pool:对象池,复用对象减少 GC。
    • atomic 包:原子操作,性能比锁更高。

6. sync.Map 和原生 Map 的区别?

【怎么回答】

  • 原生 Map线程不安全!并发读写会直接 Panic。
  • sync.Map线程安全。它内部用了读写分离(read map 和 dirty map)加锁的机制。
  • 适用场景sync.Map 并不适合所有并发场景,它最适合读多写少(Read map 命中率高)或者追加写(Key 只增不减)的场景。如果是频繁的读写混合,普通的 Mutex + map 性能可能更好。

7. GMP 模型?(重点!)

【怎么回答】这是字节必考题,必须烂熟于心:

  • **G (Goroutine)**:协程,存任务信息。
  • **M (Machine)**:内核线程,真正干活的。
  • **P (Processor)**:处理器/上下文,管理 G 的队列,是 M 和 G 的中间层。
  • 调度核心
    1. Work Stealing(工作窃取):当 P 本地队列没任务时,会去偷其他 P 的任务,或者去全局队列拿,保证 M 不闲着。
    2. Hand Off(交接):当 M 因为系统调用(比如读文件)阻塞时,P 会和 M 分手,带着剩下的 G 去找新的 M 继续干活,保证 CPU 利用率。

8. Redis 用过哪些数据结构?Zset 底层是什么?

【怎么回答】

  • 常用结构:String, Hash, Set, Zset (Sorted Set)。
  • 场景
    • Hash:存对象,比如用户信息、购物车(key 是用户 ID,field 是商品 ID)。
    • Zset:排行榜、延迟队列。
  • Zset 底层
    • 旧版:Ziplist(压缩列表) + Skiplist(跳表)。
    • 新版(Redis 7.0+)Listpack(紧凑列表) + Skiplist。
    • 为什么用 Listpack?:Ziplist 有“连锁更新”问题(一个元素变动导致后面所有元素内存重分配),Listpack 解决了这个问题,更加紧凑高效。

9. MySQL 怎么优化慢查询?

【怎么回答】老三样套路:

  1. 开启慢日志:定位是哪条 SQL 慢。
  2. Explain 分析:看执行计划,有没有走索引?是不是全表扫描?
  3. 优化手段
    • 改 SQL:避免 select *,少做复杂的 Join。
    • 改索引:加索引,或者优化最左前缀匹配。
    • 改结构:字段冗余、分库分表(量大时)。

10. 场景题:文件很大,内存很小,如何排序?

【怎么回答】这题考的是外部排序(External Sort),标准解法是归并排序

  1. 分片(Split):把大文件切成 N 个小文件,每个小文件的大小能塞进内存。
  2. 内存排序:依次把小文件读进内存,用快排排好序,写回磁盘(生成临时有序小文件)。
  3. 多路归并(Merge):同时打开这 N 个有序小文件,每个文件读第一个数放入最小堆(Min-Heap)
  4. 输出:从堆顶拿出最小的数写入最终结果文件,然后从该数所属的小文件里再读下一个数进堆。循环直到所有文件读完。

11. 手撕:快速排序

【怎么回答】快排是面试手写的高频题,一定要能默写出来。核心是 Partition(分区)思想。

代码语言:javascript
复制
func quickSort(arr []int) []int {
    iflen(arr) <= 1 {
        return arr
    }
    
    pivot := arr[0] // 选基准值
    var left, right []int
    
    for _, v := range arr[1:] {
        if v <= pivot {
            left = append(left, v)
        } else {
            right = append(right, v)
        }
    }
    
    // 递归:左 + 基准 + 右
    returnappend(append(quickSort(left), pivot), quickSort(right)...)
}
// 注意:上面是易于理解的切片版本,面试最好写原地交换(In-place)版本节省空间

结尾

这轮面试虽然没有问特别偏门的架构设计,但在 Go 语言机制(GMP、Map)和数据结构(Redis Listpack、外排)上挖得比较深。

这也提醒我们:千万别只会调 API! 对于高频使用的数据结构和并发模型,一定要知道它底层是怎么跑的。多看源码,多看书(比如 Docker、K8S、MySQL 高级进阶),才能在字节这种大厂面试中游刃有余。

祝大家都能拿到心仪的 Offer!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 王中阳 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Go 切片和数组的区别?
  • 2. Go 协程和进程、线程的区别?
  • 3. 用过 Select 吗?用来干嘛?
  • 4. Channel 是做什么用的?
  • 5. Go 的互斥机制?有哪些并发原语?
  • 6. sync.Map 和原生 Map 的区别?
  • 7. GMP 模型?(重点!)
  • 8. Redis 用过哪些数据结构?Zset 底层是什么?
  • 9. MySQL 怎么优化慢查询?
  • 10. 场景题:文件很大,内存很小,如何排序?
  • 11. 手撕:快速排序
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档