【面试题】技术场景 4、负责项目时遇到的棘手问题及解决方法

工作经验一年以上程序员必问问题

面试题概述

  • 问题为在负责项目时遇到的棘手问题及解决方法,主要考察开发经验与技术水平,回答不佳会影响面试印象。
  • 提供四个回答方向,准备其中一个方向即可。
    在这里插入图片描述

1、设计模式应用方向

  • 以登录为例,未用设计模式时登录逻辑在一个业务类中,需求变更(如登录方式增减或更换)需频繁修改业务层代码。
  • 采用工厂设计模式和策略模式后,解决了频繁修改业务层代码的问题,具体实现可参考相关案例,其他设计模式介绍方式类似。

2、线上 bug 方向

  • 项目上线后可能出现测试环境未暴露的问题,如运行一段时间后 CPU 飙高、内存泄露、线程死锁等,线上调试困难。
  • 介绍时需按问题、解决过程、最终解决方案的逻辑进行,可参考 DVM 和多线程课程案例。

3、调优方向

  • 调优时最好给出指标数据,如接口调优前后的访问耗时。
  • 重点讲述调优中间过程,可涉及 SQL 优化(加索引)、添加缓存、采用集群或高可用方案等。

4、组件封装方向

  • 分布式锁和接口幂等可封装为小型工具或组件供多项目使用,支付和事务可封装为通用服务,但难度较高且需考虑高可用和通用性。
  • 有经验可详细讲述,无经验可参考网上文章,实在想不到可从其他三个方向入手。

总结强调

  • 此面试题高频出现,需提前准备,选择一个方向深入准备能体现技术水平。

具体问题

设计模式应用案例

在一个电商项目的用户登录功能开发中,最初的设计非常简单直接。登录逻辑全部集中在一个业务类 UserLoginService 中,代码如下:

public class UserLoginService {public boolean login(String username, String password) {// 直接在该方法中进行数据库查询验证if ("admin".equals(username) && "123456".equals(password)) {return true;}return false;}
}

随着业务发展,需要支持多种登录方式,如手机号验证码登录、第三方平台登录(微信、支付宝)等。每次增加新的登录方式,都需要在 login 方法中添加大量的条件判断逻辑,导致代码越来越臃肿,维护成本急剧上升。例如添加手机号验证码登录:

public class UserLoginService {public boolean login(String identifier, String credential) {if (identifier.matches("^1[3 - 9]\\d{9}$")) {// 手机号验证码登录逻辑,查询数据库验证验证码if ("validCode".equals(credential)) {return true;}} else if ("admin".equals(identifier) && "123456".equals(credential)) {// 用户名密码登录逻辑return true;}return false;}
}

为解决这个问题,引入了工厂设计模式与策略模式。首先定义一个登录策略接口 LoginStrategy

public interface LoginStrategy {boolean login(String identifier, String credential);
}

然后分别实现用户名密码登录策略类 UsernamePasswordLoginStrategy 和手机号验证码登录策略类 PhoneCodeLoginStrategy

public class UsernamePasswordLoginStrategy implements LoginStrategy {@Overridepublic boolean login(String username, String password) {// 实际数据库查询验证逻辑if ("admin".equals(username) && "123456".equals(password)) {return true;}return false;}
}
public class PhoneCodeLoginStrategy implements LoginStrategy {@Overridepublic boolean login(String phone, String code) {// 实际数据库查询验证逻辑if ("13800138000".equals(phone) && "validCode".equals(code)) {return true;}return false;}
}

接着创建一个登录策略工厂类 LoginStrategyFactory

public class LoginStrategyFactory {public static LoginStrategy getLoginStrategy(String loginType) {if ("usernamePassword".equals(loginType)) {return new UsernamePasswordLoginStrategy();} else if ("phoneCode".equals(loginType)) {return new PhoneCodeLoginStrategy();}return null;}
}

最后,修改业务类 UserLoginService,通过工厂获取相应的登录策略:

public class UserLoginService {public boolean login(String loginType, String identifier, String credential) {LoginStrategy strategy = LoginStrategyFactory.getLoginStrategy(loginType);if (strategy!= null) {return strategy.login(identifier, credential);}return false;}
}

通过这种方式,当需要添加新的登录方式时,只需要创建新的策略类并在工厂类中添加相应的获取逻辑,无需修改业务层的核心代码,大大提高了代码的扩展性和维护性。

线上 bug 处理案例

在一个大型的在线教育平台项目上线一段时间后,运维人员反馈系统出现 CPU 使用率持续飙高的情况,导致部分课程直播卡顿,严重影响用户体验。由于线上环境复杂,难以在测试环境复现相同问题,增加了调试难度。

首先,利用 jstack 命令获取当前 Java 进程的线程堆栈信息,通过分析堆栈信息,发现有一个线程一直在执行某个方法 calculateCourseScore,该方法用于计算课程的综合得分,代码如下:

public class CourseScoreCalculator {public double calculateCourseScore(List<StudentAnswer> answers) {double totalScore = 0;for (StudentAnswer answer : answers) {// 复杂的计算逻辑,包含多层嵌套循环for (int i = 0; i < answer.getOptions().size(); i++) {for (int j = 0; j < answer.getOptions().get(i).getSubOptions().size(); j++) {totalScore += answer.getOptions().get(i).getSubOptions().get(j).getScore();}}}return totalScore;}
}

从代码逻辑上看,多层嵌套循环导致计算量过大,在高并发情况下容易使 CPU 使用率飙升。针对这个问题,对计算逻辑进行了优化。通过减少不必要的循环嵌套,将部分重复计算的逻辑提取出来,优化后的代码如下:

public class CourseScoreCalculator {public double calculateCourseScore(List<StudentAnswer> answers) {double totalScore = 0;for (StudentAnswer answer : answers) {List<Option> options = answer.getOptions();for (Option option : options) {List<SubOption> subOptions = option.getSubOptions();for (SubOption subOption : subOptions) {totalScore += subOption.getScore();}}}return totalScore;}
}

同时,监控系统资源,发现数据库查询操作也占用了较多资源。通过对数据库查询语句进行分析,发现部分查询没有使用合适的索引。例如,在查询学生课程学习记录时,原 SQL 语句为:

SELECT * FROM student_course_record WHERE student_id = 12345;

student_course_record 数据量较大,而 student_id 字段没有添加索引,导致查询效率低下。为 student_id 字段添加索引:

CREATE INDEX idx_student_id ON student_course_record(student_id);

经过这些优化措施,再次监控系统,CPU 使用率恢复到正常水平,线上直播卡顿问题得到解决。

系统调优案例

在一个电商订单系统中,有一个查询订单详情的接口 getOrderDetail,调优前该接口的平均响应时间为 2 秒,严重影响用户体验。为了提升系统性能,对该接口进行了优化。

首先,对接口涉及的 SQL 查询语句进行分析。原 SQL 语句为:

SELECT * FROM orders 
JOIN order_items ON orders.order_id = order_items.order_id 
JOIN products ON order_items.product_id = products.product_id 
WHERE orders.order_id = 12345;

通过 EXPLAIN 关键字分析查询执行计划,发现 JOIN 操作效率较低,因为相关表没有合适的索引。为 orders 表的 order_id 字段、order_items 表的 order_idproduct_id 字段、products 表的 product_id 字段添加索引:

CREATE INDEX idx_order_id_orders ON orders(order_id);
CREATE INDEX idx_order_id_order_items ON order_items(order_id);
CREATE INDEX idx_product_id_order_items ON order_items(product_id);
CREATE INDEX idx_product_id_products ON products(product_id);

经过索引优化后,SQL 查询效率得到显著提升。同时,考虑到订单数据相对稳定,添加缓存机制。使用 Redis 作为缓存,在查询订单详情时,首先从 Redis 中查询是否有缓存数据,如果有则直接返回,避免了数据库查询。代码实现如下:

public OrderDetail getOrderDetail(long orderId) {OrderDetail orderDetail = (OrderDetail) redisTemplate.opsForValue().get("order:" + orderId);if (orderDetail!= null) {return orderDetail;}// 如果缓存中没有,从数据库查询orderDetail = orderDao.getOrderDetail(orderId);if (orderDetail!= null) {redisTemplate.opsForValue().set("order:" + orderId, orderDetail);}return orderDetail;
}

经过上述优化,该接口的平均响应时间从 2 秒缩短到了 500ms,系统性能得到了大幅提升。

组件封装案例

  1. 分布式锁封装
    在一个分布式电商库存扣减系统中,为了保证库存扣减的原子性,避免超卖现象,需要使用分布式锁。封装了一个简单的分布式锁工具类 DistributedLockUtil,使用 Redis 实现分布式锁。代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;@Component
public class DistributedLockUtil {@Autowiredprivate RedisTemplate<String, String> redisTemplate;public boolean tryLock(String lockKey, String requestId, int expireTime) {return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);}public void unlock(String lockKey, String requestId) {if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) {redisTemplate.delete(lockKey);}}
}

在库存扣减业务代码中使用该分布式锁:

public class StockService {@Autowiredprivate DistributedLockUtil distributedLockUtil;public boolean deductStock(long productId, int quantity) {String lockKey = "stock:" + productId;String requestId = UUID.randomUUID().toString();if (distributedLockUtil.tryLock(lockKey, requestId, 10)) {try {// 检查库存并扣减int stock = stockDao.getStock(productId);if (stock >= quantity) {stockDao.deductStock(productId, quantity);return true;}} finally {distributedLockUtil.unlock(lockKey, requestId);}}return false;}
}

这个分布式锁工具类可以在多个电商相关项目中复用,保证了分布式环境下关键业务的一致性。

  1. 支付通用服务封装
    在一个综合性电商平台项目中,涉及多种支付方式,如微信支付、支付宝支付、银行卡支付等。为了提高代码的复用性和系统的可维护性,封装了一个支付通用服务 PaymentService

首先定义一个支付接口 PaymentProcessor

public interface PaymentProcessor {String processPayment(PaymentRequest request);
}

然后分别实现微信支付处理器 WeChatPaymentProcessor、支付宝支付处理器 AlipayPaymentProcessor 和银行卡支付处理器 BankCardPaymentProcessor

public class WeChatPaymentProcessor implements PaymentProcessor {@Overridepublic String processPayment(PaymentRequest request) {// 调用微信支付 API 进行支付处理// 返回支付结果return "微信支付成功";}
}
public class AlipayPaymentProcessor implements PaymentProcessor {@Overridepublic String processPayment(PaymentRequest request) {// 调用支付宝支付 API 进行支付处理// 返回支付结果return "支付宝支付成功";}
}
public class BankCardPaymentProcessor implements PaymentProcessor {@Overridepublic String processPayment(PaymentRequest request) {// 调用银行卡支付 API 进行支付处理// 返回支付结果return "银行卡支付成功";}
}

接着创建支付服务类 PaymentService,通过策略模式选择具体的支付处理器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;@Service
public class PaymentService {private Map<String, PaymentProcessor> paymentProcessorMap = new HashMap<>();@Autowiredpublic PaymentService(WeChatPaymentProcessor weChatPaymentProcessor,AlipayPaymentProcessor alipayPaymentProcessor,BankCardPaymentProcessor bankCardPaymentProcessor) {paymentProcessorMap.put("wechat", weChatPaymentProcessor);paymentProcessorMap.put("alipay", alipayPaymentProcessor);paymentProcessorMap.put("bankCard", bankCardPaymentProcessor);}public String processPayment(PaymentRequest request) {String paymentType = request.getPaymentType();PaymentProcessor processor = paymentProcessorMap.get(paymentType);if (processor!= null) {return processor.processPayment(request);}return "不支持的支付方式";}
}

在订单支付业务中使用该支付服务:

public class OrderService {@Autowiredprivate PaymentService paymentService;public String payOrder(Order order) {PaymentRequest request = new PaymentRequest();request.setPaymentType(order.getPaymentType());request.setAmount(order.getTotalAmount());return paymentService.processPayment(request);}
}

通过这种方式,将支付相关的复杂逻辑封装在 PaymentService 中,其他业务模块只需调用该服务即可,提高了系统的通用性和可维护性,同时也方便扩展新的支付方式。


总结

设计模式应用

  • 主要内容:电商项目用户登录功能,从初始逻辑集中导致维护困难,到引入工厂与策略模式解决问题。
  • 核心概念:工厂设计模式用于创建对象,策略模式将算法逻辑封装。
  • 关键知识点:理解两种设计模式作用,明白如何结合使用提高代码扩展性与维护性。

总结

  • 设计模式可优化代码结构,解决业务变更时代码频繁修改问题。
  • 工厂模式负责对象创建,策略模式处理不同业务逻辑实现,二者结合使代码更灵活。

线上 bug 处理

  • 主要内容:在线教育平台上线后 CPU 飙高,通过分析线程堆栈和 SQL 语句定位并解决问题。
  • 核心概念:利用工具获取线程堆栈信息辅助定位问题,关注数据库索引对查询性能影响。
  • 关键知识点:掌握获取和分析线程堆栈方法,了解索引优化 SQL 查询原理。

总结

  • 线上 bug 因环境复杂难调试,需借助工具精准定位。
  • 优化代码逻辑和数据库查询,可有效解决性能问题。

系统调优

  • 主要内容:电商订单系统查询订单详情接口,从 2 秒响应优化到 500ms,通过 SQL 优化和缓存实现。
  • 核心概念:使用 EXPLAIN 分析查询执行计划,运用缓存减少数据库查询压力。
  • 关键知识点:学会分析查询执行计划,掌握缓存机制及应用场景。

总结

  • 系统调优需关注数据库查询和缓存策略,以提升接口响应速度。
  • 索引优化可提高 SQL 查询效率,缓存能快速返回常用数据。

组件封装

  • 分布式锁封装

    • 主要内容:电商库存扣减系统使用分布式锁保证原子性,封装基于 Redis 的分布式锁工具。
    • 核心概念:分布式锁确保分布式环境下操作一致性,Redis 提供实现方式。
    • 关键知识点:理解分布式锁原理,掌握基于 Redis 实现分布式锁方法。
  • 总结

    • 分布式锁用于解决分布式场景下数据一致性问题。
    • Redis 的 setIfAbsent 等操作可实现分布式锁基本功能。
  • 支付通用服务封装

    • 主要内容:电商平台多种支付方式,通过封装支付通用服务提高复用性与可维护性。
    • 核心概念:利用策略模式实现不同支付方式处理,封装复杂支付逻辑。
    • 关键知识点:理解策略模式应用,学会封装通用服务。
  • 总结

    • 策略模式可根据不同条件选择不同支付处理器。
    • 封装通用服务能提升代码复用,便于维护和扩展新支付方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/67761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 子查询(重在练习)

第九章: 子查询 1.子查询的需求分析和问题解决 1.1基本使用 子查询(内查询)在主查询之前一次执行完成 子查询的结果被主查询(外查询)调用 注意事项 子查询要包含在括号内 将子查询放在比较条件的右侧 单行操作符对应单行子查询,多行操作符对应多行子查询 1.2子查询的分类…

物联网开发 的开发语言建议

对于物联网开发&#xff0c;选择合适的编程语言取决于具体的项目需求、硬件平台以及开发团队的技能。以下是几种常用的物联网开发语言及其适用场景&#xff0c;特别考虑到您当前的工作空间中包含 JavaScript 和 Vue 等技术栈&#xff1a; JavaScript (Node.js) 优点&#xff1a…

LeetCode:3298. 统计重新排列后包含另一个字符串的子字符串数目 II(滑动窗口 Java)

目录 3298. 统计重新排列后包含另一个字符串的子字符串数目 II 题目描述&#xff1a; 实现代码与解析&#xff1a; 滑动窗口 原理思路&#xff1a; 3298. 统计重新排列后包含另一个字符串的子字符串数目 II 题目描述&#xff1a; 给你两个字符串 word1 和 word2 。 如果…

海外招聘丨卡尔斯塔德大学—互联网隐私和安全副高级讲师

雇主简介 卡尔斯塔德大学以研究、教育和合作为基础。通过让社区参与知识发展&#xff0c;卡尔斯塔德大学为地区、国家和国际研究和教育发展做出了贡献&#xff0c;旨在提高可持续性、民主和健康。我们富有创造力的学术环境以好奇心、勇气和毅力为特征。通过采取批判性方法&…

CTFshow—文件包含

Web78-81 Web78 这题是最基础的文件包含&#xff0c;直接?fileflag.php是不行的&#xff0c;不知道为啥&#xff0c;直接用下面我们之前在命令执行讲过的payload即可。 ?filephp://filter/readconvert.base64-encode/resourceflag.php Web79 这题是过滤了php&#xff0c;…

iOS实际开发中使用Alamofire实现多文件上传(以个人相册为例)

引言 在移动应用中&#xff0c;图片上传是一个常见的功能&#xff0c;尤其是在个人中心或社交平台场景中&#xff0c;用户经常需要上传图片到服务器&#xff0c;用以展示个人风采或记录美好瞬间。然而&#xff0c;实现多图片上传的过程中&#xff0c;如何设计高效的上传逻辑并…

Cannot run program “docker“: CreateProcess error=2,系统找不到指定的文件

今天被这个问题坑了, 网上教程全是直接装插件就行 ,结果我连接可以成功 但是执行docker compose 就会出错, 检测配置 报错com.intellil,execution,process.ProcessNotCreatedException: Cannot run program “docker”: CreateProcess error2,系统找不到指定的文件 gpt 要我去…

Blender 2D动画与MATLAB数学建模:跨界融合的创新实践探索

文章目录 一、数据交换&#xff1a;从科学计算到创意表达的桥梁二、脚本自动化&#xff1a;从手动操作到无缝连接的升级三、跨界融合的创新实践意义《Blender 2D动画制作从入门到精通》亮点内容简介作者简介 《MATLAB数学建模从入门到精通》亮点内容简介作者简介 在数字创意与科…

vue项目上传ofd文件,导致文件类型丢失问题

1、问题 上传ofd文件&#xff0c;传到java后端&#xff0c;变成了无类型的文件&#xff0c;最后通过排查&#xff0c;发现前端通过vue组件上传的文件&#xff0c;没有文件类型了 2、解决办法 将文件内容重新放到一个新的文件中&#xff0c;并且给这个新的文件设置ofd类型 3…

HarmonyOS Next 日志工具介绍

HarmonyOS Next 日志工具介绍 在HarmonyOS Next开发中&#xff0c;日志是我们调试定位问题的主要手段&#xff0c;不管是hilog还是console&#xff0c;最终都可以输出到DevEco Studio的日志模块中&#xff1a; 在这里可以过滤应用进程、日志级别、日志内容呢&#xff0c;也可…

NUTTX移植到STM32

STM32移植NUTTX 1. Ubuntu下搭建开发环境1.1 先决条件1.2 下载 NuttX1.3 使用Make 进行编译1.4 烧录运行 2.通过NUTTX点亮LED2.1 部署操作系统2.2 修改配置文件2.3 编译运行程序 开发板&#xff1a;DshanMCUF407 官方开发文档&#xff1a;安装 — NuttX latest 文档 参考文档&…

什么是Transformer模型中的KV缓存:上下文新增那之前计算的KV还可用,在原有基础上对新增的进行计算就行

什么是Transformer模型中的KV缓存? 在Transformer模型中,KV缓存(Key-Value Cache)具有重要作用,以下是关于它的详细介绍: 概念含义 KV缓存主要是用于存储在模型推理过程中已经计算过的键(Key)和值(Value)信息。在Transformer架构里,比如在自注意力机制等计算环节…

【微服务】4、服务保护

微服务架构与组件介绍 单体架构拆分&#xff1a;黑马商城早期为单体架构&#xff0c;后拆分为微服务架构。跨服务调用与组件使用 服务拆分后存在跨服务远程调用&#xff0c;如下单需查询商品信息&#xff0c;使用openfeign组件解决。服务间调用关系复杂&#xff0c;需维护服务…

【网络安全 | 漏洞挖掘】通过模拟功能实现提权(Bugcrowd)

未经许可,不得转载。 我将与大家分享我在 Bugcrowd 的某个项目中发现的一个漏洞,该项目中有一个“用户模拟”功能。 什么是用户模拟? 用户模拟允许管理员在不知晓用户凭据的情况下“以用户身份登录”。这种功能常见于管理员需要调试问题、审查用户权限或解决投诉的平台中。…

蓝桥杯训练

1对于一个字母矩阵&#xff0c;我们称矩阵中的一个递增序列是指在矩阵中找到两个字母&#xff0c;它们在同一行&#xff0c;同一列&#xff0c;或者在同一 45 度的斜线上&#xff0c;这两个字母从左向右看、或者从上向下看是递增的。 例如&#xff0c;如下矩阵中 LANN QIAO有…

安装MySQL的五种方法(Linux系统和Windows系统)

一.在Linux系统中安装MySQL 第一种方法:在线YUM仓库 首先打开MySQL官网首页 www.mysql.com 找到【DOWNLOADS】选项&#xff0c;点击 下拉&#xff0c;找到 【MySQL Community(GPL) Downloads】 在社区版下载页面中&#xff0c;【 MySQL Yum Repository 】链接为在线仓库安装…

【Spring Boot 应用开发】-01 初识

特性 Spring Boot 是由 Pivotal 团队提供的全新框架&#xff0c;其设计目的是帮助开发者快速创建独立的、生产级的基于 Spring 框架的应用程序。以下是 Spring Boot 的一些主要特点&#xff1a; 简化配置&#xff1a;Spring Boot 提供了自动配置机制&#xff0c;能够根据类路径…

数学基础 -- 拉普拉斯算子的原理与应用

拉普拉斯算子的原理与应用 拉普拉斯算子&#xff08;Laplacian Operator&#xff09;是图像处理中用于检测区域二阶变化的重要工具&#xff0c;尤其擅长检测图像中的边缘、斑点等区域特征。它是基于图像的二阶导数&#xff0c;在空间域中描述像素强度变化的加速度。 1. 拉普拉…

【蓝桥杯】43709.机器人繁殖

题目描述 X 星系的机器人可以自动复制自己。它们用 1 年的时间可以复制出 2 个自己&#xff0c;然后就失去复制能力。 每年 X 星系都会选出 1 个新出生的机器人发往太空。也就是说&#xff0c;如果 X 星系原有机器人 5 个&#xff0c;1 年后总数是&#xff1a;5 9 14&#xf…

前端开发 vue 中如何实现 u-form 多个form表单同时校验

在 Vue 项目中使用 UView UI 的 u-form 组件时&#xff0c;多个表单同时校验的需求非常常见。例如&#xff0c;当我们有多个表单需要在同一个页面中进行校验并提交时&#xff0c;我们需要确保每个表单都能进行单独验证&#xff0c;同时可以在同一时刻进行批量验证。 接下来&am…