【硬核】面试官问:你参与的项目有什么亮点?怎么回答
时间:2022年08月09日
/来源:网络
/编辑:佚名
面试官拿到我们的简历,一般会关注两块内容,一块是专业技能,另一块是项目经历。
简单的个人介绍后,一般会先问些偏基础的技术问题,热热身。当然也有很多面试官上来就顺着项目问。根据你介绍项目的过程细节,穿插设置一系列的技术问题。
面试官一般会关注一些有挑战性的方案设计、解决了什么复杂难题,简单一句话,就是你的项目一定要有亮点。
那么,什么是亮点?我们的项目如何积累这些亮点?
下面我们会介绍项目中一些高频亮点设计,我们平时做项目,设计技术方案时也可以多用用,积累些实战经验 。
面对复杂业务,架构设计有什么通用思路?
答案:业务理解转化能力、思维抽象能力、软件建模能力、高并发、高性能、高可用的分布式系统架构设计能力。
1、“拆分” ,降低架构复杂度。
2、认知抽象,架构模式有通用性
3、平台化、中台化系统衍化建设。
详细内容请查看 人人都是架构师???谈何容易!!
谈谈对 DDD 的理解?
答案:通过实体、值对象、聚合根、领域服务、领域对象、限界上下文、资源库,指导微服务落地,将一个大的复杂业务域拆分成若干业务子域。定义领域模型(包含数据、行为),相似业务聚合。
更多内容,参考 DDD是如何解决复杂业务扩展问题?
画下项目的业务架构图?
答案:要对简历的项目非常熟悉,不只是自己负责的那部分,观整体通细节。
很多同学面试前没有系统化梳理,面试时现组织语言,给人感觉思路凌乱、条理不够清晰。
面试前可以先自己画画系统架构,理清其中的依赖关系。同时,提炼核心有挑战的技术难点,面试时,重点介绍其来龙去脉。
项目中用过哪些设计模式?
答案:工厂、装饰、克隆、代理、适配器、观察者、策略、模板、单例、责任链、门面等23种软件设计模式,这是软件开发的基本功,每一种设计模式都要非常熟悉。否则很难写出扩展性很高的代码。
之前写过三篇文章,每一种模式都有详细介绍:
解决复杂业务架构,软件设计模式系列(第一期)
解决复杂业务架构,软件设计模式系列(第二期)
解决复杂业务架构,软件设计模式系列(第三期)
如何提升系统的吞吐量?
答案:围绕系统的 “三高原则”,高并发、高性能、高可用,三个方向展开。
更多内容,参考 【高并发、高性能、高可用】系统设计经验
面对海量数据,什么是水平拆分、垂直拆分?
答案:
1、垂直拆分可以分为业务维度、技术维度。
业务维度:结合DDD领域驱动设计,将一个大的业务域拆分为若干业务子域,比如电商可以拆分为商户、商品、库存、权限、会员、营销、交易、支付、履约、订单、结算、仓储、物流、财务等。
技术维度:将一个有很多字段的表,按字段的大小、使用频率等特点,拆分为多张表。
2、水平拆分
由于单台机器的性能有限,无法支撑海量数据存储。我们引入逻辑表概念,采用集群模式,将一张逻辑表拆分成多张物理表分散存储在不同服务器,通过分表键路由,比如:时间、区域、用户id等。
特点:虽然有多张表,但每张表的表结构都是一样的,区别是数据不一样。所有表的数据合并起来才是这个业务表的完整数据。
画外音:数据量大,就分表;并发高,就分库
更多内容,参考 单台 MySQL 支撑不了这么多的并发请求,我们该怎么办?
分库分表时,全局性主键ID有哪些生成方案?
答案:
1、UUID,生成的是 32 位的字符串,虽然可以做到全局唯一性,但我们一般推荐使用整型。
2、SNOWFLAKE,雪花算法,生成一个 64 位的Long类型数据。组成结构:正数位(占1位)+ 时间戳(占41位)+ 工作机器id(10位)+ 序列号部分(12位)
3、数据库号段模式,对不同的业务类型定义初始值和步长,业务系统引入SDK,本地缓存预申请一定数据量的主键ID值,满足一定的并发要求。
4、TinyID,滴滴的开源框架
5、Redis 的 incr 命令
6、Leaf,美团的开源框架
7、Uidgenerator,百度的开源框架
不停机情况下,数据库扩容要怎么做?
答案:
1、首先,创建好新库,应用端修改代码,采用双写机制,将数据也同步一份到新库中
2、数据迁移,将老库中的数据迁移到新库中
3、对新老数据库做数据核对,对不一致的数据做定向同步
4、开关打开,读操作切换到新库,通过线上真实流量验证数据的准确性。
5、经过一段时间的线上验证后,如果没问题,将老库的写操作下线
缓存更新常用策略?
答案:
Cache aside,通常会先更新数据库,然后再删除缓存,为了兜底还会设置缓存时间。
Read/Write through, 一般是由一个 Cache Provider 对外提供读写操作,应用程序不用感知操作的是缓存还是数据库。
Write behind,延迟写入,Cache Provider 每隔一段时间会批量写入数据库,大大提升写的效率。像操作系统的page cache也是类似机制。
写操作时,缓存和数据库如何保持一致性?
答案:常见的方案有以下几个
1、先更新数据库,再更新缓存。
由于是两个操作,如果多线程并发情况下,很难保证线程之间的顺序,可能导致缓存的是旧数据。一般不推荐这种方案
2、先删除缓存,再更新数据库
两个操作,如果中间执行过程中,缓存被删除,恰好有其他线程来读数据,缓存会被重新预热旧的数据。一般不推荐这种方案
3、先更新数据库,再删缓存。
可能会导致无效删除,但是删除本身具有幂等性。该方案成本最低,一般推荐该方案
4、借助消息中间件,对缓存的数据强制维护,但也只能保证最终一致性。
5、不管最终采用哪种方案,Key都要设置一个过期时间,借助自身的淘汰机制保证数据的更新。
如何减少接口的响应时间?
答案:
1、减少接口的处理内容,将非核心逻辑移除,采用MQ解耦,异步化处理。
2、接口内部引入本地缓存和分布式缓存,提升数据加载速度
3、引入池化技术(线程池、进程池、对象池、内存池、连接池、协程池)
事务有哪些特性?
答案:
1、原子性(Atomicity),原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。
2、持久性(Durability),事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
3、一致性(Consistency),事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
4、隔离性(Isolation),一个事务不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
分布式事务,有哪些解决方案?
答案:
1、执行业务逻辑前,先插入流水任务,如果中间过程调用外部RPC接口服务或者本地数据库操作失败时,流水任务会被定时调度任务周期性触发、重试,直到成功。前提条件,所有接口服务都要实现幂等。当执行成功时,流水记录会被删除。
2、基于事务消息,能保证本地数据库一定成功,其他业务系统做为订阅方,通过重试机制保证最终一致性,但要注意接口幂等。
3、两阶段提交
4、引入业务状态机,初始为 init 状态(对外不可见),待所有依赖的RPC接口全部调用一遍,待所有接口的数据全部初始化,然后开始第二轮调用,将状态置为对外可见。当然,此阶段可能会部分调用失败,需要多次重试
5、TCC 模式。Try:尝试待执行的业务;Confirm:执行业务;Cancel:回滚执行的业务
6、Seata 阿里开源框架,把分布式事务定义为由若干本地事务(分支)组成的全局事务。被全局事务管理的全部分支,将在协调器的协调下,保证一起成功或一起回滚。
上面列举的方案细节,可以查看 如何解决分布式事务
如何预防死锁?
答案:互斥条件、请求和保持条件、环路等待、不可剥夺。只要打破其他一个条件即可。
什么是乐观锁?什么是悲观锁?
答案:
乐观锁:分为三个阶段:数据读取、写入校验、数据写入。在数据进行提交更新时,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回错误信息,让用户决定如何去做,fail-fast机制。
悲观锁:对数据的修改持保守态度,在整个数据处理过程中,将数据处于锁定状态,期间其他用户长时间无法访问,影响程序的并发访问性。
常见的限流策略有哪些?
答案:
1、计数器限流
2、滑动窗口限流
3、漏桶
4、令牌桶
更多内容,参考 高可用系统,必须修炼《流控》这把利剑!
接口幂等性,有哪些方案?
答案:Select 查询天然支持幂等,而写操作 多次执行可能会导致数据错误,下面简单列举常见的解决方案:
insert、update 之前,先select 查询校验
表中加唯一约束
加悲观锁 , select * from order where order_id= 100000 for update; 对操作行锁定。
加乐观锁,表中增加一个attribute_cc自增字段,借助CAS机制控制并发
有些表不适合添加唯一约束,可以单独建一张防重表,在防重表插入成功,在操作其它业务表
引入状态机, update order set status = "wait_seller_send_good" where id=1 and status = "wait_buyer_pay";
采用 分布式锁,第一次请求可以成功加锁,后续请求加锁失败,认为是重复请求。
token机制,client 首先请求获取token,提交时除了业务参数外还要带上这个token,server端会对这个token核销,只能核销一次。如果 server 查询不到token,则认为是重复请求。
定时任务,有哪些实现方案?
Time,JDK自带的定时器类,与 TimerTask 一起配合使用
ScheduledExecutorService,位于java.util.concurrent并发包下,基于多线程。
spring task,通过 @Scheduled 注解定义定时规则
quartz,开源框架
xxl-job,大众点评开源的一个分布式任务调度平台
elastic-job,当当网开源的弹性分布式任务调度系统,采用zookeeper实现分布式协调,实现任务高可用以及分片,适用于高并发等复杂业务场景。
经历过的项目,有没有遇到过什么线上问题?
答案:结合自己的工作经历来讲,一个程序员肯定都经历过线上救火的场景。那么找些有技术难度的整理下,如:
没有正确使用Redis的结构,缓存对象的粒度较大,高并发流量导致带宽不足,网络拥堵,连接无法及时释放,客户端报获取连接超时,进而引发整个系统的雪崩。
Pulsar系统告警,消息堆积。经排查,某个业务需求改动,SQl没有命中索引,导致接口处理时间增加数倍,从而降低了消费速度,进而消息堆积。
线上系统报OOM问题、内存泄露问题
以上,只是个举个例子。不同的业务,不同的系统,不同的人,经历的线上问题也千奇百怪,可以多收集些。
简单的个人介绍后,一般会先问些偏基础的技术问题,热热身。当然也有很多面试官上来就顺着项目问。根据你介绍项目的过程细节,穿插设置一系列的技术问题。
面试官一般会关注一些有挑战性的方案设计、解决了什么复杂难题,简单一句话,就是你的项目一定要有亮点。
那么,什么是亮点?我们的项目如何积累这些亮点?
下面我们会介绍项目中一些高频亮点设计,我们平时做项目,设计技术方案时也可以多用用,积累些实战经验 。
面对复杂业务,架构设计有什么通用思路?
答案:业务理解转化能力、思维抽象能力、软件建模能力、高并发、高性能、高可用的分布式系统架构设计能力。
1、“拆分” ,降低架构复杂度。
2、认知抽象,架构模式有通用性
3、平台化、中台化系统衍化建设。
详细内容请查看 人人都是架构师???谈何容易!!
谈谈对 DDD 的理解?
答案:通过实体、值对象、聚合根、领域服务、领域对象、限界上下文、资源库,指导微服务落地,将一个大的复杂业务域拆分成若干业务子域。定义领域模型(包含数据、行为),相似业务聚合。
更多内容,参考 DDD是如何解决复杂业务扩展问题?
画下项目的业务架构图?
答案:要对简历的项目非常熟悉,不只是自己负责的那部分,观整体通细节。
很多同学面试前没有系统化梳理,面试时现组织语言,给人感觉思路凌乱、条理不够清晰。
面试前可以先自己画画系统架构,理清其中的依赖关系。同时,提炼核心有挑战的技术难点,面试时,重点介绍其来龙去脉。
项目中用过哪些设计模式?
答案:工厂、装饰、克隆、代理、适配器、观察者、策略、模板、单例、责任链、门面等23种软件设计模式,这是软件开发的基本功,每一种设计模式都要非常熟悉。否则很难写出扩展性很高的代码。
之前写过三篇文章,每一种模式都有详细介绍:
解决复杂业务架构,软件设计模式系列(第一期)
解决复杂业务架构,软件设计模式系列(第二期)
解决复杂业务架构,软件设计模式系列(第三期)
如何提升系统的吞吐量?
答案:围绕系统的 “三高原则”,高并发、高性能、高可用,三个方向展开。
更多内容,参考 【高并发、高性能、高可用】系统设计经验
面对海量数据,什么是水平拆分、垂直拆分?
答案:
1、垂直拆分可以分为业务维度、技术维度。
业务维度:结合DDD领域驱动设计,将一个大的业务域拆分为若干业务子域,比如电商可以拆分为商户、商品、库存、权限、会员、营销、交易、支付、履约、订单、结算、仓储、物流、财务等。
技术维度:将一个有很多字段的表,按字段的大小、使用频率等特点,拆分为多张表。
2、水平拆分
由于单台机器的性能有限,无法支撑海量数据存储。我们引入逻辑表概念,采用集群模式,将一张逻辑表拆分成多张物理表分散存储在不同服务器,通过分表键路由,比如:时间、区域、用户id等。
特点:虽然有多张表,但每张表的表结构都是一样的,区别是数据不一样。所有表的数据合并起来才是这个业务表的完整数据。
画外音:数据量大,就分表;并发高,就分库
更多内容,参考 单台 MySQL 支撑不了这么多的并发请求,我们该怎么办?
分库分表时,全局性主键ID有哪些生成方案?
答案:
1、UUID,生成的是 32 位的字符串,虽然可以做到全局唯一性,但我们一般推荐使用整型。
2、SNOWFLAKE,雪花算法,生成一个 64 位的Long类型数据。组成结构:正数位(占1位)+ 时间戳(占41位)+ 工作机器id(10位)+ 序列号部分(12位)
3、数据库号段模式,对不同的业务类型定义初始值和步长,业务系统引入SDK,本地缓存预申请一定数据量的主键ID值,满足一定的并发要求。
4、TinyID,滴滴的开源框架
5、Redis 的 incr 命令
6、Leaf,美团的开源框架
7、Uidgenerator,百度的开源框架
不停机情况下,数据库扩容要怎么做?
答案:
1、首先,创建好新库,应用端修改代码,采用双写机制,将数据也同步一份到新库中
2、数据迁移,将老库中的数据迁移到新库中
3、对新老数据库做数据核对,对不一致的数据做定向同步
4、开关打开,读操作切换到新库,通过线上真实流量验证数据的准确性。
5、经过一段时间的线上验证后,如果没问题,将老库的写操作下线
缓存更新常用策略?
答案:
Cache aside,通常会先更新数据库,然后再删除缓存,为了兜底还会设置缓存时间。
Read/Write through, 一般是由一个 Cache Provider 对外提供读写操作,应用程序不用感知操作的是缓存还是数据库。
Write behind,延迟写入,Cache Provider 每隔一段时间会批量写入数据库,大大提升写的效率。像操作系统的page cache也是类似机制。
写操作时,缓存和数据库如何保持一致性?
答案:常见的方案有以下几个
1、先更新数据库,再更新缓存。
由于是两个操作,如果多线程并发情况下,很难保证线程之间的顺序,可能导致缓存的是旧数据。一般不推荐这种方案
2、先删除缓存,再更新数据库
两个操作,如果中间执行过程中,缓存被删除,恰好有其他线程来读数据,缓存会被重新预热旧的数据。一般不推荐这种方案
3、先更新数据库,再删缓存。
可能会导致无效删除,但是删除本身具有幂等性。该方案成本最低,一般推荐该方案
4、借助消息中间件,对缓存的数据强制维护,但也只能保证最终一致性。
5、不管最终采用哪种方案,Key都要设置一个过期时间,借助自身的淘汰机制保证数据的更新。
如何减少接口的响应时间?
答案:
1、减少接口的处理内容,将非核心逻辑移除,采用MQ解耦,异步化处理。
2、接口内部引入本地缓存和分布式缓存,提升数据加载速度
3、引入池化技术(线程池、进程池、对象池、内存池、连接池、协程池)
事务有哪些特性?
答案:
1、原子性(Atomicity),原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。
2、持久性(Durability),事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
3、一致性(Consistency),事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
4、隔离性(Isolation),一个事务不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
分布式事务,有哪些解决方案?
答案:
1、执行业务逻辑前,先插入流水任务,如果中间过程调用外部RPC接口服务或者本地数据库操作失败时,流水任务会被定时调度任务周期性触发、重试,直到成功。前提条件,所有接口服务都要实现幂等。当执行成功时,流水记录会被删除。
2、基于事务消息,能保证本地数据库一定成功,其他业务系统做为订阅方,通过重试机制保证最终一致性,但要注意接口幂等。
3、两阶段提交
4、引入业务状态机,初始为 init 状态(对外不可见),待所有依赖的RPC接口全部调用一遍,待所有接口的数据全部初始化,然后开始第二轮调用,将状态置为对外可见。当然,此阶段可能会部分调用失败,需要多次重试
5、TCC 模式。Try:尝试待执行的业务;Confirm:执行业务;Cancel:回滚执行的业务
6、Seata 阿里开源框架,把分布式事务定义为由若干本地事务(分支)组成的全局事务。被全局事务管理的全部分支,将在协调器的协调下,保证一起成功或一起回滚。
上面列举的方案细节,可以查看 如何解决分布式事务
如何预防死锁?
答案:互斥条件、请求和保持条件、环路等待、不可剥夺。只要打破其他一个条件即可。
什么是乐观锁?什么是悲观锁?
答案:
乐观锁:分为三个阶段:数据读取、写入校验、数据写入。在数据进行提交更新时,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回错误信息,让用户决定如何去做,fail-fast机制。
悲观锁:对数据的修改持保守态度,在整个数据处理过程中,将数据处于锁定状态,期间其他用户长时间无法访问,影响程序的并发访问性。
常见的限流策略有哪些?
答案:
1、计数器限流
2、滑动窗口限流
3、漏桶
4、令牌桶
更多内容,参考 高可用系统,必须修炼《流控》这把利剑!
接口幂等性,有哪些方案?
答案:Select 查询天然支持幂等,而写操作 多次执行可能会导致数据错误,下面简单列举常见的解决方案:
insert、update 之前,先select 查询校验
表中加唯一约束
加悲观锁 , select * from order where order_id= 100000 for update; 对操作行锁定。
加乐观锁,表中增加一个attribute_cc自增字段,借助CAS机制控制并发
有些表不适合添加唯一约束,可以单独建一张防重表,在防重表插入成功,在操作其它业务表
引入状态机, update order set status = "wait_seller_send_good" where id=1 and status = "wait_buyer_pay";
采用 分布式锁,第一次请求可以成功加锁,后续请求加锁失败,认为是重复请求。
token机制,client 首先请求获取token,提交时除了业务参数外还要带上这个token,server端会对这个token核销,只能核销一次。如果 server 查询不到token,则认为是重复请求。
定时任务,有哪些实现方案?
Time,JDK自带的定时器类,与 TimerTask 一起配合使用
ScheduledExecutorService,位于java.util.concurrent并发包下,基于多线程。
spring task,通过 @Scheduled 注解定义定时规则
quartz,开源框架
xxl-job,大众点评开源的一个分布式任务调度平台
elastic-job,当当网开源的弹性分布式任务调度系统,采用zookeeper实现分布式协调,实现任务高可用以及分片,适用于高并发等复杂业务场景。
经历过的项目,有没有遇到过什么线上问题?
答案:结合自己的工作经历来讲,一个程序员肯定都经历过线上救火的场景。那么找些有技术难度的整理下,如:
没有正确使用Redis的结构,缓存对象的粒度较大,高并发流量导致带宽不足,网络拥堵,连接无法及时释放,客户端报获取连接超时,进而引发整个系统的雪崩。
Pulsar系统告警,消息堆积。经排查,某个业务需求改动,SQl没有命中索引,导致接口处理时间增加数倍,从而降低了消费速度,进而消息堆积。
线上系统报OOM问题、内存泄露问题
以上,只是个举个例子。不同的业务,不同的系统,不同的人,经历的线上问题也千奇百怪,可以多收集些。
新闻资讯 更多
- 【求职指南】10.什么时候可以入职?03-02
- 【求职指南】9.你还有什么想问的吗?03-02
- 【求职指南】8.为什么来我们公司面试!03-02
- 【求职指南】7.谈谈你的职业规划03-02
- 【求职指南】6.你的优缺点有哪些?03-02
- 【求职指南】5.说说你对加班的看法?03-02
- 【求职指南】4.你手里还有其他Offer吗?03-02
- 【求职指南】3.你的期望薪资是多少?03-02