【面试题】技术场景 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 {
    @Override
    public boolean login(String username, String password) {
        // 实际数据库查询验证逻辑
        if ("admin".equals(username) && "123456".equals(password)) {
            return true;
        }
        return false;
    }
}
public class PhoneCodeLoginStrategy implements LoginStrategy {
    @Override
    public 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 {
    @Autowired
    private 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 {
    @Autowired
    private 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 {
    @Override
    public String processPayment(PaymentRequest request) {
        // 调用微信支付 API 进行支付处理
        // 返回支付结果
        return "微信支付成功";
    }
}
public class AlipayPaymentProcessor implements PaymentProcessor {
    @Override
    public String processPayment(PaymentRequest request) {
        // 调用支付宝支付 API 进行支付处理
        // 返回支付结果
        return "支付宝支付成功";
    }
}
public class BankCardPaymentProcessor implements PaymentProcessor {
    @Override
    public 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<>();

    @Autowired
    public 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 {
    @Autowired
    private 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 等操作可实现分布式锁基本功能。
  • 支付通用服务封装

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

30分钟内搭建一个全能轻量级springboot 3.4 + 脚手架 <1> 5分钟快速创建一个springboot web项目

快速导航 <1> 5分钟快速创建一个springboot web项目 <2> 5分钟集成好最新版本的开源swagger ui&#xff0c;并使用ui操作调用接口 <3> 5分钟集成好druid并使用druid自带监控工具监控sql请求 <4> 5分钟集成好mybatisplus并使用mybatisplus generator自…

【Rust自学】11.10. 集成测试

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 11.10.1. 什么是集成测试 在Rust里&#xff0c;集成测试完全位于被测试库的外部。集成测试调用库的方式和其他代码一样&#xff0c;这也…

JAVA实现2048小游戏(附源码)

文章目录 一、设计来源2048小游戏讲解1.1 主界面1.2 4*4难度界面1.3 5*5难度界面1.4 6*6难度界面1.5 挑战失败提示界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/a…

【自动化测试】—— Appium安装配置保姆教程(图文详解)

目录 一. 环境准备 二. JDK安装 1. 下载JDK 2. 安装JDK 3. 配置环境 4. 验证安装 三. Android SDK安装 1. 下载Android SDK 2. 安装Android SDK 3. 安装工具 4. 配置环境 5. 验证安装 四. NodeJS安装 1. 下载NodeJS 2. 安装NodeJS 3. 验证安装 4. 安装淘宝镜像…

vs2022+QT6.7.3打包程序流程

1、新建目录test 2、将项目配置为Release X64&#xff0c;生成XXX.exe 3、将XXX.exe放到test目录 4、管理员方式打开Qt 6.7.3 (MSVC 2022 64-bit)&#xff0c;进入test目录&#xff0c;执行&#xff1a;windeployqt6.exe XXX.exe 5、管理员方式打开x64 Native Tools Command…

RabbitMQ---消息确认和持久化

&#xff08;一&#xff09;消息确认 1.概念 生产者发送消息后&#xff0c;到达消费端会有以下情况&#xff1a; 1.消息处理成功 2.消息处理异常 如果RabbitMQ把消息发送给消费者后就把消息删除&#xff0c;那么就可能会导致&#xff0c;消息处理异常想要再获取这条消息的时…

【C++】反向迭代器

反向迭代器 一.源码及框架分析二.反向迭代器实现代码1.ReverseIterator.h2.Vector.h3.List.h4.Test.cpp 一.源码及框架分析 SGI-STL30版本源代码&#xff0c;反向迭代器实现的核心源码在stl_iterator.h中&#xff0c;反向迭代器是一个适配器&#xff0c;各个容器中再适配出自己…

浅谈云计算02 | 云计算模式的演进

云计算计算模式的演进 一、云计算计算模式的起源追溯1.2 个人计算机与桌面计算 二、云计算计算模式的发展阶段2.1 效用计算的出现2.2 客户机/服务器模式2.3 集群计算2.4 服务计算2.5 分布式计算2.6 网格计算 三、云计算计算模式的成熟与多元化3.1 主流云计算服务模式的确立3.1.…

WEB 攻防-通用漏-XSS 跨站脚本攻击-反射型/存储型/DOMBEEF-XSS

XSS跨站脚本攻击技术&#xff08;一&#xff09; XSS的定义 XSS攻击&#xff0c;全称为跨站脚本攻击&#xff0c;是指攻击者通过在网页中插入恶意脚本代码&#xff0c;当用户浏览该网页时&#xff0c;恶意脚本会被执行&#xff0c;从而达到攻击目的的一种安全漏洞。这些恶意脚…

Vue3组件设计模式:高可复用性组件开发实战

Vue3组件设计模式:高可复用性组件开发实战 一、前言 在Vue3中&#xff0c;组件设计和开发是非常重要的&#xff0c;它直接影响到应用的可维护性和可复用性。本文将介绍如何利用Vue3组件设计模式来开发高可复用性的组件&#xff0c;让你的组件更加灵活和易于维护。 二、单一职责…

深度剖析RabbitMQ:从基础组件到管理页面详解

文章目录 一、简介二、Overview2.1 Overview->Totals2.2 Overview->Nodesbroker的属性2.3 Overview->Churn statistics2.4 Overview->Ports and contexts2.5 Overview->Export definitions2.6 Overview->Import definitions 三、Connections连接的属性 四、C…

Unity 语音转文字 Vosk 离线库

市场有很多语音库&#xff0c;这里介绍Vosk SDK 除了支持untiy外还有原生开发服务器等 目录 安装unity示例demo下载语音训练文件运行demo结尾一键三联 注意事项 有可能debug出来的文本是空的&#xff0c;&#xff08;确保麦克风正常&#xff0c;且索引正确&#xff09;分大…

播放音频文件同步音频文本

播放音频同步音频文本 对应单个文本高亮显示 使用audio音频文件对应音频文本资源 音频文本内容&#xff08;Json&#xff09; [{"end": 4875,"index": 0,"speaker": 0,"start": 30,"text": "70号二啊,","tex…

【React】新建React项目

目录 create-react-app基础运用React核心依赖React 核心思想&#xff1a;数据驱动React 采用 MVC体系package.jsonindex.html好书推荐 官方提供了快速构建React 项目的脚手架&#xff1a; create-react-app &#xff0c;目前使用它安装默认是19版本&#xff0c;我们这里降为18…

mac homebrew配置使用

本文介绍mac上homebrew工具的安装、配置过程。homebrew功能类似于centos的yum&#xff0c;用于软件包的管理&#xff0c;使用上有命令的差异。 本次配置过程使用mac&#xff0c;看官方文档&#xff0c;在linux上也可以用&#xff0c;但我没试过&#xff0c;有兴趣的同学可以试试…

第一次作业三种方式安装mysql(Windows和linux下)作业

在Windows11上安装sever&#xff08;服务&#xff09;端和客户端 server端安装 打开官网MySQL 进入到主页 点击DOWMLOAD 进入下载界面 点击下方MySQL Community (GPL) Downloads 进入社区版mysql下载界面 点击 MySQL Community Server 进入server端下载 选择8.4.3LTS&…

基于Media+Unity的手部位姿三维位姿估计

使用mediapipe Unity 手部位姿三维位姿估计 参考文章 基于Mediapipe的姿势识别并同步到Unity人体模型中 MediapipeUnity3d实现虚拟手_unity mediapipe-CSDN博客 需求 我的需求就是快速、准确的跟踪手部位姿并实现一个三维显示。 主要思路 搭建mdeiapipe系统&#xff0c…

【C++】IO 流

文章目录 &#x1f449;C 语言的输入与输出&#x1f448;&#x1f449;流是什么&#x1f448;&#x1f449;C IO 流&#x1f448;C 标准 IO 流C 和 C 语言的输入格式问题C 的多次输入内置类型和自定义类型的转换日期的多次输入C 文件 IO 流文本文件和二进制文件的读写 &#x1…

成功案例分享 — 芯科科技助力涂鸦智能打造Matter over Thread模块,简化Matter设备开发

芯科科技&#xff08;Silicon Labs&#xff09;的愿景之一是让开发者每天都能够更轻松地开发无线物联网&#xff08;IoT&#xff09;。特别是在拥有相同愿景的合作伙伴的帮助下&#xff0c;我们每天都在取得进步。但是要想弥合知识水平和物联网开发之间的差距仍会面临一定的挑战…

MySQL主从部署(保姆版)

一、mysql 同步复制有关概述 一般数据库都是读取压力大于写数据压力&#xff0c;主从复制即为了实现数据库的负载均衡和读写分离。通过将Mysql的某一台主机的数据复制到其它主机&#xff08;slaves&#xff09;上&#xff0c;主服务器只负责写&#xff0c;而从服务器只负责读。…