领星-Java开发岗技术面
首先先说明一下我的招聘性质是社招(1.5年工作经验),本人目前是在深圳,面试是在线下面试的,面试主要是两轮,一轮技术面,一轮HR面;
公司环境非常不错,感觉员工都很年轻,领导年纪稍微大一些(估摸着应该30+),整个面试流程大改30min+,难度也不是很大,主要是针对简历上写的来问一些场景题, eg:
1.阐述下项目架构,用到哪些技术,有哪些技术挑战和难点等等。
2.根据你的回答,问一些基础的东西,如MQ是用在哪些地方,怎么保证消息不丢失等等
(我回答:Mq在项目中主要是用在异步解耦第三方请求上....;保证消息一致是使用消息表【数据库】解释一下流程,但不是很对,可参考以下回答)
--------------------------------------------------
RocketMQ消息丢失防治
RocketMQ的消息想要确保不丢失,需要生产者、消费者以及Broker的共同努力,缺一不可。
首先在生产者端,消息的发送分为同步和异步两种,在同步发送消息的情况下,消息的发送会同步阻塞等待Broker返回结果,在Broker确认收到消息之后,生产者才会拿到SendResult。如果这个过程中发生异常,那么就说明消息发送可能失败了,就需要生产者进行重新发送消息。
但是Broker其实并不会立即把消息存储到磁盘上,而是先存储到内存中,内存存储成功之后,就返回给确认结果给生产者了。然后再通过异步刷盘的方式将内存中的数据存储到磁盘上。但是这个过程中,如果机器挂了,那么就可能会导致数据丢失。
如果想要保证消息不丢失,可以将消息保存机制修改为同步刷盘,这样,Broker会在同步请求中把数据保存在磁盘上,确保保存成功后再返回确认结果给生产者。
使用消息表
在使用消息队列(Message Queue,简称MQ)进行系统间解耦和异步通信时,消息的可靠性是一个非常重要的考虑因素。消息表是一种常见的策略,用于确保消息不会在传输过程中丢失。以下是使用消息表来保障MQ消息不丢失的步骤:
- 消息表设计:在数据库中创建一个消息表,用于记录即将发送到MQ的消息。消息表通常包含以下字段:id(主键)、message(消息内容)、status(消息状态,如待发送、已发送、发送失败等)、send_time(发送时间)、retry_count(重试次数)等。
- 消息写入:当业务逻辑需要发送消息到MQ时,首先将消息写入消息表,并标记状态为“待发送”。为了保证数据库事务的一致性,写入消息表的操作应该与业务逻辑操作在同一个事务中。
- 消息发送:使用一个单独的服务或线程定期扫描消息表,查找状态为“待发送”的消息。将这些消息发送到MQ,并在发送后更新消息表中的状态为“已发送”。如果发送失败,更新状态为“发送失败”,并增加retry_count。
- 消息确认:在消息发送到MQ后,等待MQ的发送确认(如果MQ支持)。如果收到确认,更新消息表中的状态为“已确认”。如果没有收到确认或确认失败,根据策略决定是否重试发送。
- 失败处理:对于“发送失败”的消息,可以设置一个重试机制,例如指数退避策略。如果达到最大重试次数仍然失败,可以记录错误日志,并通过告警通知运维人员。
- 消息清理:对于已经“已确认”的消息,可以设置一个清理策略,定期清理旧消息,以避免消息表无限增长。
- 高可用和容错:确保消息表所在的数据库是高可用的,可以通过主从复制、数据库集群等方式。对于消息发送服务,也应该考虑其高可用性,避免单点故障。
通过以上步骤,可以有效地保障MQ消息的可靠性。以下是一些额外的最佳实践:
- 幂等性:确保消息处理是幂等的,即重复处理同一消息不会产生副作用。
- 顺序性:如果消息处理需要保持顺序,消息表应该支持顺序性,例如通过分区键或时间戳。
- 监控和告警:实施监控,确保能够及时发现问题并进行处理。
使用消息表虽然可以保障消息不丢失,但也会引入额外的复杂性和可能的性能瓶颈。因此,在设计系统时,应根据业务需求和系统特性综合考虑。
-------------------------
3.根据我的简历,问了一些关于Redis的使用场景,在项目里怎么用的
(我的回答:在项目中,一些登录信息和一些配置、业务信息会用到Redis,举例使用配置信息怎么放到Redis的:首先配置信息属于是热点数据,一般查询的时候会从redis中读取,redis中没有再去数据库中查询,减少数据库的压力,在更新或者插入时先删除redis中的数据再去修改数据库,保证缓存数据的一致性[其实这里还可以拓展缓存穿透问题:比如使用布隆过滤器之类的进行判空操作],可以参考以下回答)
-------------------------
保证缓存一致性
为了保证Redis和数据库的数据一致性,肯定是要缓存和数据库双写了。
一般来说,在业内有3种比较常见的具体方案:
1、先更新数据库, 再删除缓存。
2、延迟双删:先删除缓存,再更新数据库,再删除一次缓存
3、cache-aside:更新数据库,基于 binlog 监听进行缓存删除
缓存击穿、缓存穿透、缓存雪崩
缓存击穿:是指当某一key的缓存过期时大并发量的请求同时访问此key,瞬间击穿缓存服务器直接访问数据库,让数据库处于负载的情况。
缓存穿透:是指缓存服务器中没有缓存数据,数据库中也没有符合条件的数据,导致业务系统每次都绕过缓存服务器查询下游的数据库,缓存服务器完全失去了其应用的作用。
缓存雪崩:是指当大量缓存同时过期或缓存服务宕机,所有请求的都直接访问数据库,造成数据库高负载,影响性能,甚至数据库宕机。
解决方案:
一、缓存击穿:
1.异步定时更新:在缓存处理上,同理,比如某一个热点数据的过期时间是1小时,那么每59分钟,通过定时任务去更新这个热点key,并重新设置其过期时间。
2.互斥锁:在缓存处理上,通常使用一个互斥锁来解决缓存击穿的问题。简单来说就是当Redis中根据key获得的value值为空时,先锁上,然后从数据库加载,加载完毕,释放锁。若其他线程也在请求该key时,发现获取锁失败,则先阻塞。
二、缓存穿透:
1.缓存空值:在缓存中,之所以会发生穿透,就是因为缓存没有对那些不存在的值得Key缓存下来,从而导致每次查询都要请求到数据库。那么我们就可以为这些key对应的值设置为null并放到缓存中,这样再出现查询这个key 的请求的时候,直接返回null即可 。
2.BloomFilter: 很多时候,缓存穿透是因为有很多恶意流量的请求,这些请求可能随机生成很多Key来请求查询,这些肯定在缓存和数据库中都没有,那就很容易导致缓存穿透。在缓存穿透防治上常用的技术是布隆过滤器(Bloom Filter)。布隆过滤器是一种比较巧妙的概率性数据结构,它可以告诉你数据一定不存在或可能存在,相比Map、Set、List等传统数据结构它占用内存少、结构更高效。对于缓存穿透,我们可以将查询的数据条件都哈希到一个足够大的布隆过滤器中,用户发送的请求会先被布隆过滤器拦截,一定不存在的数据就直接拦截返回了,从而避免下一步对数据库的压力。
三、缓存雪崩:
1.不同的过期时间:为了避免大量的缓存在同一时间过期,可以把不同的key过期时间设置成不同的, 并且通过定时刷新的方式更新过期时间。
2.集群:在缓存雪崩问题防治上面,一个比较典型的技术就是采用集群方式部署,使用集群可以避免服务单点故障。
-------------------------
4.问在生产环境中CPU飙高一般是什么情况,如何排查
(我的回答结合实际生产环境的场景列举:一般出现CPU飙高,说明服务器中肯定存在CPU的大量运算,可能出现:1.代码中存在死循环;2.代码结构不好,导致CPU一直处于高度占用状态;3.在使用多线程或者并发高时,出现等待或阻塞死锁的情况【这里反问了一下,死锁为什么会出现CPU飙高呢?我回回答了:因为Java的线程在尝试获取锁或者在进行子内存和主内存间的交换数据时导致CPU不停的在计算,可能会导致CPU飙高;排查呢可以使用排查呢可以使用JvisualVm(一些性能检测工具),查看内存使用情况以及CPU使用情况,看代码运行时间,大改就可以判断出哪块代码出现问题,我感觉不是很对也不完整,可以参考以下回答)
---
在Java生产环境中遇到CPU使用率飙升的情况,通常是由多种原因导致的。下面是一些常见的原因以及排查方法:
常见原因
- 线程问题:死锁、大量线程竞争、线程长时间运行。
- 内存问题:频繁的垃圾回收(GC),内存泄露。
- 代码效率问题:存在效率低下的算法或资源使用不当。
- 外部资源等待:数据库、网络或其他系统响应缓慢。
- 系统问题:操作系统或硬件问题。
排查步骤
- 使用TOP或任务管理器:查看CPU使用率高的进程,确认是Java进程导致的。
- 线程 Dump:使用jstack命令获取Java进程的线程堆栈信息。分析线程状态,查看是否有大量线程处于运行状态,是否有死锁。
- 分析GC日志:启用GC日志,分析日志文件,看是否频繁进行Full GC。使用工具如VisualVM, GCViewer等来分析GC日志。
- CPU Profile:使用VisualVM, JProfiler等工具进行CPU抽样或跟踪,查看哪些方法占用CPU时间最长。
- 查看系统资源:检查磁盘I/O、网络使用情况,看是否有异常。检查操作系统日志,看是否有硬件错误或资源瓶颈。
- 代码审查:分析可能存在性能问题的代码段,如循环、递归调用、同步块等。使用代码分析工具,如FindBugs, Checkstyle等,帮助发现潜在问题。
- 外部系统检查:如果应用依赖外部系统(如数据库),检查这些系统的性能和日志。
- 复现问题:如果可能,尝试在测试环境中复现问题,进行更深入的分析。
实用命令
//查看Java进程: ps -ef | grep java //获取线程堆栈: jstack -l <pid> > thread_dump.txt //查看GC情况: jstat -gcutil <pid> 1000 //分析堆信息: jmap -heap <pid> 或 jmap -histo <pid>
通过上述步骤,通常能够定位到导致CPU使用率飙升的原因。在处理问题时,建议记录每一步的操作和观察到的现象,这有助于问题的定位和解决。
---
5.根据简历上写的SQL优化提问,如何优化SQL,你在项目中是怎么做的
(回答:一般SQL优化主要是看索引是否失效,减少SQL的复杂度等等,列举一些实际场景如:SQL中联表查出现大表套小表,超过3张表一起查询等等,优化方案如:如何拆分SQL,优化索引,使用java代码对查询做封装和优化等等,可以参考以下回答)
-------------------------
SQL优化策略:
- 使用索引:为经常用于查询条件的列创建索引。避免在索引列上使用函数或计算,这会导致索引失效。
- 选择合适的索引类型:根据查询需求选择合适的索引类型,如B-Tree、HASH、FULLTEXT等。
- 优化查询语句:避免使用SELECT *,只查询需要的列。使用JOIN代替子查询,特别是在WHERE子句中。使用LIMIT限制返回结果的数量,尤其是在分页查询中。
- 减少数据量:使用WHERE子句过滤不必要的行。使用GROUP BY和HAVING子句进行数据聚合。
- 优化数据模型:正规化数据模型以减少数据冗余。在某些情况下,适当的反规范化可以提高查询性能。
- 使用查询缓存:如果数据库支持查询缓存,确保它被合理配置和使用。
- 分析执行计划:使用EXPLAIN或类似的命令分析查询的执行计划,查找潜在的性能瓶颈。
项目中实施SQL优化的步骤:
- 性能监控:使用数据库的性能监控工具(如MySQL的Performance Schema、PostgreSQL的pg_stat_statements等)来监控慢查询。
- 慢查询日志分析:启用慢查询日志,定期分析慢查询,找出性能瓶颈。
- 建立SQL审核流程:在代码提交前,对SQL查询进行代码审查,确保遵循最佳实践。
- 定期优化:定期对数据库进行维护,如更新统计信息、重建索引等。
- 测试和基准测试:在部署前对SQL查询进行测试,使用基准测试工具(如sysbench、Apache JMeter等)来评估性能。
- 持续改进:根据监控结果和业务需求,持续优化SQL查询和数据模型。
- 培训和教育:对开发人员进行数据库性能优化方面的培训,提高他们的意识和技能。
-------------------------
6.根据简历上使用的多线程,线程池进行提问,如怎么设置的核心线程数,线程存活时间等等,你是怎么设置这些参数的,有用到线程池的拦截策略吗?是什么?
(我的回答:在项目中用到的线程数量大约在十多个左右,由于大多线程主要是在做数据库的操作,少部分数数据组织操作,主要是IO密集型和CPU密集型,但是线程数量也不是很多,加上服务器的CPU配置也还能接受,所以主要考虑的是有多少线程就设置多大的线程池; 拦截策略主要说了在项目中对一些参数的校验,如果不通过则不允许执行等等,可以参考以下回答)
-------------------------
线程池的核心参数:
- acc : 获取调用上下文
- corePoolSize: 核心线程数量
- maximumPoolSize: 最大的线程数量
- workQueue:多余任务等待队列
- keepAliveTime:非核心线程空闲时间
- threadFactory: 创建线程的工厂
- handler:线程池拒绝策略
影响线程数的因素:
CPU 核数
- 多核处理器:理想的线程数肯定取决于处理器的核心数。在理想情况下,每个核心运行一个线程是最高效的。
- 超线程技术(如Intel的Hyper-Threading):目前,很多CPU都采用了超线程技术,也就是利用特殊的硬件指令,把两个逻辑内核模拟成两个物理芯片,让单个处理器都能使用线程级并行计算。所以我们经常可以看到"4核8线程的CPU",也就是物理内核有4个,逻辑内核有8个。如果CPU支持超线程技术,可以为每个核心分配更多的线程,因为超线程可以提高CPU资源的利用率。
应用类型
- CPU密集型:对于CPU密集型的任务(如计算密集型任务),线程数最好设置为核心数的1到1.5倍,因为这些任务主要消耗CPU资源。
- I/O密集型:如果任务涉及大量的等待或阻塞(如数据库操作、文件操作、网络操作等),则可以配置更多的线程,比如2倍,因为线程在等待时CPU可以切换去处理其他任务。
JVM和系统资源
- 内存限制:每个线程都会占用一定的内存(如栈空间)。如果创建过多线程,可能会消耗大量内存,甚至导致内存溢出。
- 操作系统限制:操作系统对进程可创建的线程数通常有限制,过多的线程可能导致系统性能下降。
其他考虑
- RT要求:如果系统对响应时间有严格要求,可能需要更多线程来减少处理延迟。
- 任务特性:不同的任务可能对线程数的需求不同。长时间运行的任务与短时任务,同步任务与异步任务,都需要考虑不同的线程配置。
公式
根据我们前面提到的这些影响因素来看,主要和CPU核心数、以及应用类型等有关。简单一点的话,可以根据应用类型来套用以下公式(但是,实际应用起来,也不要死守着公式不放,公式只是可以当作参考):
- 如果是CPU密集型应用,则线程池大小设置为N+1
- 如果是IO密集型应用,则线程池大小设置为2N+1
上面的N为CPU总核数
但是,上面的公式中,前提要求是知道你的应用是IO密集型还是CPU密集型,那么,到底怎么样算IO密集,怎么样又算CPU密集呢?一个应用就真的能明确的定位出来是CPU密集还是IO密集吗?
所以,还有另外一个公式:
等待时间是指线程在执行过程中花费在等待外部操作完成的时间。这些外部操作通常包括I/O操作(如读写文件、数据库操作、网络请求等)和其他资源的同步等待(如等待锁的释放)。在等待时间内,线程通常不占用CPU资源,因为它在等待某个事件或资源可用。
计算时间是指线程实际进行计算处理的时间,即线程在CPU上执行操作的时间。计算时间通常指的是CPU密集型操作,如数学计算、数据处理等。
在这个公式中,"等待时间 / 计算时间"的比例是一个关键因素,它帮助决定合适的线程数量以平衡CPU利用和等待效率:
- I/O密集型任务:对于I/O密集型任务,等待时间通常远大于计算时间,这意味着可以分配更多的线程。当一个线程在等待时(如等待网络响应),CPU可以切换到另一个线程进行计算,从而提高CPU利用率。
- CPU密集型任务:对于CPU密集型任务,计算时间通常占主导地位。在这种情况下,增加线程数可能不会提高性能,因为大部分时间都在进行CPU计算,线程之间的上下文切换可能导致性能下降。
-------------------------
7.最后反问面试官一些问题
(问了一些面试官公司项目架构以及使用到的技术栈这块的问题;问了需求开发流程这一块的问题;问了整个项目管理上是否为客户提供多个分支的问题,以及Git代码管理方式的问题)
总结:
面试的整体感受:
整个面试流程感觉面试官也比较亲和,主要就是根据简历上写的和项目中的东西来提问,我觉得在面试过程中可添加一些自己的想法,对一些问题的探讨,比如举例在项目中实际遇到的一些场景,怎么解决的,思路是什么,怎么做的等等;
面试之前也在牛客上看了几篇面试的帖子,不知道是不是各位同志是校招的原因,还是项目不够多,面试官没得问,直接上来就是八股拷打(本人八股也不是很好,只能记个大概Q_Q),导致我在去面试的时候慌得一批,但是提前去到公司之后,在那呆了差不多半个钟把状态调整了一下,面试的时候稍微好一些;
我觉得八股背肯定是要背的,但是你得理解着背,套到实际当中去,这样即使想不起来,也能通过某个案例说出个大概出来;
最后分享一下我的一些想法:
1.遇到不会的提问时不会不要硬答,说错了反而印象不好;
2.有一点把握的问题,能答多少答多少,说一说自己的看法;
3.保持良好的心态,面试的时候不要紧张,当和朋友聊天一样就行;
ps:上述的以下参考回答来源于 智谱清言、《Java 8 Guwen D》---Hollis,如有涉及侵权请私信删除
再ps:有可能回狂问八股,我看其他帖子有写,万一有的面试官追着问八股就完玩了,还是要看一些的,具体可以参考@想逆袭好楠、@offer比饭好吃给我offer吃吧 这两位楼主的帖子(楼主好人一生平安!!Respect!!!)
2024/9/18HR面:
主要问了一些上家公司的情况和一些基本信息,为什么离职,对工作的一些态度,对领星业务的了解度等等,最后是反问。
--------------------------------------------------
领星业务:
1.在以亚马逊为主的跨境电商ERP中,领星ERP市占率第一,50万+跨境卖家的一致选择,支持20+主流跨境平台,助力卖家全球出海。
2.
具体可参考领星官方微信公众号,以及官网https://www.lingxing.com/
2024/9/20更新:
没通过面试,HR面结束后就挂了,也不知道什么地方有问题,有可能是项目架构的原因(但是技术面听HR反馈感觉蛮好的),如果有要面试的同志可以稍微打磨一下自己的项目点,也有可能是HR面时表现出对上一家公司的稍微不满(比如加班,福利待遇之类的),也有可能被横向对比了,总之再接再励吧!
-------HR反馈是与岗位匹配度不够,还是项目问题诶
#Java面试##领星网络##面经Java#面试经验(八股去si,再也不想背八股了)