系统设计相关

title: 系统设计相关 description: Springboot相关

系统设计相关

1. 【面试题目】项目中常用的设计模式有哪些?核心实现(需创建的名称及作用)及实际应用场景是什么?

回答要点关键字

(1) 高频模式:单例、工厂(简单/工厂方法)、策略、观察者、动态代理、模板方法
(2) 核心组件:抽象层(接口/抽象类)+ 实现层(具体类)+ 调度层(工厂/上下文),解耦复用
(3) 组件作用:抽象层定义规范、实现层落地逻辑、调度层封装依赖,降低耦合
(4) 应用场景:工具类(单例)、多实现创建(工厂)、算法切换(策略)、事件通知(观察者)、AOP增强(代理)

打开详情

🍺基础回答:

项目里常用的设计模式核心是“抽象+实现+调度”的组合。单例模式要创建“单例类”,作用是全局只存一个实例,比如Redis工具类,避免重复创建浪费资源;工厂模式要创建“抽象产品接口+具体产品类+工厂类”,工厂类负责按类型创建产品实例,比如支付场景的支付宝、微信支付,不用写一堆if-else;策略模式要创建“策略接口+具体策略类+上下文类”,上下文类选策略执行,比如电商满减、折扣优惠,切换优惠规则不用改业务代码;观察者模式要创建“事件类(被观察者)+ 监听类(观察者)+ 事件发布器”,比如下单后自动发短信、扣库存,事件触发后自动通知;动态代理要创建“目标接口+目标实现类+代理处理器”,作用是在不改业务代码的情况下加日志、事务;模板方法要创建“抽象模板类+具体实现类”,抽象类定义流程,实现类填细节,比如数据库CRUD统一流程。

🎉高级扩展版:

各高频设计模式的核心组件名称及作用(无代码,仅组件+职责):

一、单例模式(Singleton)

需创建的组件及作用:

  1. 单例类:核心组件,封装业务逻辑(如工具方法、连接池操作)
  2. 私有构造方法:作用是禁止外部通过new关键字创建实例,保证实例唯一性
  3. 静态实例变量:作用是存储全局唯一的实例(需volatile修饰防指令重排)
  4. 静态获取方法(getInstance):作用是提供外部获取实例的唯一入口,内部实现线程安全校验(双重检查锁定)

应用场景:工具类(日期、加密、缓存工具)、数据库连接池、配置类、全局缓存实例

二、工厂模式(Factory)

1. 简单工厂(项目首选)

需创建的组件及作用:

  1. 抽象产品接口:作用是定义产品的统一规范(如支付、数据源的核心方法)
  2. 具体产品类:作用是实现抽象产品接口,落地具体逻辑(如支付宝支付、MySQL数据源)
  3. 工厂类:作用是根据入参(如支付类型、数据源类型)创建对应具体产品实例,解耦业务代码与实例创建

2. 工厂方法(扩展场景)

需创建的组件及作用:

  1. 抽象产品接口:定义产品规范
  2. 具体产品类:实现产品逻辑
  3. 抽象工厂接口:定义创建产品的工厂规范
  4. 具体工厂类:每个具体产品对应一个具体工厂,作用是创建对应产品实例,避免简单工厂的if-else膨胀

应用场景:多支付渠道、多数据源切换、插件化功能(如不同文件解析器)

三、策略模式(Strategy)

需创建的组件及作用:

  1. 策略接口:作用是定义算法/规则的统一规范(如优惠计算、排序逻辑的核心方法)
  2. 具体策略类:作用是实现策略接口,落地不同的算法/规则(如满减优惠、会员折扣、冒泡排序)
  3. 上下文类:作用是持有策略实例,提供统一对外接口,根据业务场景(如用户选择的优惠类型)切换策略,屏蔽策略切换细节

应用场景:电商优惠计算、日志输出方式(控制台/文件/ELK)、排序算法切换、第三方接口适配(不同供应商的API调用)

四、观察者模式(Observer)

需创建的组件及作用:

  1. 事件类(被观察者/主题):作用是封装事件信息(如订单ID、状态),提供观察者注册/移除、事件发布的方法
  2. 观察者类(监听类):作用是实现事件处理逻辑,响应被观察者的事件通知(如短信发送、库存扣减)
  3. 事件发布器(可选,Spring框架已封装):作用是简化事件发布逻辑,统一管理事件与观察者的关联

应用场景:订单状态变更通知、消息推送、缓存更新、业务事件联动(如支付成功后触发发货、积分更新)

五、动态代理模式(Proxy)

需创建的组件及作用:

  1. 目标接口:作用是定义目标对象的核心业务方法(如用户登录、订单提交)
  2. 目标实现类:作用是实现目标接口,落地核心业务逻辑
  3. 代理处理器类:作用是封装增强逻辑(日志、事务、限流、权限校验),在目标方法执行前后触发
  4. 代理工厂类(可选):作用是创建代理实例,屏蔽代理创建的底层细节(JDK动态代理/CGLIB代理)

应用场景:AOP编程、接口日志记录、事务管理、接口限流、权限校验、远程调用(RPC代理)

六、模板方法模式(Template Method)

需创建的组件及作用:

  1. 抽象模板类:作用是定义业务流程的固定步骤(如数据库CRUD:连接→执行→关闭),将可变步骤定义为抽象方法
  2. 具体实现类:作用是继承抽象模板类,实现抽象方法,落地可变步骤的具体逻辑(如MySQL的CRUD、Oracle的CRUD)

应用场景:数据库CRUD统一流程、接口调用统一流程(请求封装→发送→响应解析)、报表生成流程(数据查询→计算→导出)

七、装饰器模式(Decorator)

需创建的组件及作用:

  1. 抽象组件接口:作用是定义被装饰对象的核心规范(如IO流、接口调用的核心方法)
  2. 具体组件类:作用是实现抽象组件接口,落地基础功能(如FileInputStream、基础接口调用)
  3. 装饰器抽象类:作用是实现抽象组件接口,持有抽象组件实例,定义装饰器的统一规范
  4. 具体装饰器类:作用是继承装饰器抽象类,增强具体组件的功能(如BufferedInputStream的缓冲功能、接口调用的超时重试功能)

应用场景:IO流增强(缓冲、加密)、接口功能增强(超时重试、熔断、降级)、日志增强(脱敏、结构化)

📌 加分项:

  1. 模式组合使用案例:工厂模式+策略模式(工厂创建策略实例,上下文切换策略,如支付渠道选择+优惠计算)、观察者模式+装饰器模式(事件通知前增强事件信息,如日志脱敏)
  2. 框架底层应用细节:Spring的BeanFactory(工厂方法模式)、Spring AOP(动态代理+模板方法)、MyBatis的Executor(策略模式)、Spring事件机制(观察者模式,ApplicationEvent=事件类,ApplicationListener=观察者类)
  3. 组件设计技巧:策略接口+Spring自动注入(上下文类通过@Autowired注入所有策略实例,无需手动注册)、观察者模式结合异步线程池(事件通知异步执行,提升性能)
  4. 扩展优化:工厂类结合配置文件/枚举(避免硬编码类型)、策略类通过注解标记(上下文类通过注解扫描获取策略实例)

⚠️注意事项:

  1. 单例模式:必须保证线程安全(双重检查锁定+volatile),避免反射/序列化破坏单例;禁止在单例类中存储可变状态(如用户信息),否则导致线程安全问题
  2. 工厂模式:简单工厂适合实现类较少的场景,过多实现类需切换为工厂方法,避免工厂类臃肿;工厂类避免依赖具体产品类,需依赖抽象产品接口(依赖倒置原则)
  3. 策略模式:策略类过多时需拆分(如按业务域分组),上下文类避免直接依赖所有策略实例,可通过懒加载优化;避免策略切换逻辑耦合在业务代码中,统一由上下文类管理
  4. 观察者模式:避免观察者过多导致事件发布耗时过长,需结合异步执行;防止循环依赖(观察者发布事件,被观察者又触发该观察者)
  5. 动态代理:JDK动态代理仅支持接口,无接口场景需用CGLIB代理;增强逻辑避免过于复杂,否则影响性能;代理类需避免修改目标方法的返回值和入参(除非业务需要)
  6. 模板方法模式:抽象模板类的固定步骤需稳定,避免频繁修改;可变步骤不宜过多,否则拆分抽象类(单一职责原则)

2. 【面试题目】如何设计一套高可用、高性能、高并发(三高)系统?核心组件及设计思路是什么?

回答要点关键字

(1) 核心目标:高可用(99.99%+可用性)、高性能(低延迟高吞吐)、高并发(支撑海量请求)
(2) 设计维度:架构分层、集群部署、缓存优化、异步解耦、限流熔断、存储扩容、监控容灾
(3) 核心组件:负载均衡、缓存集群、消息队列、熔断限流组件、分布式存储、监控告警系统
(4) 设计原则:无单点故障、流量削峰填谷、读写分离、服务解耦、弹性伸缩、快速故障转移

打开详情

🍺基础回答:

设计三高系统核心是围绕“不宕机、响应快、能抗量”来做。首先架构要分层,比如接入层、应用层、服务层、存储层,每层都集群部署,避免一个节点挂了整个系统崩掉。然后用缓存(比如Redis)存热点数据,不用每次都查数据库,响应速度就快了;高并发来了用消息队列(比如RabbitMQ)削峰,把瞬间的海量请求存起来慢慢处理,避免冲垮数据库。还要加限流(比如Sentinel),超过系统承载能力就拒绝或降级,比如秒杀时提示“当前拥挤”;服务之间加熔断(比如Hystrix),防止一个服务挂了连累其他服务。存储方面用读写分离(主库写、从库读)、分库分表,支撑海量数据存储和查询。最后得有监控告警,出问题能及时发现,还能自动切换故障节点,保证高可用。

🎉高级扩展版:

三高系统设计按“架构层+核心组件+设计策略”展开,核心组件及作用如下:

一、高可用设计(避免宕机,快速恢复)

核心组件及作用:

  1. 集群部署组件:应用集群、数据库集群、缓存集群,作用是避免单点故障,一个节点故障时其他节点接管
  2. 负载均衡组件(Nginx/HAProxy/ALB):作用是分发请求到集群节点,实现流量均衡,同时过滤故障节点
  3. 熔断降级组件(Sentinel/Hystrix/Resilience4j):作用是服务故障时熔断(停止调用),或降级(返回默认结果),防止级联失败
  4. 故障转移组件(Keepalived/MySQL MGR/Redis Sentinel):作用是自动检测故障节点,将流量切换到健康节点,实现无感恢复
  5. 异地多活/灾备组件:跨地域部署集群,作用是单个地域故障时,切换到备用地域,保障业务连续性

设计策略:

  • 无状态化设计:应用层不存储本地状态(如用户会话存Redis),支持水平扩容
  • 重试机制:非核心服务调用失败后重试(限次数+退避策略),避免瞬时故障影响
  • 数据多副本:数据库主从复制、Redis集群主从+哨兵,确保数据不丢失

二、高性能设计(低延迟,高吞吐)

核心组件及作用:

  1. 多级缓存组件(本地缓存Caffeine+分布式缓存Redis+CDN):作用是缓存热点数据(商品详情、用户信息),减少数据库查询,降低延迟
  2. 异步解耦组件(RabbitMQ/Kafka/RocketMQ):作用是将同步调用改为异步(如下单后异步发短信、扣库存),提升接口响应速度
  3. 读写分离组件(MySQL读写分离中间件MyCat/Sharding-JDBC):作用是主库处理写请求,从库处理读请求,分散数据库压力
  4. 连接池组件(Druid/HikariCP/Redis连接池):作用是复用连接,减少连接创建销毁开销,提升并发处理能力
  5. 序列化组件(Protobuf/Kryo):作用是优化数据传输格式,减小数据体积,提升网络传输效率

设计策略:

  • 接口优化:合并冗余接口(如一次查询多数据)、压缩响应数据(Gzip)
  • 计算下沉:复杂计算(如统计报表)异步预计算,存储结果供查询
  • 网络优化:选择高性能协议(如HTTP/2、gRPC),减少网络往返次数

三、高并发设计(支撑海量请求)

核心组件及作用:

  1. 限流组件(Sentinel/Gateway限流/Nginx限流):作用是限制单位时间内的请求量(如QPS=10万),避免超出系统承载能力
  2. 流量削峰组件(消息队列):作用是承接瞬时高峰流量(如秒杀、促销),异步消费,避免数据库被压垮
  3. 分布式锁组件(Redis Redlock/ZooKeeper):作用是解决高并发下的数据竞争(如库存扣减),保证数据一致性
  4. 分库分表组件(Sharding-JDBC/ShardingSphere):作用是将海量数据拆分到多个数据库/表(如按用户ID分表),提升查询和写入性能
  5. 弹性伸缩组件(K8s/HPA/云服务器弹性扩容):作用是根据流量动态增加/减少节点,应对流量波动

设计策略:

  • 队列削峰:高峰流量先入MQ队列,消费端按能力消费
  • 热点隔离:核心业务(支付)与非核心业务(日志)用独立集群,避免资源抢占
  • 限流粒度:按接口、用户、IP分级限流,优先保障核心用户/核心接口

四、基础支撑组件(保障系统稳定运行)

  1. 监控告警组件(Prometheus+Grafana/ELK/Zabbix):作用是监控系统指标(QPS、延迟、错误率),异常时及时告警(短信/邮件)
  2. 链路追踪组件(SkyWalking/Pinpoint/Zipkin):作用是追踪请求全链路,定位性能瓶颈和故障点
  3. 配置中心组件(Nacos/Apollo/ConfigServer):作用是动态调整配置(限流阈值、缓存过期时间),无需重启系统
  4. 分布式事务组件(Seata/TCC模式):作用是保证分布式场景下的数据一致性(如下单+扣库存+减余额)

📌 加分项:

  1. 架构演进思路:从单体→垂直拆分→微服务→云原生,逐步提升三高能力,避免一步到位的过度设计
  2. 热点问题优化:热点数据缓存防击穿(布隆过滤器)、防雪崩(缓存过期时间随机化)、防穿透(空值缓存)
  3. 成本优化:结合云原生技术(K8s、Serverless)实现弹性伸缩,降低闲置资源成本;缓存分级(本地缓存+分布式缓存)平衡性能与成本
  4. 压测与演练:定期进行全链路压测(JMeter/Gatling),验证系统承载能力;开展混沌工程(故障注入),检验高可用设计有效性
  5. 业务适配:核心业务(支付、下单)优先保障高可用,非核心业务(推荐、评论)可降级,平衡体验与稳定性

⚠️注意事项:

  1. 避免过度设计:小流量系统无需复杂架构(如分库分表、异地多活),否则增加维护成本
  2. 数据一致性:异步解耦、缓存使用可能导致数据不一致,需设计补偿机制(如MQ消息重试、缓存更新策略)
  3. 缓存风险:缓存集群故障可能引发“缓存雪崩”,需设计降级方案(如直接查数据库+限流)
  4. 限流降级粒度:避免一刀切限流(如核心用户不限流,普通用户限流),防止影响核心业务
  5. 监控盲区:不仅监控技术指标(QPS、延迟),还需监控业务指标(下单成功率、支付转化率),全面感知系统状态
  6. 分布式事务:避免滥用强一致性事务(性能差),优先使用最终一致性方案(如TCC、SAGA),平衡一致性与性能

3. 【面试题目】有40亿个QQ号,仅1GB内存,如何实现去重?

回答要点关键字

(1) 核心方案:布隆过滤器(空间效率极高,允许可控误判)
(2) 设计核心:bit数组大小计算、哈希函数个数选型、误判率控制
(3) 补充策略:数据分片(内存不足时)、误判后磁盘二次校验(需全量数据)
(4) 核心优势:内存占用极小(40亿数据≈500-900MB)、插入查询效率O(k)(k为哈希函数个数)

打开详情

🍺基础回答:

40亿个QQ号直接存的话要几十GB内存,1GB根本不够,核心得用布隆过滤器。它就是一个bit数组加几个哈希函数,每个QQ号通过哈希函数映射到bit数组的几个位置,把这些位置设为1。去重的时候,看新QQ号对应的所有bit位是不是全1:全1就是大概率已经存在,有0就肯定不存在。这样40亿数据大概只占几百MB内存,完全符合1GB限制。如果怕误判,后面可以用磁盘存的全量数据做二次检查,确保准确。

🎉高级扩展版:

  1. 核心参数计算(适配1GB内存):
    • 已知:数据量n=4×10⁹,目标误判率p=0.1(可调整)
    • bit数组大小m≈-n×lnp/(ln2)²≈-4e9×ln0.1/(0.7²)≈4e9×2.3/0.49≈18.8亿bit≈2.35GB(超1GB)
    • 优化方案:数据分片,按QQ号哈希值分成2片(每片2×10⁹数据),m≈-2e9×ln0.1/0.49≈9.4亿bit≈1.17GB,再把误判率调到0.15,m≈7.75亿bit≈969MB,刚好适配1GB。
    • 哈希函数个数k≈(m/n)×ln2≈(7.75e9/2e9)×0.693≈2.7,取3个(MurmurHash、CRC32、SHA-1截取,平衡冲突率)。
  2. 实现步骤:
    (1) 用long数组实现bit数组(1个long占64bit,无额外开销);
    (2) QQ号转long型(10位QQ≤9e9,long足够),减少哈希计算开销;
    (3) 插入:3个哈希函数计算bit位置,置为1;判断:3个位置全1则判定重复;
    (4) 误判处理:布隆过滤器作为预过滤,判定重复的QQ号去磁盘分片文件中二次校验(确保100%准确)。
  3. 内存优化:避免用Java BitSet(有对象 overhead),直接用long[]手动计算bit位置;分片时确保哈希均匀,避免单个分片超内存。

📌 加分项:

  1. 替代方案:布谷鸟过滤器(支持删除操作,误判率更低,适合动态去重场景);
  2. 并行优化:多线程处理不同分片,每个线程持有独立bit数组,最后按位OR合并;
  3. 磁盘配合:非实时去重时,先按哈希分片写入磁盘文件,再逐个文件用布隆过滤器去重,进一步降低单轮内存占用;
  4. 动态调整:根据实际内存大小,用公式动态计算m和k,平衡误判率与内存占用。

⚠️注意事项:

  1. 布隆过滤器不支持删除(删除一个bit会影响其他数据),需动态去重请换布谷鸟过滤器;
  2. 哈希函数个数需严格计算:过多导致bit数组快速饱和,过少导致冲突增多,均会升高误判率;
  3. 分片必须均匀,避免单个分片数据量过大超出1GB内存限制;
  4. 无需100%准确时可直接用布隆过滤器,需绝对准确必须配合磁盘全量数据二次校验,否则会漏判。

42. 【面试题目】请设计一个接口防刷方案,包括限流、黑名单、设备指纹等机制?

回答要点关键字

(1) 核心机制:限流(控制请求频率)、黑名单(拦截恶意主体)、设备指纹(唯一标识设备)
(2) 实现组件:限流组件、设备指纹生成器、黑名单存储、降级兜底、监控告警
(3) 粒度控制:接口级、用户级、IP级、设备级(多维度组合)
(4) 核心原则:精准识别恶意请求、最小化误判、不影响正常用户、性能开销低

打开详情

🍺基础回答:

接口防刷方案核心是“识别恶意主体+控制请求频率+拦截恶意行为”。首先设备指纹,给每个访问设备生成唯一标识(比如用设备型号、操作系统、IP、浏览器信息组合哈希),就算换IP也能识别同一设备。然后限流,按接口、用户、设备、IP多维度限制请求频率,比如单个用户1分钟最多调用100次,单个设备5秒内最多3次,用令牌桶或漏桶算法实现。黑名单是把恶意请求的用户ID、IP、设备指纹加入黑名单,一定时间内禁止访问,还能手动添加恶意主体。另外还要有降级兜底,比如限流后返回“当前请求过频繁”,黑名单返回“账号异常”,再加上监控告警,实时看到限流和黑名单拦截情况,及时调整策略。

🎉高级扩展版:

  1. 核心组件及实现细节:

    一、设备指纹机制(唯一标识设备,防IP伪造)

    • 生成维度:设备硬件信息(CPU型号、设备型号)、系统信息(OS版本、内核版本)、软件信息(浏览器UA、APP版本)、网络信息(IP、MAC地址)、行为信息(屏幕分辨率、字体大小);
    • 实现方式:将多维度信息拼接,通过MD5/SHA-256哈希生成唯一指纹(如“device:fingerprint:xxx”),APP端可结合设备唯一标识(如Android的IMEI、iOS的IDFA,需用户授权);
    • 防篡改:指纹生成逻辑在服务端执行,客户端仅传递原始信息,服务端校验信息完整性(如加签),避免客户端伪造指纹。

    二、限流机制(多维度控制请求频率)

    • 限流维度:
      1. 接口级:每个接口独立限流(如登录接口QPS=100,查询接口QPS=1000);
      2. 用户级:按用户ID限流(如用户1分钟最多5次下单);
      3. 设备级:按设备指纹限流(如设备10秒最多3次登录);
      4. IP级:按IP限流(如单个IP1分钟最多200次请求);
    • 限流算法:
      1. 令牌桶算法(推荐):固定速率往桶里放令牌,请求需拿令牌才能执行,支持突发流量(适合接口调用);
      2. 漏桶算法:请求按固定速率处理,超出部分缓存或丢弃(适合写操作,如下单);
    • 存储实现:用Redis存储限流计数器(如“rate:limit:interface:login:ip:192.168.1.1”),结合EXPIRE设置过期时间,原子操作INCR+EXPIRE保证线程安全。

    三、黑名单机制(拦截恶意主体)

    • 黑名单主体:用户ID、设备指纹、IP地址、手机号;
    • 加入规则:
      1. 自动加入:限流阈值触发(如1分钟内触发3次限流)、恶意行为检测(如密码连续错误5次、请求参数异常);
      2. 手动加入:运营后台手动添加恶意主体(如诈骗用户);
    • 存储实现:Redis Set存储黑名单(如“blacklist:user:10086”“blacklist:device:xxx”),支持过期时间(如临时黑名单24小时,永久黑名单无过期),查询时O(1)判断。

    四、兜底与监控机制

    • 降级策略:限流/黑名单拦截后,返回友好提示(如“当前请求过频繁,请稍后再试”“账号存在安全风险,请联系客服”),核心接口可返回降级数据(如缓存结果);
    • 监控告警:通过Prometheus监控限流拦截率、黑名单新增数、接口调用频率,设置阈值告警(如拦截率>5%短信告警);
    • 误判处理:提供申诉渠道(如用户申诉页面),人工审核后移除黑名单,调整限流阈值。
  2. 整体架构流程:
    (1) 请求入口:网关(Nginx/Gateway)接收请求,提取请求参数(用户ID、IP、设备信息);
    (2) 设备指纹生成:服务端根据设备信息生成指纹,判断是否在黑名单;
    (3) 限流校验:多维度限流校验,是否超出频率限制;
    (4) 业务处理:校验通过则执行接口逻辑,失败则返回降级提示;
    (5) 日志上报:记录拦截日志(主体、时间、原因),用于监控和分析。

📌 加分项:

  1. 智能风控增强:结合用户行为画像(如登录地点、设备是否常用),异常行为(如异地登录+频繁下单)触发更严格的限流或人工审核;
  2. 性能优化:网关层优先拦截(Nginx+Lua脚本执行IP/设备指纹限流),减少后端服务压力;Redis限流用Pipeline批量操作,提升性能;
  3. 动态调整:通过配置中心(Nacos/Apollo)动态调整限流阈值、黑名单过期时间,无需重启服务;
  4. 分布式支持:Redis集群存储限流和黑名单数据,支持多服务节点共享状态,适配分布式架构。

⚠️注意事项:

  1. 设备指纹需兼顾准确性和隐私合规:避免采集敏感信息(如IMEI需用户授权),遵守《个人信息保护法》;
  2. 限流阈值需结合业务场景调整:核心接口(支付、登录)阈值较低,非核心接口(查询、评论)阈值较高,避免影响正常用户;
  3. 黑名单避免滥用:临时黑名单优先,永久黑名单需严格审核,减少误判导致的用户投诉;
  4. 性能开销控制:网关层拦截避免全量请求穿透到后端,Redis操作需优化(如批量查询、合理设置过期时间),避免Redis成为瓶颈。