哭晕,腾讯的面试太难了。。。

国庆假期刚结束,马上就收到了一位粉丝分享的腾讯音乐面试经历。这是一场质量很高的面试,大厂面试官确实实力强劲。

但是由于一直忙于交接和工作,他完全裸面,所以在面试后直呼:“想哭,面试中全程被面试官牵着走,没有掌握主动权将话题引向自己擅长的方向”。

所以我建议大家在面试前一定要充分准备,特别是项目相关内容要深入理解每个决策背后的逻辑。

下面看一下他分享的内容吧:

面试内容

(一)开场

面试官介绍岗位

岗位为腾讯音乐 - 全民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.Mapfunc 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)实现同步,避免锁的竞争。

(四)算法与反问

算法手撕

反问

#腾讯##腾讯面试##Go##面经##腾讯音乐#
Go面试题 文章被收录于专栏

这里汇总了大量了跟Go相关的面试题和面经,希望对大家有所帮助!

全部评论
这是问了多久
点赞 回复 分享
发布于 10-30 00:31 湖北

相关推荐

10-29 11:50
东北大学 C++
分布式缓存,你的分布式key是怎么分桶的?分桶规则是什么?节点的增减会不会影响你的数据分布?有没有主节点?没有主节点来管理整个数据的一个分布规则,如果有一个节点挂了,客户端怎么感知的?假设正在请求中,down了一个节点,比如原来5个down之后变成4个,那这个时候你请求的有部分数据,它的数据分布就变了,你的客户端怎么知道这个事情?怎么会路由到其他节点上去呢?你这个数据可能正在迁移,你数据正在迁移,假设你的第五个节点宕机了,那么你的第五个节点的数据从哪里取出来?你这个节点宕机了,其他节点怎么拿到他这个宕机的数据?相当于你存的节点的数据在数据库都有?缓存有副本吗?平时都建议把C++模板类的实现,放到头文件中,基于什么考虑?C++一个虚函数可以定义成模板函数吗?如果我在一个头文件中定义一个函数,这里会有什么问题吗?一个类的大小有什么有关?如果一个类本身有一个虚函数,再加一个虚函数它的大小怎么变化?unordered_map和map的区别有unordered_map查找时间复杂度是O(1),既然有基于哈希表的map,为什么还要有基于红黑树的map?解决哈希冲突的一些方法C++三种智能指针unique_ptr怎么做到的单独拥有一块内存,而不是和别人共享一块内存?手撕:字符串相乘,没撕出来,挂
腾讯一面1582人在聊 查看17道真题和解析
点赞 评论 收藏
分享
10 31 评论
分享
牛客网
牛客企业服务