国庆假期刚结束,马上就收到了一位粉丝分享的腾讯音乐面试经历。这是一场质量很高的面试,大厂面试官确实实力强劲。
但是由于一直忙于交接和工作,他完全裸面,所以在面试后直呼:“想哭,面试中全程被面试官牵着走,没有掌握主动权将话题引向自己擅长的方向”。
所以我建议大家在面试前一定要充分准备,特别是项目相关内容要深入理解每个决策背后的逻辑。
下面看一下他分享的内容吧:
面试内容
(一)开场
- 面试官介绍岗位
- 岗位为腾讯音乐 - 全民K歌 - 国际版 - 直播歌房后台研发工程师,主要负责直播相关功能,开发语言为Go和C++。
- 关于求职者基本情况
- 要求5分钟以内的自我介绍。
- 被问到所在公司技术团队裁撤后为何会留下自己。
- 岗位base深圳能否接受。
(二)项目相关
- 系统重构 - 数据迁移项目
- 微服务相关
- 如何理解微服务,为什么微服务能提升系统可拓展性。
- 微服务相比于单体服务的缺点。
- 服务间通信成本指的是什么,成本体现在哪。
- 数据库迁移原因与成本评估
- 把数据库从MongoDB迁移到MySQL的原因。
- 如何评估迁移异构数据库(MongoDB到MySQL)的成本,是否仅考虑成熟度和被接受程度,因为迁移涉及数据同步工作量巨大。
- 技术选型相关
- 如果采用最终一致性方案,MySQL支持事务而MongoDB不支持事务,有什么原因不使用最终一致性方案。
- 数据同步与异常处理
- 迁移过程中数据同步怎么做(新数据库写MySQL,写脚本迁移Mongo数据到MySQL)。
- 业务要读写新旧数据时怎么办。
- 迁移比较暴力,如何发现数据异常,异构数据库迁移时如何保证每条数据正常迁移到MySQL。
- 唯一键场景下(Mongo已有数据,切换到MySQL写时可能重复写入)的处理。
- 优化服务器项目
- 分布式缓存实现
- 优化服务器引入分布式缓存技术(具体是Redis),如何实现的。
- 缓存与数据库一致性
- 如何保证Redis缓存和数据库的数据一致性(回答缓存先写数据库后写存在问题)。
- 写缓存成功数据库失败时缓存是否为脏数据。
- 先写数据库成功再写Redis,缓存更新失败怎么办,如何知道何时将数据库数据同步到缓存及同步的做法。
- 缓存是否有过期时间机制,有没有不过期一直生效的情况,有过期的话多久过期。
- 缓存同时过期会有什么问题以及如何解决。
- 数据抓取业务项目
- Kafka相关
- Kafka在业务中的角色。
- 业务为什么要经过Kafka这一层,账号量有多大。
- 定时任务可分散执行时间,Kafka在这种情况下削峰意义不大,是否有其他考虑使用Kafka的因素。
- 防止账号丢失已有确认机制,当作下游处理失败重新触发即可,为何还需要Kafka。
- 抖音微信小游戏归因业务项目
- Token缓存相关
- 微信小游戏的token存于sync.map(服务内存),为何使用服务内存而不使用Redis或其他外部缓存方案。
- 用户量相关,当有一定数量实例和用户量时这种方案是否有缺陷(此处存在理解偏差导致回答失误)。
(三)Go和MySQL
1. MySQL相关
1. MySQL中索引如何实现,为什么MySQL里不使用B树。
在MySQL中,索引的实现主要依赖于特定的数据结构,以提高数据检索的速度。其中最常见的索引类型是基于B+树(B+Tree)的数据结构。
MySQL中的索引实现
B+树索引:
- MySQL中的大多数存储引擎(如InnoDB和MyISAM)默认使用B+树作为索引的数据结构。
- B+树的所有叶子节点都处于同一层,且相互之间有指针相连,这样可以方便地进行范围查询。
- 叶子节点包含了指向记录的指针,而非内部节点,这使得B+树更适合用于全键值、范围或排序操作。
B树索引:
- 虽然B树也可以用作索引,但在MySQL中使用较少。
- B树的每个节点都可以存储数据,包括内部节点,这可能导致更多的磁盘I/O操作,因为每次查询可能需要遍历更多的节点。
为什么MySQL里不使用B树
尽管B树在理论上可以用来实现索引,但在MySQL中更倾向于使用B+树,主要原因包括:
磁盘I/O优化:
- B+树的所有数据都在叶子节点上,这意味着对于非叶子节点来说,只需要存储键值和指向下一层节点的指针,这使得每个节点可以容纳更多的键值,进一步降低了树的高度,减少了磁盘I/O次数。
范围查询优化:
- B+树的叶子节点之间有链接,这使得范围查询更加高效。执行范围查询时,可以从第一个满足条件的叶子节点开始,沿着节点间的链接快速访问后续节点,无需返回上层节点重新寻找下一个满足条件的节点。
插入删除效率:
- 在B+树中,插入和删除操作通常只影响叶子节点,不会影响非叶子节点,这简化了树的维护过程,提高了操作效率。
Go相关
2. 切片如何实现
一个切片实际上是一个包含三个字段的结构体:
- 指针(Pointer):指向底层数组的一个指针。
- 长度(Length):表示切片当前包含的元素个数。
- 容量(Capacity):表示从切片指向的数组起始位置到数组末尾的元素个数。
底层结构:
type slice struct {
array uintptr // 指向底层数组的指针
len int // 切片的长度
cap int // 切片的容量
}
3. Go里并发读写map会出现问题,如何解决
- 使用互斥锁(Mutex)
使用 sync.Mutex 或 sync.RWMutex 对 map 的读写操作进行同步控制。这种方法适用于读写操作都较频繁的场景,示例:
import (
"sync"
)
type SafeMap struct {
mu sync.Mutex
m map[string]int
}
func (sm *SafeMap) Set(key string, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}
func (sm *SafeMap) Get(key string) (int, bool) {
sm.mu.Lock()
defer sm.mu.Unlock()
value, ok := sm.m[key]
return value, ok
}
- 使用 sync.Map:
Go 1.9 版本引入了 sync.Map,这是一个并发安全的 map 实现。sync.Map 适用于读多写少的场景,因为它在读取时不加锁,而在写入时才加锁,从而减少了锁的竞争,示例:
import (
"sync"
)
var sm sync.Map
func Set(key, value interface{}) {
sm.Store(key, value)
}
func Get(key interface{}) (interface{}, bool) {
return sm.Load(key)
}
- 使用通道(Channel):
通过通道协调对 map 的访问,确保同一时间只有一个goroutine可以读写 map。
4. Go里的sort排序如何实现
sort
包提供了多种排序功能,可以对不同类型的切片和自定义集合进行排序。sort
包的核心思想是通过实现 sort.Interface
接口来提供排序功能:
sort.Interface
是一个接口,定义了排序所需的三个方法:
Len()
:返回要排序的元素个数。Less(i, j int)
:如果第i
个元素应该排在第j
个元素之前,则返回true
。Swap(i, j int)
:交换第i
个元素和第j
个元素的位置。
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
常见类型的排序:
sort
包提供了对常见类型的切片进行排序的便捷函数:
Ints
:对[]int
类型的切片进行升序排序。Float64s
:对[]float64
类型的切片进行升序排序。Strings
:对[]string
类型的切片进行升序排序。
5. 协程和线程的区别,线程开销大的原因,协程有上下文切换为何线程消耗更多CPU资源
协程和线程的区别
资源消耗:
- 线程:每个线程有自己的栈空间(几十KB到几MB),内存消耗大。
- 协程:栈空间较小(几KB),动态调整,内存消耗小。
调度方式:
- 线程:由操作系统控制,抢占式调度,涉及内核态切换。
- 协程:由应用程序控制,协作式调度,在用户态进行。
上下文切换:
- 线程:涉及内核态和用户态切换,开销大。
- 协程:只在用户态进行,开销小。
并发粒度:
- 线程:适用于处理多个相对独立的任务。
- 协程:适用于在单个线程内实现多个任务的协作和切换。
线程开销大的原因
内存分配:
- 每个线程需要固定大小的栈空间,占用大量内存。
上下文切换:
- 涉及内核态和用户态切换,需要保存和恢复大量寄存器状态和栈指针。
调度开销:
- 操作系统维护复杂的调度算法和数据结构,增加系统开销。
同步机制:
- 使用锁、信号量等机制,高并发场景下可能导致性能瓶颈。
协程消耗更少CPU资源的原因
上下文切换简单:
- 只在用户态进行,不需要内核态介入,开销小。
调度灵活:
- 开发者控制调度,按需切换,减少不必要的上下文切换。
内存管理高效:
- 栈空间小且动态调整,内存利用率高。
同步机制简单:
- 通常通过通道(如Go语言中的channel)实现同步,避免锁的竞争。
(四)算法与反问
- 算法手撕
- 反问
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:sf面试群。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。