当前位置: 首页 > article >正文

【面试题】技术场景 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 等操作可实现分布式锁基本功能。
  • 支付通用服务封装

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

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

http://www.kler.cn/a/501036.html

相关文章:

  • 30_Redis哨兵模式
  • 对智能的一种新理解
  • 漏洞扫描工具
  • 学技术步骤,(tomcat举例)jar包api手写tomcat静态资源基础服务器
  • [创业之路-243]:《华为双向指挥系统》-1-组织再造-企业不同组织形式下的指挥线的种类?
  • MapReduce完整工作流程
  • 【mysql】约束的基本使用
  • 《跟我学Spring Boot开发》系列文章索引❤(2025.01.09更新)
  • ollama教程(window系统)
  • element plus 使用 el-tree 组件设置默认选中和获取所有选中节点id
  • Q_OBJECT宏报错的问题
  • Liunx-搭建安装VSOMEIP环境教程 执行 运行VSOMEIP示例demo
  • 网络安全-防火墙
  • DolphinScheduler自身容错导致的服务器持续崩溃重大问题的排查与解决
  • 第41章 使用 Docker Compose 进行容器迁移的技术指南及优势
  • 二十三种设计模式-原型模式
  • 深度学习张量的秩、轴和形状
  • HarmonyOS鸿蒙开发 弹窗及加载中指示器HUD功能实现
  • Redis数据库——Redis快的原因
  • Erlang语言的软件工程
  • ⭐MySQL的底层原理与架构
  • Unity编程与游戏开发-编程与游戏开发的关系
  • Helm部署activemq
  • 【1】Word:邀请函
  • Unity3D仿星露谷物语开发20之道具简介弹出窗
  • Python Selenium库入门使用,图文详细。附网页爬虫、web自动化操作等实战操作。