4/25 中科大:字节实习一年的感受,真的有点···
- 作者
- Name
- 青玉白露
- Github
- @white0dew
- Modified on
- Reading time
- 15 分钟
阅读:.. 评论:..
大家好,我是青玉白露,下面是一位中科大的同学,分享在字节实习一年的刚收,真的有点东西,强烈建议多次阅读!收藏!
分享
作为一个中科大软件工程的学生,我有幸在字节跳动实习了一年多的时间。
这段经历无疑是我学习和成长过程中的一笔宝贵财富。一年多来,我见证了自己的变化,也积累了不少心得。是时候把这些经历和感悟分享给大家了,我想通过两个部分来聊聊:一是面试经历的总结,二是来到字节后的个人体会。
面试经历总结
首先,字节的技术岗面试大致是"3+1"面的流程。
我的第一轮面试主要是同事面,侧重于计算机基础知识的考查,同时对我做过的项目也表示出了浓厚的兴趣。
第二轮则是组长面,这轮依然聚焦于基础知识,同时对我以往的项目和实习经历进行了深入的了解和讨论。
第三轮是领导面,这一轮的问题相对更加宏观。
在面试过程中,项目提问是一个重点。一般来说,会围绕以下几个方面进行提问:
- 项目中的职责分工
- 项目的难点和亮点
- 解决问题的方式和思路
提前对所做项目做一个思路的整理是非常有必要的。
描述时保持语速适中,避免过快,以免面试官跟不上。
算法题几乎每一轮都会出现,可能一上来就要做,也可能是面试的最后。
我的经验是,面试前至少要把top100的题目过一遍,并且对题目进行一些专题总结。
面试时不要慌张,和面试官沟通清楚思路再动手,避免因为急躁出现粗心大意的错误。
至于基础知识,网络知识几乎是必问的,此外数据库、操作系统、缓存、分布式、高并发等相关问题也是常见的提纲。日常积累很关键,面试前最好找一些提纲,尝试串起来说一遍。
字节一年多的实习体会
在字节跳动实习的这段时间,给我最深的感受就是这里对实习生的包容和尊重。
初来乍到,一切都显得新奇又陌生。我也曾在会后感到脑子一片混乱,信息消化不过来。但正是这个阶段,我觉得自己成长最快。
在字节,我们互相称呼为"同学",这让我感觉就像在学校一样。
大家相处融洽,工作学习氛围不设边界,自由自在。
最重要的是,这里鼓励自底向上的驱动模式,不是等着被分配任务,而是需要自己发现要做的事情,制定目标和计划。
一年多的实习生涯,让我受益良多。
虽然我仍然觉得自己很"菜",但我也为自己定下了更多的目标,一步步朝着它们前进。我不喜欢设定太长远的目标,因为生活总是充满变数。
但至少,我希望能够早日在某个方向上拥有完整的Owner能力,不断超越自我,创造更多的佳绩。
好了,讲了这么多,接下来就让我们看一次字节面试吧。
面试分享
面试官: Hi,欢迎来到今天的面试。首先,我们聊聊热部署。你能解释一下热部署的原理,尤其是它是如何打破双亲委派机制的吗?
求职者: 当然。热部署主要是为了在开发过程中提高效率,允许开发者在应用运行时更新代码而无需重启应用。它打破双亲委派机制的核心在于自定义类加载器。在Java的双亲委派模型中,类加载器会先尝试让父加载器去加载类,只有在父加载器加载失败时才自己加载。热部署通过使用自定义类加载器,每次都重新加载指定的类文件,从而实现类的热替换。
面试官: 很好。接下来,谈谈RocketMQ是如何实现事务消息的?
求职者: RocketMQ实现事务消息主要依靠半消息和事务状态回查机制。首先,生产者发送一条半消息到MQ,此时消息不对消费者可见。然后,执行本地事务。如果事务执行成功,生产者再次发送一条确认消息给MQ,使之前的半消息对消费者可见。如果本地事务执行失败,则发送一条回滚消息,MQ将删除半消息。此外,MQ还会定期对半消息进行回查,确保每条消息都能最终被确认或回滚。
面试官: 接着,如何实现顺序消费?
求职者: 顺序消费的关键在于确保生产者将消息发送到同一个队列,并且消费者串行消费该队列。在RocketMQ中,可以通过设置MessageQueueSelector来确保同一类消息发送到同一个Message Queue中。消费时,采用单线程消费模式或者MessageListenerOrderly监听器保证消息能够按照发送顺序被消费。
面试官: 好的。现在,如何排查慢SQL?
求职者: 排查慢SQL主要可以通过以下步骤:
- 开启慢查询日志,记录执行时间超过阈值的SQL语句。
- 使用
EXPLAIN
或其他数据库分析工具分析这些慢查询,查看执行计划,识别是否有全表扫描、索引失效等情况。 - 根据分析结果,优化SQL语句,比如添加合适的索引,调整查询条件,或者改写查询逻辑。
- 对于复杂查询,可以考虑分解成多个简单查询,减少单次查询负担。
面试官: Spring自动装配是如何工作的,你知道怎么通过pom文件知道导入哪个配置类么?
求职者: Spring自动装配是通过@ComponentScan来扫描包路径下的组件,并通过@Autowired注解来自动注入依赖。而Spring Boot项目中,通过在pom文件中添加starter依赖,Spring Boot的自动配置机制会根据classpath中的jar包来自动配置相应的Bean。这是因为每个starter中都包含了一个spring.factories文件,里面配置了需要自动配置的类。所以,我们通过查看pom文件中的starter依赖,可以间接知道哪些配置类会被导入。
面试官: 两种代理模式的区别是什么?
求职者: 两种常见的代理模式是静态代理和动态代理。静态代理是在编译时就已经确定代理类和真实对象的关系,每个代理类只能服务于一种类型的对象。而动态代理是在运行时动态生成代理类和绑定真实对象关系的,Java的动态代理通过反射机制实现,可以为多种类型的对象服务。
面试官: 为什么要选用Kafka和RocketMQ?
求职者: Kafka和RocketMQ都是高性能的消息中间件,选择它们的原因包括:
- 吞吐量高:两者都能处理高速的数据流。
- 可扩展性:支持集群模式,能够通过增加节点来水平扩展。
- 持久性和可靠性:都支持数据的持久化,保证消息不丢失。
- 顺序保证:尤其是RocketMQ,它提供了更细粒度的顺序消息保证。
- 使用场景:Kafka适合处理大量的日志数据流,RocketMQ在事务消息和顺序消息方面表现更好。
面试官: 用户30天自动登录是如何实现的?
求职者: 用户的30天自动登录通常通过设置带有过期时间的Cookie来实现。在用户登录时,服务器生成一个token,并将其存储在Cookie中,同时设置Cookie的Max-Age
为30天。每次用户访问网站时,浏览器都会发送这个Cookie,服务器通过验证Cookie中的token来识别用户身份。为了安全,token应该是加密的,服务器端可以结合用户信息和一些随机数据生成token,并在验证时进行解密验证。
面试官: 好的,我们来到了手撕代码环节。请找出环状链表的入口。
求职者:
public class Solution { public ListNode detectCycle(ListNode head) { ListNode slow = head, fast = head; while (true) { if (fast == null || fast.next == null) return null; fast = fast.next.next; slow = slow.next; if (fast == slow) break; } fast = head; while (fast != slow) { fast = fast.next; slow = slow.next; } return fast; } }
这段代码通过快慢指针找出环状链表的入口。首先,快慢指针从头开始遍历,快指针每次移动两步,慢指针每次移动一步。如果链表有环,那么快慢指针最终会相遇。相遇后,把快指针重新放到头节点,然后快慢指针都以每次一步的速度移动,当它们再次相遇时,相遇点就是环的入口。面试官: 非常好,代码实现很清晰。接下来,我们继续深入。在实现热部署时,热替换的类文件通常存放在哪里?如何避免内存泄露问题?
求职者: 热替换的类文件通常存放在特定的目录中,这个目录被自定义类加载器用于加载类文件。为了避免内存泄露,需要确保类加载器的实例可以被垃圾回收。这通常意味着需要断开类加载器与被它加载的类之间的所有引用关系。通常,热部署框架会负责管理这些类加载器的生命周期,确保在替换类后,旧的类加载器可以被垃圾回收。
面试官: RocketMQ中的事务状态回查机制是如何工作的?
求职者: RocketMQ事务状态回查机制是指,当事务消息发送后,如果没有收到生产者的二次确认消息,RocketMQ会定期向生产者发起回查请求,询问该事务消息最终的状态。生产者在接收到回查请求后,需要检查本地事务的状态,并向MQ返回事务的状态信息。根据返回的状态,MQ会决定是提交还是回滚之前的半消息。
面试官: 顺序消费时,如果消费者宕机或者处理速度跟不上生产者怎么办?
求职者: 如果消费者宕机,可以通过集群消费的方式来保证顺序消费。集群中的其他消费者可以接管宕机消费者的队列。如果处理速度跟不上,可以通过增加消费者数量来进行水平扩展,或者优化消费逻辑以提高处理速度。此外,可以设置适当的流控策略,避免生产者速度过快导致消费者负载过大。
面试官: 慢SQL除了使用EXPLAIN等工具外,还有哪些方法可以帮助优化?
求职者: 除了EXPLAIN,还可以使用以下方法来优化慢SQL:
- 索引优化:检查并优化索引,确保常用的查询条件都有对应的索引。
- 查询重写:优化查询语句的逻辑,减少不必要的表连接和复杂的子查询。
- 数据库分析器:使用数据库自带的性能分析工具分析慢查询,比如MySQL的
Performance Schema
。 - 数据库优化:根据数据库的性能监控结果调整数据库配置,如缓存大小、连接池等。
面试官: 在Spring中,如果两个Bean之间存在循环依赖,Spring是如何处理的?
求职者: Spring框架可以处理单例作用域Bean之间的循环依赖。当两个Bean相互依赖时,Spring容器使用三级缓存来解决这个问题。在创建Bean的过程中,Spring容器首先将Bean实例的早期引用放入到第三级缓存中。如果另一个Bean请求依赖该Bean时,容器就可以使用这个早期引用来解决循环依赖。通过这种方式,Spring能够在不破坏Bean生命周期的前提下解决循环依赖的问题。
面试官: 谈谈你对动态代理和静态代理在性能方面的理解。
求职者: 从性能角度来看,静态代理通常比动态代理快,因为它的代理类是在编译时就生成了,没有运行时的额外处理。而动态代理需要在运行时通过反射来创建代理对象和调用方法,这个过程会带来额外的性能开销。但是,随着JVM优化和现代硬件的发展,这种性能差异已经不像过去那么明显了。
面试官: 在选择消息队列时,如何权衡Kafka和RocketMQ的优缺点?
求职者: 选择消息队列时,需要根据具体的业务需求和系统架构来权衡。Kafka以其高吞吐量、可靠性和扩展性而著称,非常适合处理大规模的数据流和日志收集。而RocketMQ提供了更丰富的消息模型,包括顺序消息、定时消息和事务消息等,适合对消息可靠性和顺序有较高要求的场景。在考虑选择时,也要考虑到团队的技术栈和运维能力。
面试官: 让我们继续到手撕代码环节,除了找到环形链表的入口外,你还能否提供一种不同的方法?
求职者: 当然,另一种方法是使用哈希表来检测环形链表的入口。我们可以遍历链表,并将访问过的节点存入哈希表,一旦遇到哈希表中已存在的节点,就意味着这是环的入口。这种方法的空间复杂度为O(n),时间复杂度也为O(n)。
public class Solution { public ListNode detectCycle(ListNode head) { Set<ListNode> visited = new HashSet<>(); ListNode current = head; while (current != null) { if (visited.contains(current)) { return current; } visited.add(current); current = current.next; } return null; } }
面试官: 非常好,你提供了另一种有效的方法。现在我想问一个更具挑战性的问题,如果我们需要在不允许使用额外空间的情况下找到环形链表的入口,你会怎么做?
求职者: 如果不允许使用额外空间,那我会使用之前提到的快慢指针的方法。这种方法不需要额外的存储空间,只需两个指针即可。快指针每次移动两步,慢指针每次移动一步。如果存在环,则它们最终会在环内某点相遇。一旦相遇,把快指针重新置于链表起始处,然后快慢指针都以单步速度移动,它们相遇的点就是环的入口。这种方法的空间复杂度是O(1),符合不使用额外空间的要求。
面试官: 说到用户30天自动登录,你会如何在服务器端存储和管理这些登录状态信息?
求职者: 为了管理用户的登录状态,我们可以在服务器端使用一个会话存储。当用户登录时,服务器生成一个唯一的session token,并将其与用户的登录状态关联存储在会话存储中。然后把这个token发送到客户端存储在Cookie中。在用户之后的每次请求中,服务器都会检查Cookie中的token,从会话存储中查找对应的登录状态。为了安全,这个token应该是由用户信息和随机数据加密生成的,并且会话存储中的登录状态应当在30天后过期。
面试官: 现在,我们继续讨论消息队列。在大流量场景下,如何保证Kafka或RocketMQ的高性能和稳定性?
求职者: 在大流量场景下,保证Kafka或RocketMQ的高性能和稳定性可以通过以下方法:
- 合理规划分区:增加足够的分区数目,以平衡负载并提高并行处理能力。
- 优化消息大小:根据实际场景调整消息的大小,避免因为消息过大造成的性能问题。
- 监控和调优:实施实时监控,根据监控数据进行调优,比如调整队列长度、消费者数量、批处理大小等参数。
- 硬件优化:确保足够的网络带宽和磁盘IO能力,以支撑高吞吐量的数据传输和存储。
- 消息压缩:对消息进行压缩,减少网络传输和存储的负担。
- 使用独立的存储:将消息队列服务的存储与业务数据存储分离,避免资源争夺。
面试官: 最后,你觉得在实习期间,最重要的学习或成长点是什么?
求职者: 我认为在实习期间,最重要的学习点是快速适应和学习新技术或工具,并能够将其应用到实际项目中。而成长点则是提升解决复杂问题的能力,无论是技术问题还是团队协作方面的挑战。通过实习,我也学会了如何更有效地进行沟通和协作,这对我的职业生涯非常重要。