系统设计面试题

时间:2024-10-10 07:10:20
主题 链接
Java基础知识 面试题
Java集合容器 面试题
Java并发编程 面试题
Java底层知识 面试题
Java常用框架 面试题
计算机网络 面试题
数据库 面试题
RabbitMQ 面试题
Redis 面试题
Elasticsearch 面试题
Zookeeper 面试题
系统设计 面试题

文章目录

  • 系统设计
    • 系统鉴权
    • 分布式ID分发器
      • UUID
      • 数据库主键自增
      • Redis
      • 雪花算法
    • 分布式锁
      • Redis分布式锁
      • Zookeeper分布式锁
      • 数据库
    • 单点登录
    • 权限管理
    • 秒杀系统
    • 新鲜事系统
      • Pull + Push
      • Pull Model
      • Push Model
      • 扩展
  • 分布式与微服务
    • 什么是微服务
    • 什么是分布式系统
    • CAP定理
    • BASE理论
    • 分布式事务
      • 2PC
      • 3PC
      • TCC
    • Service Mesh
    • ServiceLess
    • DevOps
    • SaaS、PaaS、IaaS
  • 高并发系统设计
    • 高性能
    • 高可用
    • 扩展性
    • 池化技术
    • 读写分离
    • 分表分库
    • 缓存
    • 数据同步
    • 限流
  • 性能调优
      • JVM参数如何优化
      • tomcat如何调优
      • 数据库连接池如何调优
      • Spring Cloud: Hystrix、Ribbon调优
  • 常见问题
      • 内存溢出、线程死锁、类加载冲突
      • 当一个Java程序响应很慢时如何查找问题、
      • 当一个Java程序频繁FullGC时如何解决问题、
      • 如何查看垃圾回收日志,有什么意义
      • 当一个Java应用发生OutOfMemory时该如何解决、
      • 如何判断是否出现死锁、
      • 如何判断是否存在内存泄露
    • Arthas
      • 使用Arthas快速排查Spring Boot应用404/401问题
      • 使用Arthas排查线上应用日志打满问题
      • 利用Arthas排查Spring Boot应用NoSuchMethodError
      • 服务性能问题如何定位
  • 算法与数据结构
    • 跳表与红黑树
    • 手写排序算法
    • 手写生产者、消费者
    • 手写一个线程池
    • 手写一个死锁
    • 手写一下单例模式
    • 手写一下工厂模式
    • 手写一下观察者模式
    • 手写一个栈
    • 手写一个队列
    • 手写ArrayList

系统设计

系统鉴权

参考微信公众平台的接口授权方式,采用一个授权中心来统一管理。 被调用者拥有一个 systemId,由系统生成;调用者拥有唯一的 appId 和 appSecret,调用者通过 appId 和 appSecret 向授权中心请求一个 accessToken

分布式ID分发器

UUID

原理:包括网卡MAC地址、时间戳、随机数、时序等元素生成
优点:本地生成ID,不需要进行远程调用,时延低,性能非常高,扩展性好
缺点:空间占用较多,不能生成递增有序的数字,没有业务含义

数据库主键自增

优点:简单方便,有序递增,方便排序和分页
缺点:分库分表麻烦,受限于数据库的性能,数据库宕机服务不可用,简单递增容易暴露信息

Redis

原理:Incr,IncrBy原子性增加
优点:性能比数据库好,能满足有序递增
缺点:可能会出现ID重复和不稳定

雪花算法

原理:1bit:一般是符号位,不做处理;41bit:用来记录时间戳;10bit用来记录机器ID;12bit:循环位
优点:高性能高可用:生成时不依赖于数据库,完全在内存中生成;ID自增:存入数据库中,索引效率高
缺点:依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能会造成id冲突或者重复

分布式锁

Redis分布式锁

利用Redis的setnx命令、或者set带参数命令(高版本redis),解锁时利用lua脚本将删除命令和判断超时的命令原子化(不判断超时,可能删除了其它人的锁)
RedLock、Redission

Zookeeper分布式锁

利用Zookeeper的顺序临时节点,临时节点删除则释放锁
在节点上绑定监听器,实现非阻塞锁
将客户端信息写入节点,实现可重入锁

数据库

利用数据库的行锁
优点:实现简单
缺点:性能问题、锁的失效、需要不断重试、非重入锁

单点登录

参考链接

权限管理

用户(用户组)、角色、权限(权限组)
角色继承、角色与组织架构关联、角色隔离
页面权限、操作权限、数据权限、权限期限、权限委托、多租户权限设计
通过注解+拦截器实现比较好

秒杀系统

前端:防重复点击、动态URL、CDN资源静态化
后端:Nginx限流、库存放到Redis预热、MQ异步下单

新鲜事系统

Pull + Push

普通用户用Push,明星用户使用Pull

Pull Model

原理
1、get followings
2、get tweets frin followings
3、merge and return

缺陷
N次DB Reads非常慢,且发生在用户获得News Feed的请求过程中,用户体验差

算法
K路归并算法:在用户查看News Feed时,获取每个好友的前100条Tweets,合并出前100条News Feed

复杂度
1、Post a tweet => 1次DB Write的时间
2、News Feed => 假如有N个关注对象,则为N次DB Reads的时间 + K路归并时间

Push Model

算法
1、为每个用户建一个List存储他的News Feed信息
2、 用户发一个Tweet之后,将该推文逐个推送到每个用户的News Feed List中(Fanout)
3、用户需要查看News Feed时,只需要从该News Feed List中读取最新的100条即可

复杂度
News Feed => 1次DB Read
Post a tweet => N个粉丝,需要N次DB Writes • 好处是可以用异步任务在后台执行,无需用户等待

缺陷
followers的数目可能很大
异步执行慢
僵尸粉没必要推送

扩展

解决Pull缺陷

  • 在DB访问前加入cache
  • Cache每个用户的timeline
  • Cache每个用户的News Feed

解决Push缺陷

  • 异步Push时排序,不活跃用户排在后面执行
  • 明星用户采用Pull+普通用户采用Push
  • 摇摆问题:设置过渡区间、定时刷新用户类型

什么时候用Push
资源少、实现简单、实时性要求不高、用户发帖较少、双向好友关系、没有明星问题(比如朋友圈)

什么时候用Pull
资源充足、实时性要求高、用户发帖多、单向好友关系、有明星问题

分布式与微服务

什么是微服务

服务拆分、独立维护、发布,风险小、成本低。通过http\rpc等方式进行接口调用
分布式系统带来复杂性;部署、测试、监控变得复杂;分布式事务的问题和CAP问题

什么是分布式系统

硬件或软件分布在不同的网络计算机上,彼此通过消息传递进行通信和协调的系统
分布式、对等性(没有主从之分)、并发性(并发操作同一资源)、缺乏全局时钟、故障无法避免
问题:通信异常、网络分区、三态(成功、失败、超时)、节点故障

CAP定理

分布式系统中一致性、可用性、分区容错性不能同时满足
无法同时做到一致性和可用性,分布式系统要保证分区容错,只能选CP或者AP

BASE理论

即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性
三个状态:基本可用(性能差或服务降级)、软状态(存在中间状态,数据延迟)、最终一致性(分布式系统每个节点数据一致)

分布式事务

2PC

第一阶段:事务管理器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.
第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。
缺点:单点问题、资源阻塞(要等待提交完成)、数据不一致(部分commit未执行)

3PC

CanCommit
PreCommit
DoCommit

TCC

Try阶段:主要是对业务系统做检测及资源预留。
Confirm阶段:确认执行业务操作。
Cancel阶段:取消执行业务操作。
TCC本质上就是一个应用层面的2PC,需要通过业务逻辑来实现,不同的业务场景所写的代码都不一样,不好复用

阿里巴巴Seata实现了2PC和TCC
参考链接

Service Mesh

参考链接

ServiceLess

参考链接

DevOps

参考链接

SaaS、PaaS、IaaS

参考链接

高并发系统设计

高性能

参考链接

性能指标:QPS\TPS\DAU

分位值:75分位、90分位、95分位

高可用

可用性指标
平均故障时间、故障的平均恢复时间
系统可用性:99.9——年故障时间8小时

系统设计

  • 故障转移
  • 超时控制
  • 降级限流

系统运维

  • 灰度发布
  • 故障演练(混沌工程——随机制造系统故障)

扩展性

存储拆分——按业务纬度分库、分表分库
业务拆分——根据业务维度、重要性维度、请求来源维度

池化技术

线程池
数据库连接池

读写分离

分表分库

垂直拆分:专库专用,不同表放不同的数据库;数据层面的故障隔离

水平拆分

  • 按某一个字段的哈希值做拆分:适用于实体表例如:用户表的ID
  • 按某一字段的区间做拆分:常用时间字段,例如评论表的“创建时间”

水平拆分缺点

  • 所有查询都要带上分区键(可以将要查询的字段跟分区键做一个映射表)
  • 不能直接join,要在业务代码中组合筛选
  • count类操作麻烦,采用redis单独存储计数数据

缓存

旁路缓存策略

  • 读:先读缓存,再读数据库(写入缓存)
  • 写:先写入数据库,再删除缓存(若更新缓存需要加锁)

读穿 / 写穿 策略

  • 用户只与缓存打交道,由缓存和数据库通信,写入或者读取数据

写回策略

  • 写入数据时只写入缓存,并且把缓存块儿标记为“脏”的。脏数据只有被再次使用时才会将其中的数据写入到后端存储中。

数据同步

双写方案:1、同时写入新库和旧库,从旧库读;2、从新库读 3、去掉旧库
级联同步

限流

四种限流算法

性能调优

JVM参数如何优化

参考链接

tomcat如何调优

maxThreads: Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数。默认值150。

acceptCount: 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。默认值10。

minSpareThreads: Tomcat初始化时创建的线程数。默认值25。

maxSpareThreads: 一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。默认值75。

enableLookups: 是否反查域名,默认值为true。为了提高处理能力,应设置为false

connnectionTimeout: 网络连接超时,默认值60000,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。

maxKeepAliveRequests: 保持请求数量,默认值100。 bufferSize: 输入流缓冲大小,默认值2048 bytes。

compression: 压缩传输,取值on/off/force,默认值off。 其中和最大连接数相关的参数为maxThreads和acceptCount。如果要加大并发连接数,应同时加大这两个参数。

数据库连接池如何调优

Spring Cloud: Hystrix、Ribbon调优

常见问题

内存溢出、线程死锁、类加载冲突

当一个Java程序响应很慢时如何查找问题、

当一个Java程序频繁FullGC时如何解决问题、

如何查看垃圾回收日志,有什么意义

当一个Java应用发生OutOfMemory时该如何解决、

如何判断是否出现死锁、

如何判断是否存在内存泄露

Arthas

官方文档

使用Arthas快速排查Spring Boot应用404/401问题

使用Arthas排查线上应用日志打满问题

利用Arthas排查Spring Boot应用NoSuchMethodError

服务性能问题如何定位

算法与数据结构

跳表与红黑树

手写排序算法

堆排序
二分查找
希尔排序
快速排序
归并排序
选择排序
冒泡排序

手写生产者、消费者

生产者与消费者

手写一个线程池

线程池

手写一个死锁

死锁

手写一下单例模式

单例模式

手写一下工厂模式

工厂模式

手写一下观察者模式

观察者模式

手写一个栈

手写栈

手写一个队列

手写队列

手写ArrayList

手写ArrayList