写点什么

腾讯的面试,拷问的太全面了

作者:王中阳Go
  • 2025-07-01
    北京
  • 本文字数:7117 字

    阅读完需:约 23 分钟

腾讯的面试,拷问的太全面了

今天分享的是腾讯校招的一面面经,大厂拷问的知识点都很广泛,如果你也准备冲击大厂,一定要做足了准备,语言基础、数据库、缓存、消息队列、操作系统、计算机网络、算法、项目等等,基本上都会问到而且会问的很全很细,而且去大厂面试,要是没通过的话还会有记录,后面还想再去面就难咯。


先唠到这,下面开始分享热乎乎的面经:


1. 请简单做一个自我介绍

这个问题以及最后一个问题我昨天也提到了,想知道回答思路的可以移步这篇文章

2. Go 语言里怎样处理哈希冲突?

在 Go 语言中,哈希冲突的处理采用的是链地址法,也被叫做拉链法。其具体做法是,当多个键值对通过哈希函数计算后,得到相同的哈希值,这些键值对会被存储在同一个哈希桶里,而每个哈希桶都连着一个链表或者其他数据结构。以 Go 语言的原生哈希表 map 为例,它的底层实现是数组加上链表。当出现哈希冲突时,新的键值对会被添加到对应桶的链表中。要是链表变得过长,为了提升查询效率,还会对链表进行优化,比如把链表转换为红黑树。

3. 链地址法有什么优点和缺点?

优点


  • 实现起来较为简单,在设计哈希表时,无需对哈希函数提出过高要求,就能有效处理哈希冲突。

  • 具备良好的扩展性,链表的长度可以根据实际存储的数据量动态调整。

  • 对删除操作友好,在链表中删除一个节点的时间复杂度较低。


缺点


  • 当链表过长时,会使哈希表的查询、插入和删除操作的时间复杂度增加,退化为 O (n)。

  • 由于链表中的节点在内存中是离散存储的,不利于 CPU 缓存的优化,会影响性能。

  • 每个链表节点除了要存储数据,还需要额外的指针,这会增加内存的开销。

4. Java 的 HashMap 是如何应对哈希冲突的?

Java 的 HashMap 主要依靠链地址法来处理哈希冲突,其底层数据结构是数组和链表(或红黑树)。当发生哈希冲突时,新的元素会被添加到对应桶的链表尾部。为了避免链表过长导致性能下降,Java 8 引入了红黑树优化。当链表长度超过 8 且哈希表容量大于 64 时,链表会转换为红黑树,此时查询、插入和删除操作的时间复杂度从 O (n) 降低到 O (log n)。而当树的节点数减少到 6 时,红黑树又会退化为链表。

5. 进程和线程有什么本质上的不同?协程有哪些优势和特点?

进程和线程的本质区别


  • 进程是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。每个进程都有自己独立的内存空间、文件描述符等资源。

  • 线程是进程中的一个执行单元,是 CPU 调度和分派的基本单位。同一进程内的多个线程共享进程的资源,如内存、文件句柄等,但每个线程有自己独立的栈空间和程序计数器。


协程的优势和特点


  • 轻量级:协程的创建和销毁开销远小于线程,占用的内存资源也更少,因此可以在单个进程中创建大量协程。

  • 高效的上下文切换:协程的上下文切换由用户程序控制,不需要操作系统介入,避免了内核态和用户态的切换开销,提高了并发性能。

  • 基于事件驱动:协程通常配合异步 I/O 使用,在等待 I/O 操作时会主动让出执行权,让其他协程继续执行,从而提高系统的并发处理能力。

  • 简化异步编程:协程可以使用同步的编程方式编写异步代码,避免了回调地狱,使代码更易于理解和维护。

6. 在使用 Go 协程时,你遇到过哪些问题?

在使用 Go 协程的过程中,常见的问题有以下这些:


  • 资源竞争:多个协程同时访问和修改共享数据时,可能会引发数据竞争,导致程序出现不可预期的结果。

  • 死锁与活锁:在使用通道(channel)或者锁进行协程同步时,如果设计不当,可能会造成死锁或者活锁,使程序无法正常运行。

  • 内存泄漏:若协程中存在长时间运行的任务,且没有适当的退出机制,或者通道没有正确关闭,就容易导致内存泄漏。

  • goroutine 泄漏:协程启动后,如果因为某些异常情况未能正常结束,会不断积累,最终耗尽系统资源。

  • 性能问题:大量协程同时运行可能会增加调度开销,而且不合理的协程数量可能会导致系统资源过度使用,影响性能。

7. 子协程发生 panic 会造成什么后果?通常怎样解决?

后果:子协程发生 panic 时,若没有进行处理,会导致该协程崩溃并打印错误堆栈信息,但不会直接致使整个程序崩溃。不过,要是父协程依赖子协程的结果,或者子协程负责关键资源的释放,那么子协程的崩溃可能会让程序处于不一致的状态。


解决方法


  • 使用 recover 捕获 panic:在子协程中使用 deferrecover 来捕获 panic,避免协程异常退出。

  • 通过通道传递错误:子协程可以将错误信息通过通道传递给父协程,由父协程进行统一处理。

  • 使用 context 控制协程生命周期:借助 context 包来管理协程的取消和超时,保证在出现异常时能够及时关闭协程。

  • 监控与告警:对系统进行监控,一旦发现有未处理的 panic,及时发出告警,以便运维人员及时处理。

8. 简述 TCP 的三次握手和四次挥手过程

三次握手


  1. 客户端发送 SYN:客户端向服务器发送一个 SYN 包,并随机初始化一个序列号 seq = x,以此表明客户端希望建立连接并请求同步初始序列号。

  2. 服务器回复 SYN + ACK:服务器收到 SYN 包后,会向客户端发送一个 SYN + ACK 包。其中,SYN 包的序列号 seq = y 是服务器随机生成的,ACK 包的确认号 ack = x + 1,用于确认客户端的 SYN 包。

  3. 客户端发送 ACK:客户端收到服务器的 SYN + ACK 包后,会向服务器发送一个 ACK 包,确认号 ack = y + 1,表示客户端已收到服务器的 SYN 包。此时,连接建立成功。


四次挥手


  1. 客户端发送 FIN:客户端向服务器发送一个 FIN 包,seq = u,表示客户端想要关闭连接。

  2. 服务器回复 ACK:服务器收到 FIN 包后,会向客户端发送一个 ACK 包,ack = u + 1,确认客户端的 FIN 包。此时,服务器到客户端的连接仍处于开放状态。

  3. 服务器发送 FIN:服务器处理完数据后,向客户端发送一个 FIN 包,seq = v,表示服务器也想要关闭连接。

  4. 客户端回复 ACK:客户端收到服务器的 FIN 包后,会向服务器发送一个 ACK 包,ack = v + 1,确认服务器的 FIN 包。此时,连接彻底关闭。

9. 四次挥手之后,用 linux 命令查看会出现 time_wait 状态,它有什么意义?

TIME_WAIT 状态存在的意义主要有以下两点:


  • 确保最后的 ACK 能到达对方:在四次挥手的最后一步,客户端发送的 ACK 包有可能会丢失。如果服务器没有收到这个 ACK 包,就会重新发送 FIN 包。客户端处于 TIME_WAIT 状态,能够重新发送 ACK 包,从而保证连接的正常关闭。

  • 防止新旧连接混淆:TIME_WAIT 状态会持续 2MSL(Maximum Segment Lifetime,报文最大生存时间)。在这段时间内,本次连接的所有报文都会在网络中消失。这样一来,当新的连接建立时,就不会受到旧连接残留报文的干扰,避免了数据混淆的问题。

10. 为什么 TCP 建立连接需要三次握手,断开连接需要四次挥手?

三次握手的原因


  • 三次握手的主要目的是同步客户端和服务器的初始序列号,确保双方都有发送和接收数据的能力。

  • 第一次握手让服务器知道客户端有发送数据的能力;第二次握手让客户端知道服务器有接收和发送数据的能力;第三次握手让服务器知道客户端有接收数据的能力。

  • 两次握手无法保证双方初始序列号的同步,也不能确保双方都具备数据收发能力。


四次挥手的原因


  • TCP 连接是全双工的,这意味着双方可以同时进行数据的发送和接收。因此,关闭连接时需要分别关闭两个方向的连接。

  • 四次挥手将关闭连接的过程分为两个阶段:首先由客户端请求关闭发送方向的连接,然后服务器请求关闭接收方向的连接。由于服务器在收到客户端的 FIN 包后,可能还有数据需要发送,所以 ACK 和 FIN 通常分开发送,这就导致了四次挥手的过程。

11. 客户端通过三次握手建立连接后,怎样维持这个连接?

TCP 连接建立之后,主要通过以下几种方式来维持连接:


  • 心跳机制:应用层可以实现心跳机制,定期发送心跳包,以此确认对方是否在线。例如,在长连接的场景中,客户端和服务器会定期交换 PING/PONG 消息。

  • TCP Keep-Alive:TCP 协议本身提供了 Keep-Alive 机制。在连接长时间没有数据传输时,TCP 会发送 Keep-Alive 包。如果对方没有响应,经过多次重试后,TCP 会认为连接已经断开,并关闭该连接。

  • 滑动窗口机制:通过滑动窗口协议,接收方会不断向发送方确认已接收的数据,保证数据的可靠传输,同时也间接维持了连接的状态。

  • 超时重传:发送方发送数据后会启动定时器,如果在规定时间内没有收到确认,就会重传数据,确保连接的可靠性。

12. 你使用过哪些数据库?

我使用过多种数据库,包括关系型数据库和非关系型数据库。具体如下:


  • 关系型数据库:MySQL、PostgreSQL、Oracle、SQL Server。

  • 非关系型数据库:MongoDB、Redis、Elasticsearch、Cassandra。

  • 分布式数据库:TiDB、CockroachDB。

  • 内存数据库:Redis、Memcached。

13. B + 树的优势体现在哪些方面?

B + 树作为数据库索引的常用数据结构,具有以下优势:


  • 高度平衡:B + 树是一种平衡树,所有叶子节点都位于同一层,这使得查询效率稳定,时间复杂度为 O (log n)。

  • 范围查询高效:B + 树的叶子节点通过指针连接成有序链表,因此范围查询时无需进行中序遍历,直接沿着链表顺序访问即可,大大提高了范围查询的效率。

  • 磁盘读写优化:B + 树的节点可以存储多个键值,减少了树的高度,从而减少了磁盘 I/O 次数。同时,节点大小与磁盘块大小相匹配,进一步优化了磁盘读写性能。

  • 查询效率稳定:所有查询都要从根节点遍历到叶子节点,路径长度相同,因此查询效率比较稳定。

  • 插入和删除操作高效:B + 树通过分裂和合并节点来保持平衡,插入和删除操作的效率较高。

14. 什么情况下适合建立索引?

适合建立索引的情况主要有以下几种:


  • 经常用于查询条件的字段:在 WHERE 子句、JOIN 条件中经常出现的字段,建立索引可以加快过滤和连接的速度。

  • 用于排序的字段:对于 ORDER BY 和 GROUP BY 操作涉及的字段,索引可以避免文件排序,提高排序效率。

  • 用于连接的字段:多表连接时,连接字段建立索引可以加快表之间的匹配速度。

  • 高选择性的字段:字段的值分布比较分散,例如用户 ID、状态码等,建立索引后可以快速定位到少量数据。

  • 经常需要查询的字段:对于一些经常单独查询的字段,可以创建覆盖索引,避免回表查询,提高查询效率。

15. MySQL 有哪些事务隔离级别?

MySQL 支持四种事务隔离级别,从低到高依次为:


  • READ UNCOMMITTED(读未提交):在这种隔离级别下,一个事务可以读取另一个未提交事务的数据,会产生脏读、不可重复读和幻读问题。

  • READ COMMITTED(读提交):一个事务只能读取另一个已经提交事务的数据,避免了脏读,但仍然存在不可重复读和幻读问题。这是大多数数据库的默认隔离级别,也是 MySQL 中 InnoDB 存储引擎的默认隔离级别。

  • REPEATABLE READ(可重复读):在同一个事务中,多次读取同一数据的结果是一致的,避免了脏读和不可重复读。不过,在这种隔离级别下,仍然可能出现幻读。MySQL 的 InnoDB 存储引擎通过 MVCC(多版本并发控制)和间隙锁解决了幻读问题。

  • SERIALIZABLE(串行化):事务串行执行,避免了脏读、不可重复读和幻读。但这种隔离级别会导致并发性能下降,通常只在对数据一致性要求极高且并发量较低的场景下使用。

16. 可重复读隔离级别是如何解决幻读问题的?

MySQL 的 InnoDB 存储引擎在可重复读隔离级别下,通过以下两种机制解决幻读问题:


  • MVCC(多版本并发控制):MVCC 为数据的每个版本都保存了一个时间戳,通过比较时间戳来决定哪些版本的数据是可见的。在可重复读隔离级别下,事务在开始时会创建一个一致性视图,之后的查询都会使用这个视图,保证了在同一个事务中多次读取同一数据的结果是一致的,避免了幻读。

  • 间隙锁(Gap Lock):当执行范围查询时,InnoDB 不仅会锁定查询到的记录,还会锁定这些记录之间的间隙,防止其他事务在这些间隙中插入新记录,从而避免了幻读。例如,执行 SELECT * FROM table WHERE id BETWEEN 1 AND 10 FOR UPDATE 时,InnoDB 会锁定 ID 为 1 到 10 之间的间隙,阻止其他事务插入 ID 在这个范围内的记录。

17. 你参与过权限系统相关的项目吗?

18. 如果要为腾讯云的文档系统设计一个权限系统,你会怎么做?

为腾讯云文档系统设计权限系统时,可以参考以下设计思路:


1. 采用多层次权限模型


  • 全局层级:对用户的注册、登录、找回密码等操作进行权限控制。

  • 组织层级:在企业或团队层面,设置管理员、成员等角色,管理员能够管理组织的基本信息和成员。

  • 空间层级:不同的空间可以设置不同的访问权限,例如公开空间、内部空间等。

  • 文档层级:针对具体的文档或文件夹,设置创建、读取、更新、删除、分享等细粒度的权限。


2. 实现混合权限控制


  • 基于角色的访问控制(RBAC):预先定义好角色,如管理员、编辑者、查看者等,每个角色对应一组权限。用户通过被分配角色来获得相应的权限,这种方式便于权限的批量管理。

  • 基于属性的访问控制(ABAC):根据用户属性(如部门、职位)、资源属性(如文档类型、敏感度)、环境属性(如访问时间、IP 地址)等多方面因素动态计算用户的权限,增强权限控制的灵活性。


3. 设计权限继承与覆盖机制


  • 文档的权限默认继承自父文件夹或空间,但也可以单独设置文档的权限,覆盖继承的权限。

  • 当用户同时拥有多个角色或权限时,采用权限累加的原则,即用户拥有所有角色权限的并集。


4. 集成多因素认证


  • 对于高敏感的文档或操作,如删除重要文档、导出大量数据等,要求用户进行二次认证,例如短信验证码、指纹识别等,提高系统的安全性。


5. 建立审计与监控机制


  • 记录用户的所有权限变更操作和重要的文档访问行为,以便进行审计和追踪。

  • 设置异常行为监控规则,如多次尝试访问受限文档、异常的权限变更操作等,及时发现并处理潜在的安全风险。


6. 提供灵活的权限管理界面


  • 为管理员提供直观的权限管理界面,支持批量分配权限、权限模板管理等功能。

  • 允许文档所有者或协作者自主管理文档的共享权限,方便日常使用。


7. 考虑与其他系统的集成


  • 与企业的 LDAP、OAuth 等身份认证系统集成,实现单点登录。

  • 与企业的组织结构同步,自动更新用户的角色和权限。

19. 如何应对突发的流量高峰?怎样设计一个智能的流量调度系统?

应对突发流量高峰的策略


  • 水平扩展:借助容器化技术(如 Kubernetes)和云服务(如 AWS Auto Scaling、腾讯云弹性伸缩),根据流量情况自动调整服务器数量,实现快速扩容和缩容。

  • 缓存机制:在关键位置设置多级缓存,如 CDN、浏览器缓存、Redis 等,减少对后端服务的直接访问压力。

  • 限流与熔断:

  • 限流:对请求进行限速,例如采用令牌桶、漏桶算法,防止系统被过量请求压垮。

  • 熔断:当服务出现故障或过载时,自动切断部分请求,避免级联失败。

  • 降级策略:在流量高峰期间,暂时关闭非核心功能,保证核心业务的正常运行。例如,简化页面展示、暂停推荐算法等。

  • 异步处理:将非实时性的任务放入消息队列(如 Kafka、RabbitMQ)进行异步处理,降低同步处理的压力。

  • 负载均衡:使用高性能的负载均衡器(如 Nginx、HAProxy)将流量均匀分配到多个服务器上。


智能流量调度系统的设计


  • 多区域部署:采用多可用区、多地域部署服务,利用全局负载均衡(如 AWS Global Accelerator)将用户请求引导至最近且负载较低的区域。

  • 智能路由:

  • 依据用户地理位置,将请求路由到最近的服务器。

  • 根据服务器的负载情况,动态调整流量分配比例。

  • 对于特殊用户(如 VIP 用户),优先分配高性能服务器资源。

  • 流量预测与预案:

  • 基于历史数据和机器学习算法,预测流量高峰的时间和规模。

  • 提前制定不同级别的应急预案,如扩容方案、限流阈值等。

  • 自适应调整:

  • 实时监控系统的负载、性能指标,动态调整流量分配策略。

  • 结合 A/B 测试,自动选择最优的流量调度方案。

  • 弹性资源分配:与云服务提供商合作,实现资源的弹性分配。例如,在流量高峰时自动租用更多的云服务器,流量下降后释放资源,降低成本。

20. 你实现过任务优先级调度系统吗?请介绍一下实现思路

实现任务优先级调度系统时,可以参考以下思路:


1. 任务模型设计


  • 为每个任务定义优先级属性,例如使用整数(1 - 10)或枚举类型(LOW、MEDIUM、HIGH)来表示优先级。

  • 除了优先级,任务还可以包含执行时间、超时时间、重试次数等其他属性。


2. 优先级队列实现


  • 采用优先队列(如 Go 语言中的 container/heap 包、Java 中的 PriorityQueue)来存储任务,确保高优先级的任务能够优先被处理。

  • 当有新任务加入队列时,根据其优先级将任务插入到合适的位置。


3. 调度算法选择


  • 抢占式调度:高优先级的任务可以中断正在执行的低优先级任务。这种调度方式适用于对实时性要求较高的场景。

  • 非抢占式调度:高优先级的任务需要等待当前正在执行的任务完成后才能开始执行。这种调度方式实现相对简单,系统开销较小。


4. 资源分配策略


  • 根据任务的优先级,为不同优先级的任务分配不同比例的系统资源,例如 CPU 时间、内存等。

  • 对于高优先级的任务,可以分配更多的资源,确保其能够快速执行。


5. 任务执行与监控


  • 使用工作线程池来执行任务,线程池的大小可以根据系统资源和负载情况动态调整。

  • 监控任务的执行状态,记录任务的开始时间、完成时间、执行结果等信息。

  • 对于超时未完成的任务,可以根据其优先级决定是否重新调度或终止执行。


6. 优先级调整机制


  • 实现任务优先级的动态调整机制,例如任务等待时间过长时提高其优先级,避免饥饿现象。

  • 支持人工干预,允许管理员手动调整任务的优先级。


7. 容错与重试策略


  • 对于执行失败的任务,根据其优先级和重要性决定是否进行重试。

  • 高优先级的任务可以设置更多的重试次数或更短的重试间隔。


8. 监控与告警


  • 实时监控任务队列的长度、任务执行时间、系统资源使用情况等指标。

  • 当队列长度超过阈值、高优先级任务等待时间过长时,及时发出告警,以便管理员进行干预。

21. 你有什么问题想要问我的吗?

这个问题以及第一个问题我昨天也提到了,想知道回答思路的可以移步这篇文章

欢迎关注 ❤

我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。


没准能让你能刷到自己意向公司的最新面试题呢。


感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。

发布于: 刚刚阅读数: 3
用户头像

王中阳Go

关注

靠敲代码在北京买房的程序员 2022-10-09 加入

【微信】wangzhongyang1993【公众号】程序员升职加薪之旅【成就】InfoQ专家博主👍掘金签约作者👍B站&掘金&CSDN&思否等全平台账号:王中阳Go

评论

发布
暂无评论
腾讯的面试,拷问的太全面了_Go_王中阳Go_InfoQ写作社区