25年春招:携程java开发一面
base上海,旅游事业部,java开发,50分钟左右,比较简单,基本都答上来了。但是面试官的风格是比较压力的,一直在询问我为什么能做得比别人更好hhh,是春招面试中对于基本情况问得最多的一个hhh。
本人bg:双非,非科班女生,三段实习,分别是小、中、大厂,第一段和第二段都为java开发,第三段为python开发。所以很多面试都重点拷打了大厂实习内容。
1.基本情况
1.1.自我介绍
1.2.三份实习中,哪一份印象最深刻
1.3.介绍一下在腾讯做的事情
1.4.你简历里面其他的工作地点都是在深圳是吧?上海没问题吗?
1.5.学医学相关为什么要学计算机呢?
讲了大一下参加的一个项目,AI+医疗。从此对软件开发感兴趣。大二上去选修计软的课程,大二下被计软老师推荐去第一份实习。
1.6.你怎么用一年的时间去和别人四年的时间比?
回答的是,其实也不是相当于和别人四年的时间比,当时自己是大二,其实也就是相当于一个水平还行的大二科班生。
1.7.那你觉得老师推荐你去第一段实习的理由是什么呢?
原本回答的是在课堂上的表现不错,老师可能觉得我工程能力不错。结果后面感觉面试官不信,一直在问我具体怎么体现我的工程能力。现在想一下,可能回答主观能动性这方面会更好,老师觉得比起按部就班的科班那边的学生,我这种积极主动地态度可能更适合到实习岗位去进一步地锻炼和成长。
1.8.你觉得老师推荐你去第一段实习,是运气好被老师看到,还是主要是工程能力好,体现在哪个点?
回答了工程能力比较好这一点。
1.9.什么样的工程能力比较好?工程的一个能力好的评价标准是什么?
我说老师不止是课程的老师,还作为我们参加比赛的指导老师,计软比赛譬如计设、服创都是真实的企业下发需求给到学生外包来做,在这个过程中体现了我的工程能力比较好。
1.10.比如说你去做了这样的一个事情,别人也做了这样的事情,为什么你做的事情比别人做的好呢?
别人没有达到按时交付的水平,除此之外我还提到在整个项目的开发前期,我有积极地提出系统设计相关的想法。譬如数据库设计相关的。
1.11.请列举2到3条数据库设计相关意见的亮点
回答是这个项目是大二的时候做的了,已经记不得了。但是有考虑一些大方向的意见,譬如是不是需要分库分表,是不是需要索引优化,同时可以参照阿里的开发手册之类的行业标准去进行设计。
这里其实可以回答一些通用的点,像是针对用户的手机号和邮箱字段创建了唯一索引,这样可以确保数据的唯一性,同时加快根据手机号或邮箱查询用户信息的速度。
1.12.你觉得对于一个研发来说,它最重要的特质是什么呢?
我觉得是按时交付的能力,如果在实施的过程中遇到了困难或者需要技术支持,需要及时上报和沟通,总之不能让问题卡在那。最后一定要在排期内交付到成果。
1.13.那你未来的话,你是想搞哪一部分的呢?是想做后端研发,还是做python的数据处理之类的呢?
回答了只要是开发都行。语言本身不是问题。
1.14.对于后端研发用到的一些中间件,你了解多少?
回答了消息队列RabbitMQ、Kafka。缓存Redis、服务注册与发现的Canal和Eureka。
2.实习相关
2.1.实习中为什么要用canal和FlinkCDC?
之前使用 Canal 时,它缺少整个数据快照的能力,而实习的一些业务场景比较依赖数据快照。通过引入 Flink CDC 这种流式计算框架,能够实时捕捉数据库的变更并将其流式处理,在一定程度上弥补了数据快照能力的不足。Flink CDC 可以持续监听数据库的变化,将变更数据以流的形式输出,这样就可以根据业务需求构建出某个时间点的数据快照,满足我们业务场景对于数据快照的依赖。
2.2.你的主从架构是用FlinkCDC实现的,还是用数据库自己的主从架构?
用canal实现的,canal的底层实际上也是模仿mysql的主从架构原理。它底层其实就是将自己伪装成mysql的一个从节点,然后去监听binlog的变化嘛。
2.3.假设你们流式计算依赖两个数据源,第一个数据源的数据到了,但是第二个数据源产生了积压,会怎么解决?譬如,当单价数据已经到了,但是数量的数据还没有到,导致算不了总价,这个问题以前有遇到过吗,怎么解决?
难得的很有含金量的一问……
可以对第一个数据源的数据发送速率进行控制,避免数据过度积压。例如使用令牌桶算法,根据第二个数据源的处理能力动态调整令牌发放速率,从而控制数据流入。
异步处理与缓存:引入异步处理机制和缓存,将第一个数据源的数据先缓存起来,待第二个数据源恢复处理能力后再进行处理。例如使用Redis作为缓存,将数据临时存储在Redis中。
可以采用水印(Watermark)机制来处理数据的乱序和延迟问题。水印是一种时间戳,用于表示在这个时间戳之前的数据都已经到达。通过设置合适的水印,可以在一定程度上保证数据的一致性。
可以使用滑动窗口或滚动窗口来处理这种情况。例如,我们可以设置一个固定大小的滚动窗口,当单价数据到达时,将其存储在窗口中,等待数量数据在窗口时间内到达。如果数量数据在窗口时间内到达,就可以计算总价;如果窗口时间结束,数量数据仍未到达,就可以根据业务需求进行特殊处理,比如记录日志或者发送告警。
2.4.Flink CDC的窗口概念是指它的等待时间吗?(基于上一问的)
在Flink CDC里,窗口概念并不单纯指等待时间。窗口是一种对无限流进行有限分组的手段,可将流数据切割成有限大小的“桶”,方便进行各类计算。
窗口类型
Flink CDC中有多种窗口类型,不同类型的窗口含义不同:
- 时间窗口(Time Window):
- 滚动时间窗口(Tumbling Time Window):将数据按固定的时间长度进行切分,窗口之间没有重叠。例如,设置一个5分钟的滚动时间窗口,每5分钟就会创建一个新的窗口,对该窗口内的数据进行计算。
- 滑动时间窗口(Sliding Time Window):窗口有固定的大小,并且会以固定的时间间隔滑动。比如,窗口大小为10分钟,滑动间隔为5分钟,那么每5分钟就会计算一次最近10分钟内的数据。
- 会话窗口(Session Window):由一系列没有固定时长、由不活动间隙分隔开的窗口组成。如果一段时间内没有新的数据到来,就会关闭当前窗口,开启新的窗口。
- 计数窗口(Count Window):
- 滚动计数窗口(Tumbling Count Window):根据元素的数量来划分窗口,当窗口内的元素数量达到设定值时,就会触发计算。例如,设置窗口大小为100,那么每100个元素就会形成一个窗口进行计算。
- 滑动计数窗口(Sliding Count Window):同样基于元素数量划分窗口,但窗口会以固定的元素数量间隔滑动。例如,窗口大小为100,滑动间隔为10,那么每增加10个元素就会计算一次最近100个元素的数据。
等待时间
等待时间通常和水位线(Watermark)相关,它是处理乱序数据的一种机制。水位线是一个时间戳,用来表示在这个时间戳之前的数据都已经到达。设置等待时间可以让系统在一定时间内等待迟到的数据,避免过早地触发窗口计算,从而保证计算结果的准确性。
3.八股相关
3.1.线程池的相关参数有哪些?(python)
主要是询问腾讯相关的实习内容时提到了线程优化,所以提到了线程池。
core_pool_size
(核心线程数):核心线程数是线程池始终保持的线程数量。可以将其设置为 10,这样线程池会一直维持 10 个线程处于活动状态,随时准备执行任务。maximum_pool_size
(最大线程数):最大线程数是线程池允许创建的最大线程数量。如果将线程数固定为 10,那么maximum_pool_size
也可以设置为 10。keep_alive_time
(线程空闲存活时间):当线程池中的线程数量超过核心线程数时,多余的空闲线程在经过keep_alive_time
时间后会被销毁。可以根据具体情况设置一个合适的值,例如 60 秒。work_queue
(工作队列):工作队列用于存储待执行的任务。可以选择不同类型的队列,如ArrayBlockingQueue
、LinkedBlockingQueue
等。队列的大小也需要根据实际情况进行调整,以避免任务堆积过多或过少。thread_factory
(线程工厂):线程工厂用于创建线程。可以自定义线程工厂,为线程设置名称、优先级等属性,方便调试和管理。rejected_execution_handler
(拒绝策略):当工作队列已满且线程池中的线程数量达到最大线程数时,新提交的任务会被拒绝。可以选择不同的拒绝策略,如AbortPolicy
(直接抛出异常)、CallerRunsPolicy
(由调用线程执行任务)等。
3.1.索引优化主要是写入还是查询
查询。
3.2.刚才说的索引优化,写入时怎么做可以优化;
写入优化用的就是全量和增量写入,batch_insert和batch_update。
索引优化的是查询,不是写入。
面试官可能没听清我的回答。
3.3.为什么使用索引会让查询变快?
mysql为例,索引本身就是一个帮助mysql查找数据的数据结构,它底层是一个B+树,因为它的非叶子节点是不存储数据的,只存储索引列的值以及指向子节点的指针,只有叶子节点才存储数据的主键,而且叶子节点之间会通过双向链表互相连接,所以对于一些区间查找、或者说范围查找,它读的时候会很快。
其次就是当查询的所有列都包含在索引中时,数据库可以直接从索引中获取所需的数据,而不需要访问实际的数据行。这种情况称为覆盖索引。
最后就是创建索引的话,在数据库中也会根据索引创建的顺序排序,那么数据至少是有局部有序性的,所以一般用索引会比不用索引要快。
而且索引本身是存储在内存中的,那我们数据库接到了查询请求时,首先通过内存中的索引结果查找满足条件的记录位置,然后再从磁盘中读取这些记录,所以一般用索引会比不用索引要快。
3.4.联合索引和单个索引的区别是什么?
3.5.索引的叶子节点存的是数据,还是指针和数据?
聚簇索引的叶子节点存储的是完整的数据记录。
非聚簇索引的叶子节点存储的是索引列的值以及对应的主键值。
3.6.mysql的隔离级别,你知道有哪些吗?什么是脏读?
3.7.开发中python和java的差别在哪里?
Java通常比Python快。python可能存在性能问题。
3.8.python为什么会存在性能问题?
- 全局解释器锁(GIL):Python有全局解释器锁(GIL),这是一个非常“粗暴”的全局锁。它使得同一时刻Python解释器只能执行一个线程,所以Python无法做到真正意义上的并行。对于CPU密集型任务,多个线程无法同时利用多核CPU的优势,性能表现不佳;但对于I/O密集型任务,在等待I/O操作时线程可以释放GIL,让其他线程执行,所以表现还不错。
- 解释性语言特性:Python是解释性语言,执行时逐行解释,相比编译型语言,会有额外的解释时间开销,这也可能导致性能问题。
3.9.java中的多态是一个什么概念?
多态允许不同类的对象对同一消息做出不同响应。多态主要通过以下三种方式实现:
-
方法重载
方法重载指的是在一个类中,可以定义多个同名但参数列表不同的方法。编译器会根据调用方法时传入的参数类型、数量和顺序来决定具体调用哪个方法。方法的返回类型和访问修饰符可以不同,但仅返回类型不同不能构成方法重载。 -
方法重写
方法重写发生在子类和父类之间,子类可以重新定义父类中已有的方法。重写方法的方法名、参数列表和返回类型必须与父类被重写的方法一致,并且访问权限不能比父类中被重写的方法更严格。 -
接口实现
接口是一种抽象类型,它只包含抽象方法的定义。一个类可以实现一个或多个接口,并实现接口中定义的所有方法。通过接口,不同的类可以实现相同的行为,从而实现多态。
3.10.Java中重载跟重写的区别是什么?
- 重载:指在一个类中,可以定义多个同名但参数列表不同(参数类型、参数个数或参数顺序不同)的方法。Java编译器会根据调用方法时传入的实际参数来决定具体调用哪个方法。
- 重写:也叫覆盖,是指子类对父类中允许访问的方法进行重新实现。重写方法的方法名、参数列表和返回类型必须与父类被重写的方法一致。
3.11.单例模式知道吗?写一个单例模式
一开始写了饿汉式单例,然后面试官让写懒汉式单例,最后又问我懒汉式是不是线程安全的,我就说不是,然后又给他写了线程安全的版本。
1. 饿汉式单例
这种方式在类加载时就创建了单例实例,因此是线程安全的。
public class EagerSingleton { // 在类加载时就创建单例实例 private static final EagerSingleton INSTANCE = new EagerSingleton(); // 私有构造函数,防止外部实例化 private EagerSingleton() {} // 提供全局访问点 public static EagerSingleton getInstance() { return INSTANCE; }
}
2. 懒汉式单例(非线程安全)
这种方式在第一次调用 getInstance()
方法时才创建单例实例,但在多线程环境下可能会创建多个实例。
public class LazySingleton { private static LazySingleton instance; private LazySingleton() {} public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; }
}
3. 懒汉式单例(线程安全)
为了保证在多线程环境下也能正常工作,可以使用 synchronized
关键字来实现线程安全。
public class ThreadSafeLazySingleton { private static ThreadSafeLazySingleton instance; private ThreadSafeLazySingleton() {} // 使用 synchronized 关键字保证线程安全 public static synchronized ThreadSafeLazySingleton getInstance() { if (instance == null) { instance = new ThreadSafeLazySingleton(); } return instance; }
}