大厂场景题总结(附解决思路,优化了排版)
整理不易,球球牛油们来点小花花支持一下,感谢!
淘宝购物场景
问题分析 :在淘宝购物场景中,从点击支付跳转到支付页面,输入支付码,完成支付之后返回响应的订单列表页面,可能存在的问题包括支付流程的安全性、支付页面的加载速度、支付信息的准确性、订单状态的及时更新、网络异常导致的支付失败或重复支付、第三方支付平台的兼容性等。
架构设计思路 :
- 安全方面 :采用SSL/TLS加密协议保障数据传输安全,对支付信息进行加密处理,防止信息泄露和篡改。同时,进行严格的身份验证,确保用户身份的真实性。
- 性能优化 :对支付页面进行优化,如减少页面元素、采用CDN加速等,提高页面加载速度。在服务器端,可采用负载均衡、分布式缓存等技术,应对高并发访问。
- 数据处理 :建立可靠的支付状态同步机制,确保支付结果能及时准确地反馈到订单系统。可采用消息队列实现异步处理,提高系统响应速度和可靠性。
- 容错机制 :设计完善的异常处理机制,如网络超时重试、支付失败后的回滚操作等,确保在出现异常情况时系统能稳定运行,避免影响用户体验。
- 兼容性 :针对不同的第三方支付平台,进行充分的兼容性测试,确保在不同平台上的支付流程都能正常进行。
大文件排序
问题分析 :大文件小内存,文件内存储的是数字,要求对文件内容进行排序,主要面临内存不足无法一次性加载整个文件进行排序的问题。
解决思路 :
- 分块排序 :将大文件分成多个小块,每个小块的大小不超过内存限制,然后对每个小块进行排序。
- 外部归并排序 :对排序后的小块文件进行外部归并,每次从各个小块中读取一部分已排序的数据,利用最小堆等数据结构找出当前最小值,依次写入到最终的排序结果文件中。
- 多线程优化 :可采用多线程技术,同时对多个小块进行排序,提高排序效率。
- 磁盘I/O优化 :合理安排数据的读写顺序,减少磁盘的随机读写,提高整体排序速度。
表新增字段
问题分析 :在表上新增一个字段时,如果这个表正在进行读写操作,可能会导致锁表、影响现有操作的性能甚至造成数据不一致等问题。
处理方法 :
- 使用在线DDL :如MySQL的pt-online-schema-change工具,它可以在不锁定表的情况下,逐步完成表结构的变更,确保在变更过程中表的读写操作不受影响。
- 创建新表并迁移数据 :创建一个新表,结构与原表相同只是新增了字段,然后通过批处理的方式将原表的数据插入到新表中,最后替换原表。
- 选择合适的时间窗口 :如果业务允许,可在业务低峰期进行表结构的变更操作,减少对业务的影响。
- 使用事务和锁机制 :在变更过程中,合理使用事务和锁机制,确保数据的一致性和完整性。
Linux命令执行过程
问题分析 :在Linux命令行敲下一行命令,会涉及到命令的解析、执行等多个步骤。
执行过程 :
- 命令解析 :Shell会对输入的命令进行解析,判断是内置命令还是外部命令,确定命令的类型和参数。
- 环境变量查找 :如果命令需要在外部执行,Shell会根据环境变量(如PATH)查找命令所在的可执行文件路径。
- 创建子进程 :Shell通常会创建一个子进程来执行命令,避免影响当前Shell进程。
- 加载可执行文件 :在子进程中,加载找到的可执行文件到内存中,进行初始化操作。
- 执行命令 :按照命令的逻辑和参数,执行相应的操作,如读取文件、进行计算、启动服务等。
- 返回结果 :命令执行完成后,将结果返回给Shell,Shell再将结果显示在终端上。
QQ号查阅效率提升
问题分析 :42亿个QQ号,10万行数据,查阅效率比较低,采用分库的方法,如前5万行放到一个库里,后5万行放到另一个库里,但查找特定名字的账号时需要查两次,效率仍不高。
优化思路 :
- 哈希分库 :根据QQ号的哈希值进行分库,这样每个QQ号都能唯一对应到一个库,查找时只需计算哈希值即可快速定位到对应的库,减少查找次数。
- 建立索引 :在每个库中对名字字段建立索引,如B树索引,提高查找效率。
- 分布式搜索引擎 :采用分布式搜索引擎技术,如Elasticsearch,将数据分布到多个节点上,利用其高效的搜索和索引能力,实现快速查阅。
- 缓存热点数据 :对于经常被查询的名字或账号,可缓存在内存数据库如Redis中,加快查询速度。
查询优化
问题分析 :从前端页面到Java后台再到数据库,表存在上百万条数据,需要从这三个层面进行查询优化。
优化方法 :
- 前端优化 :实现分页加载,减少一次性请求的数据量;对非关键数据进行懒加载,提高页面响应速度。
- Java后台优化 :采用缓存机制,如Redis缓存查询结果,减少数据库查询次数;对查询条件进行预处理,优化SQL语句的生成。
- 数据库优化 :对表建立合适的索引,优化查询条件;定期进行数据库维护,如分析表、更新统计信息;对于复杂查询,可考虑采用物化视图等技术。
内存频繁FullGC
问题分析 :在内存充足的情况下,仍频繁发生FullGC,可能是因为对象存活率高、内存泄漏、堆结构不合理等原因。
排查思路 :
- 分析GC日志 :查看GC日志中的FullGC频率、耗时、回收的内存大小等信息,判断是否存在异常。
- 使用内存分析工具 :如JVisualVM、MAT等,对应用进行内存快照分析,找出内存中对象的分布情况,是否存在内存泄漏。
- 检查代码 :审查代码中是否存在大对象创建、静态集合无限制增长等可能导致内存问题的情况。
- 优化JVM参数 :根据应用的特点,调整JVM的堆大小、新生代和老年代比例、GC算法等参数,提高内存管理效率。
面向对象与面向过程判断
问题分析 :如何判断一种语言是面向对象的还是面向过程的,需要从语言的特性和编程范式角度进行分析。
判断依据 :
- 数据抽象和封装 :面向对象语言强调将数据和操作封装成对象,通过类和对象的方式进行组织;面向过程语言则以函数或过程为中心,数据和操作分离。
- 继承和多态 :面向对象语言支持类的继承和多态特性,可实现代码的复用和扩展;面向过程语言通常不支持这些特性。
- 编程思维 :面向对象编程更注重对现实世界对象的建模和模拟,面向过程编程则更关注解决问题的步骤和流程。
普通互斥锁实现读写锁
问题分析 :使用普通的互斥锁实现读写锁,需要考虑读写操作的并发控制,确保在多个读操作可以并发执行,而写操作独占执行。
实现思路 :
- 定义变量 :引入一个计数器变量,用于记录当前有多少个读操作正在进行。
- 读锁获取和释放 :读操作获取锁时,先获取互斥锁,增加计数器,然后释放互斥锁;读操作释放锁时,先获取互斥锁,减少计数器,若计数器为0则释放写锁。
- 写锁获取和释放 :写操作获取锁时,直接获取互斥锁,同时阻塞其他读写操作;写操作释放锁时,释放互斥锁。
- 确保线程安全 :在操作计数器和锁的过程中,必须保证线程安全性,防止出现竞态条件。
Canal同步数据库binlog宕机后的同步方案
问题分析 :后端项目的集群部署,使用Canal同步数据库binlog时发生宕机,需要确保从节点能正确同步数据,保证数据一致性。
同步方案 :
- 故障转移 :将Canal的主节点切换到备用节点,确保Canal服务能继续运行。
- 数据校验和补偿 :对宕机期间可能未同步的数据进行校验,通过比对数据库的binlog位置或数据记录,找出未同步的部分,进行手动补偿同步。
- 重启同步任务 :在Canal恢复后,重新启动同步任务,确保后续的binlog能正确同步到从节点。
- 监控和预警 :建立完善的监控和预警机制,及时发现Canal的异常情况,快速进行处理,防止数据不一致问题的扩大化。
服务与MQ消息同步意外暂停的排查
问题分析 :服务和MQ之间发送消息进行数据同步的过程意外暂停,可能是由于网络问题、MQ服务器故障、代码异常等原因导致。
排查步骤 :
- 检查网络连接 :验证服务与MQ之间的网络连接是否正常,是否存在网络中断、延迟过高或丢包等情况。
- 查看MQ服务器状态 :检查MQ服务器是否正常运行,是否有足够的资源(如内存、CPU)来处理消息,查看MQ的日志文件,查找是否有错误信息。
- 审查代码和配置 :检查服务端发送消息的代码逻辑,是否存在异常抛出、线程阻塞等问题;同时检查MQ的配置参数,如队列大小、连接数等是否合理。
- 监控消息队列 :查看消息队列中的消息数量、消费速率等指标,判断是否存在消息积压或消费停滞的情况,从而定位问题所在环节。
向小白讲解MySQL的作用和底层实现
问题分析 :将面试官看成小白,需要用简单易懂的方式讲解MySQL的作用和底层实现,对比使用文本文件存储。
讲解思路 :
- 作用讲解 :将MySQL比作一个超级大的电子表格,可以存储大量的数据,如用户信息、订单记录等。它能快速地帮我们找到想要的数据,还能同时让很多人进行数据的读写操作,就像多人协作编辑一个大的共享文档。
- 底层实现对比文本文件 :文本文件存储数据就像把东西随便堆在一个箱子里,找东西时需要一个个翻,效率低。而MySQL底层采用数据库管理系统,数据存储有结构,如表结构,通过索引技术(如B树)能快速定位数据,就像图书馆的书籍分类和索引系统,能快速找到特定的书。同时,MySQL还能处理并发访问,保证数据的安全性和一致性,而文本文件在多人同时读写时容易出问题。
选课系统设计
问题分析 :选课系统中,课的人数不能超,人的时间段不能重,需要设计合理的数据结构和算法来满足这些约束条件。
设计思路 :
- 数据结构设计 :为课程和学生建立数据模型,课程包含课程ID、名称、最大人数、上课时间等属性;学生包含学生ID、已选课程列表等属性。
- 人数限制实现 :在学生选课时,先检查课程的当前人数是否已满,若未满则允许选课,并增加课程人数计数;否则拒绝选课请求。
- 时间冲突检测 :当学生选课时,遍历其已选课程的时间段,与待选课程的时间段进行比较,若存在重叠则提示时间冲突,不允许选课;否则添加到已选课程列表中。
- 事务处理 :将选课操作作为一个事务处理,确保在选课过程中数据的一致性,如在人数增加和课程添加到学生列表的操作中,若其中一个失败则进行回滚操作。
关联表与冗余字段设计
问题分析 :在设计表的时候,关联表和在一个表中加冗余字段各有优劣,需要根据具体业务场景进行选择。
关联表优势 :
- 数据完整性 :通过外键约束等机制,能更好地维护数据的完整性和一致性,避免出现孤立记录或数据不匹配的情况。
- 减少数据冗余 :避免重复存储相同的数据,节省存储空间,同时减少数据更新时的维护工作量。
- 灵活扩展 :当业务需求变化时,更容易进行表结构的扩展和调整,如添加新的关联关系等。
冗余字段优势 :
- 查询性能 :减少表连接操作,提高查询效率,尤其在频繁进行复杂查询的情况下,能显著提升性能。
- 简化查询 :使查询语句更简单,不需要进行多表连接,降低开发难度和出错概率。
- 数据局部性 :相关数据集中存储,有利于数据的缓存和读取,提高系统的响应速度。
分库分表方案(淘宝购物场景)
问题分析 :在淘宝购物场景中,区分用户订单和商家订单,需要采用分库分表方案来应对海量数据和高并发访问。
方案设计 :
- 分库策略 :根据业务特点,可按用户ID或商家ID进行分库,将不同用户或商家的数据分布到不同的数据库中,减少单库的负载和数据量。
- 分表策略 :在每个库内,再根据订单时间或订单ID等进行分表,进一步细分数据,提高数据管理和查询效率。
- 路由层设计 :建立路由层,根据请求中的参数(如用户ID、商家ID、订单ID等)自动将请求路由到对应的库和表,实现透明的数据访问。
- 分布式事务处理 :对于涉及多个库的事务操作,采用分布式事务解决方案,如XA协议或基于消息队列的最终一致性方案,确保数据的一致性。
库存系统设定
问题分析 :库存系统在高并发读的情况下要扛住压力,同时保证数据一致性,加锁的粒度和流程需要合理设计。
设计思路 :
- 读优化 :采用缓存机制,如Redis缓存库存数量,对于读操作优先从缓存中获取,减少数据库的读压力。同时,可对库存数据进行预加载和更新缓存策略,提高缓存命中率。
- 写优化 :在写操作时,合理控制锁的粒度,如按商品ID进行粒度划分,对不同的商品库存加不同的锁,避免锁竞争。采用乐观锁或悲观锁机制,根据业务场景选择合适的锁策略,确保在高并发写时数据的一致性和正确性。
- 异步处理 :对于一些非实时性要求高的操作,如库存更新后的后续流程,可采用异步消息队列的方式进行处理,提高系统的响应速度和吞吐量。
- 监控和预警 :建立库存系统的监控体系,实时监控库存数量、读写请求量、锁等待时间等指标,及时发现潜在问题并进行预警和处理。
内存泄露排查
问题分析 :遇到内存泄露问题,需要通过一系列的排查步骤来定位问题所在。
排查方式 :
- 监控内存使用情况 :使用工具如JConsole、VisualVM等,监控JVM的内存使用趋势,观察堆内存、栈内存、本地方法栈等各区域的使用情况,确定内存泄露的大致区域。
- 生成内存快照 :在应用运行过程中,生成内存快照文件,通过内存分析工具如MAT进行分析,找出占用内存较多的对象及其引用链,判断是否存在未被垃圾回收的对象。
- 分析日志和代码 :查看应用的日志文件,寻找可能与内存泄露相关的异常信息或频繁创建大对象的操作。同时,审查代码中是否存在资源未正确释放、集合对象无限制增长等问题。
- 压力测试 :通过模拟高并发等压力场景,观察内存使用情况的变化,复现内存泄露问题,进一步缩小问题范围。
堆内存溢出查看指标
问题分析 :在查看堆内存溢出时,需要关注一系列关键指标来帮助定位问题。
查看指标 :
- 堆内存使用率 :监控堆内存的使用率,若持续增长并接近100%,则可能存在内存泄露或内存分配不足的问题。
- GC频率和耗时 :频繁的FullGC且每次回收的内存较少,可能导致堆内存溢出。观察GC日志中的GC频率、每次GC回收的内存大小、GC耗时等指标。
- 对象创建和存活情况 :通过内存分析工具,查看堆内存中对象的分布情况,包括不同类型的对象数量、占用内存大小等,找出可能存在异常的对象增长情况。
- JVM参数配置 :检查JVM的堆内存大小、新生代和老年代比例、GC算法等参数配置是否合理,是否存在堆内存设置过小或参数不合理导致的内存溢出问题。
解决超卖问题
问题分析 :超卖问题通常出现在高并发场景下,多个请求同时修改库存等资源导致资源超限。
解决思路 :
- 乐观锁控制 :在更新库存等资源时,采用乐观锁机制,通过版本号或更新时间戳等方式,确保每次更新操作都是基于最新的数据状态,防止并发更新导致的数据不一致和超卖。
- 分布式锁 :利用分布式锁(如Redis锁、Zookeeper锁)对关键操作进行控制,确保在同一时刻只有一个请求能够修改资源,保证操作的原子性。
- 库存预扣减 :在用户下单时,先预扣减库存,若库存足够则继续后续流程;若库存不足则拒绝下单请求,避免超卖情况的发生。
- 消息队列缓冲 :将下单请求放入消息队列中,逐步处理订单并扣减库存,通过消息队列的缓冲作用,平滑高并发请求,确保库存扣减操作的有序进行。
数据库ID选择雪花ID
问题分析 :为什么数据库的ID不用自增ID而是用雪花ID,需要从分布式系统的需求和特性角度进行分析。
原因分析 :
- 分布式环境需求 :在分布式系统中,多个节点可能同时生成ID,自增ID无法保证全局唯一性,而雪花ID通过结合时间戳、机器ID和序列号等信息,能确保在分布式环境下生成的ID是全局唯一的。
- 性能考虑 :自增ID在高并发情况下可能会出现性能瓶颈,如数据库的自增锁机制等。雪花ID的生成是基于算法计算,不依赖数据库,性能更高,能更好地适应高并发场景。
- 数据分片和扩展性 :雪花ID中包含机器ID等信息,便于在数据分片时进行路由和管理,有利于系统的扩展和维护。
- 避免热点问题 :自增ID可能导致某些表的热点行问题,影响数据库的性能。雪花ID由于其分布式的特性,能更好地分散数据访问热点。
单例模式线程安全
问题分析 :单例模式在多线程环境下是否存在线程安全问题,需要根据其实现方式进行判断。
情况分析 :
- 懒汉式单例 :如果采用懒汉式单例(在getInstance方法中进行实例创建),在多线程环境下,可能会出现多个线程同时进入同步块,导致创建多个实例,存在线程不安全问题。需要通过双重检查锁定(Double-Checked Locking)等机制来确保线程安全。
- 饿汉式单例 :饿汉式单例在类加载时就创建实例,不存在多线程创建实例的问题,天然线程安全。
- 静态内部类式单例 :利用Java的类加载机制,静态内部类在第一次被使用时才加载,从而实现懒加载且线程安全。
Java程序到运行的过程
问题分析 :编写Java程序到运行经历了多个步骤,包括编译、加载、执行等。
过程描述 :
- 编写代码 :使用Java编程语言编写源代码文件(.java文件)。
- 编译 :通过Java编译器(javac)将源代码文件编译成字节码文件(.class文件),字节码是Java虚拟机(JVM)可理解的中间代码。
- 类加载 :当运行Java程序时,JVM的类加载器将字节码文件加载到内存中,进行类的加载和链接操作,包括验证、准备和解析等步骤。
- 初始化 :对类进行初始化,执行静态初始化块和静态变量的赋值操作。
- 方法调用和执行 :JVM解释执行字节码指令,调用类中的方法,执行程序逻辑。在执行过程中,可能会进行即时编译(JIT)将字节码编译成机器码,提高执行效率。
- 程序结束 :当程序执行完毕,JVM进行资源回收和销毁操作,释放占用的内存等资源。
viloate关键字作用及JVM指令重排序原因
问题分析 :viloate关键字作用以及为什么JVM会指令重排序,以及如何加快运行速率。
viloate关键字作用 :viloate关键字通常用于定义违反某种规则或约束的情况,如在数据库中,当插入或更新数据违反了表的约束条件(如唯一约束、外键约束等)时,会触发相应的错误处理机制,确保数据的完整性和一致性。
JVM指令重排序原因及加快运行速率 :
- 原因 :JVM进行指令重排序是为了优化程序的执行性能。现代CPU具有指令流水线和乱序执行等特性,通过重新排列指令的执行顺序,可更好地利用CPU的资源,减少因等待某些操作完成而产生的空闲时间,提高程序的执行效率。
- 加快运行速率 :指令重排序使得CPU能够在等待某些较慢操作(如内存读写)的同时,执行其他不依赖于这些操作的指令,充分利用CPU的并行处理能力,从而加快整个程序的运行速率。同时,JVM的即时编译器(JIT)也会根据运行时的情况,对字节码进行进一步的优化,包括指令重排序,以提高执行性能。
防抖和节流实现
问题分析 :防抖和节流是优化高频事件处理的常用技术,需要了解它们的实现原理和方法。
实现思路 :
- 防抖(Debounce) :在事件被触发后,设置一个延迟时间,在这个延迟时间内如果事件再次被触发,则重新计时;只有当延迟时间结束后事件没有再次被触发,才执行相应的处理函数。可使用定时器来实现,每次事件触发时清除之前的定时器并重新设置新的定时器。
- 节流(Throttle) :在一定时间间隔内,只允许事件处理函数执行一次。可采用时间戳或计数器的方式,记录上次执行处理函数的时间或次数,当事件触发时判断是否达到设定的时间间隔或次数间隔,若满足条件则执行处理函数,否则忽略当前触发的事件。
服务器大量请求超时排查
问题分析 :服务器大量请求超时可能是由多种原因引起的,如网络问题、服务器性能不足、应用代码问题等。
排查步骤 :
- 网络排查 :检查服务器与客户端之间的网络连接是否正常,是否存在网络延迟过高、丢包或带宽不足等问题。可使用网络诊断工具如ping、traceroute等进行测试。
- 服务器性能检查 :查看服务器的CPU、内存、磁盘I/O等资源使用情况,判断是否存在性能瓶颈。若服务器资源耗尽,可能导致无法及时处理请求,造成超时。
- 应用代码分析 :审查应用代码中是否存在性能问题,如数据库查询效率低、算法复杂度过高、线程阻塞等。通过日志分析、性能测试工具等手段定位代码中的性能瓶颈。
- 服务器配置检查 :检查服务器的配置参数,如Web服务器的连接数限制、超时设置、线程池大小等,是否合理。不合理的配置可能导致服务器无法高效地处理大量请求。
栈溢出对其他进程的影响
问题分析 :栈溢出是否会对其他进程造成影响,需要从操作系统进程隔离机制角度进行分析。
影响分析 :
- 现代操作系统采用进程隔离机制 :每个进程拥有独立的地址空间和资源,栈溢出通常只会导致当前进程的栈空间被破坏,进而可能引发当前进程的崩溃或异常行为。其他进程由于运行在独立的地址空间中,不会直接受到影响。
- 特殊情况 :如果栈溢出导致系统资源(如内存、CPU)被过度消耗,可能间接影响其他进程的性能,如导致系统整体响应变慢、其他进程获取资源困难等。此外,若栈溢出引发安全漏洞(如缓冲区溢出攻击),攻击者可能利用漏洞进一步危害系统中其他进程或数据的安全。
程序在计算机上运行的过程
问题分析 :程序从编写到在计算机上运行,涉及到多个步骤和层次的处理。
运行过程 :
- 编写源代码 :使用编程语言编写源代码文件。
- 编译或解释 :对于编译型语言,将源代码编译成机器码或中间代码;对于解释型语言,通过解释器逐行解释执行源代码。
- 链接 :将编译后的代码与所需的库文件等进行链接,生成可执行文件。
- 加载到内存 :操作系统将可执行文件加载到内存中,分配必要的内存空间和资源。
- CPU调度和执行 :操作系统根据进程调度算法,将CPU时间片分配给程序的进程或线程,CPU执行程序中的指令,完成相应的操作。
- 与操作系统交互 :程序在运行过程中,可能通过系统调用与操作系统进行交互,访问文件系统、网络资源等。
- 程序结束 :当程序执行完毕或遇到错误终止时,操作系统回收分配给程序的内存和资源。
设置线程超时返回
问题分析 :需要启动一个线程去完成某一个工作,耗时不确定,要设置一个超时时间,不管运不运行完都要返回。
设计思路 :
- 使用Future和ExecutorService :通过Java的ExecutorService创建线程池,提交任务到线程池并获取Future对象。调用Future的get方法时,可设置超时时间,若在超时时间内任务未完成,则抛出TimeoutException异常,此时可进行相应的超时处理并返回。
- 定时器线程 :创建一个定时器线程,在主线程中启动工作线程后,启动定时器线程,让其等待设定的超时时间。若超时时间到达,工作线程仍未完成,则通过中断或其他方式通知工作线程停止运行,并让主线程返回。
- 异步回调 :采用异步回调机制,工作线程在完成任务后通过回调通知主线程。同时,设置一个超时监控机制,若在超时时间内未收到回调通知,则认为任务超时,主线程进行相应处理并返回。
MySQL和Redis使用Kafka解耦后的数据一致性
问题分析 :当MySQL和Redis使用Kafka解耦之后,若有一部分失败导致数据不一致,需要采取措施来保证数据最终一致性。
解决方法 :
- 消息重试机制 :在Kafka消费者端,对失败的消息进行记录和重试。可设置重试队列或延迟队列,定期重新消费失败的消息,直到处理成功或达到最大重试次数。
- 数据校验和补偿 :定期对MySQL和Redis中的数据进行校验,找出不一致的数据记录。对于不一致的数据,根据业务规则进行补偿操作,如重新同步数据、回滚错误操作等。
- 分布式事务 :在数据更新时,采用分布式事务机制,确保MySQL和Redis的操作要么全部成功,要么全部失败回滚,从而保证数据一致性。但分布式事务实现较为复杂,需根据业务场景谨慎使用。
- 最终一致性设计 :在系统设计时,接受一定时间内的数据不一致,但通过合理的业务流程和数据处理逻辑,确保在最终达到数据一致性。例如,对于某些非实时性要求高的数据更新,可设置异步处理和延迟校验机制。
Bitmap的作用及使用场景
问题分析 :Bitmap是一种高效的数据结构,具有特定的作用和适用场景。
作用及场景 :
- 空间高效存储 :Bitmap用每一位表示一个布尔值或状态,能极大地节省存储空间。适用于需要存储大量二进制状态信息的场景,如文件系统的磁盘空间位图,记录哪些磁盘块已被占用,哪些为空闲。
- 快速查询和操作 :Bitmap支持高效的位操作,如按位与、或、异或等,能快速进行集合运算和状态查询。例如,在数据库中可利用Bitmap索引加速查询操作,对于某些范围查询或多条件组合查询,Bitmap索引能快速定位符合条件的记录。
- 布隆过滤器 :Bitmap是布隆过滤器的基础数据结构,用于快速判断元素是否存在于集合中,适用于需要进行大规模数据存在性检测的场景,如网页爬虫中的URL去重。
- 分布式系统中的位图 :在分布式系统中,可用于记录资源的分配和使用情况,如分布式锁的位图实现,通过Bitmap快速判断资源是否被占用,提高资源分配的效率。
微博评论系统设计
问题分析 :微博成千上万的评论,一个评论可能还会有很多回复,需要设计一个高效、可扩展的评论系统。
设计思路 :
- 数据模型设计 :建立评论和回复的数据模型,评论包含评论ID、用户ID、微博ID、评论内容、创建时间等属性;回复则关联到对应的评论ID,包含回复ID、用户ID、回复内容、创建时间等属性。
- 分层存储 :采用分层存储策略,热门微博的评论和回复可缓存在内存数据库如Redis中,提高读取速度;较早或访问量较低的评论数据可存储在关系型数据库中,定期进行归档处理。
- 分页和懒加载 :在前端展示时,对评论进行分页处理,每次只加载一定数量的评论,减少数据传输量。对于评论下的回复,可采用懒加载方式,当用户点击查看回复时才进行数据加载。
- 异步处理和消息队列 :当用户发表评论或回复时,先将请求放入消息队列中,异步进行数据存储和处理,提高系统的响应速度和吞吐量,避免高并发写入导致的阻塞。
- 索引和查询优化 :在数据库中对微博ID、创建时间等字段建立索引,优化评论的查询性能。对于复杂的查询条件,如按点赞数排序等,可采用预计算或物化视图等技术。
悲观锁与乐观锁的使用场景
问题分析 :在业务上,什么情况使用悲观锁,什么情况使用乐观锁,需要根据数据的并发访问特点和业务需求进行判断。
使用场景 :
- 悲观锁适用场景 :当数据的并发冲突概率较高,且数据更新操作较为频繁,对数据一致性要求极高时,适合使用悲观锁。例如,在库存管理系统中,高并发的下单操作可能导致库存超卖,此时采用悲观锁对库存进行加锁,确保每次更新操作的独占性,防止并发冲突。
- 乐观锁适用场景 :当数据的并发冲突概率较低,且希望减少锁的开销和提高系统的并发性能时,适合使用乐观锁。例如,在一些读多写少的场景,如文章编辑系统,用户很少同时编辑同一篇文章,此时采用乐观锁,在提交更新时通过版本号等机制判断是否发生冲突,若冲突则提示用户进行相应处理。
多线程查多个结果集的主线程等待
问题分析 :使用多线程去查多个结果集,主线程需要知道前面的线程执行完了并且得到结果集,需要采用合适的线程同步机制。
实现方法 :
- Join方法 :主线程调用子线程的join方法,等待所有子线程执行完毕后再继续执行。但这种方式会使主线程阻塞,直到所有子线程完成,可能影响主线程的响应速度。
- CountDownLatch :在Java中,可使用CountDownLatch来实现主线程等待多个子线程完成。主线程初始化一个CountDownLatch,计数等于子线程数量,每个子线程在执行完毕后调用countDown方法减少计数,主线程调用await方法等待计数为0时继续执行。
- Future和ExecutorService :通过ExecutorService提交任务获取Future对象,主线程遍历所有Future对象,调用get方法等待任务完成并获取结果。这种方式可在获取结果的同时进行其他操作,提高线程利用率。
- CompletableFuture :Java 8引入的CompletableFuture提供了更灵活的异步编程模型,可通过thenApply、whenComplete等方法链式处理异步任务的结果,主线程可方便地等待所有异步任务完成并获取结果集。
帖子最热排行及三元组存储
问题分析 :如何对帖子按照最热进行排行,以及用户点赞/关注这个三元组(如果数据量很大)怎么存储查询。
解决思路 :
- 最热排行实现 :定义“最热”的衡量标准,如点赞数、评论数、分享数等的加权和。在数据库中,为每个帖子记录这些指标数据,并定期根据这些数据计算热度值,按照热度值进行排序。为了提高查询效率,可建立热度值的索引,或者采用缓存策略,将热门帖子缓存在内存中。
- 三元组存储和查询 :对于用户点赞/关注的三元组(用户ID、帖子ID、操作类型),由于数据量较大,可采用以下方法: 数据库优化 :建立合适的索引,如对用户ID和帖子ID的组合索引,提高查询效率。对于频繁的查询操作,如查找某个用户点赞的所有帖子,可通过索引快速定位。分布式数据库或缓存 :将数据分布到多个数据库节点或缓存服务器中,如采用一致性哈希等分片策略,根据用户ID或帖子ID进行数据分片,提高数据存储和查询的性能。数据压缩和归档 :对于较早或不活跃的三元组数据,可进行压缩存储或归档到低成本存储中,减少在线数据量,提高活跃数据的查询效率。
1000w URL排序,10M内存
问题分析 :在只有10M内存的情况下对1000w URL进行排序,面临内存不足的问题,需要采用外部排序等方法。
排序步骤 :
- 分块读取和排序 :将URL数据分成多个小块,每个小块的大小不超过10M内存限制。对每个小块进行内部排序,如使用快速排序或归并排序等算法。
- 外部归并排序 :对排序后的小块文件进行k路归并。每次从各个小块中读取一部分已排序的URL,利用最小堆等数据结构找出当前最小的URL,依次写入到最终的排序结果文件中。在读取和写入过程中,合理控制缓冲区大小,优化磁盘I/O操作。
- 多线程优化 :可采用多线程技术,同时对多个小块进行排序,提高排序效率。在归并阶段,也可通过多线程的方式并行处理多个归并任务,加快整体排序速度。
商品秒杀库存设计
问题分析 :一个商品1000万库存,20w秒杀,只用设计减库存环节,需要确保在高并发下库存的准确性和操作的高效性。
设计思路 :
- 数据库优化 :对库存表建立索引,优化查询和更新操作。采用高性能的数据库引擎,如InnoDB,并合理设置参数,提高并发处理能力。
- 缓存策略 :将库存数量缓存在内存数据库如Redis中,秒杀操作先从缓存中扣减库存,若成功则后续再同步到数据库。这样可减少对数据库的直接压力,提高响应速度。
- 并发控制 :在扣减库存时,采用分布式锁或乐观锁机制,防止并发操作导致库存超卖。例如,使用Redis的SETNX命令实现分布式锁,在扣减库存前获取锁,操作完成后释放锁。
- 异步处理 :秒杀成功后,将订单生成和库存更新等操作放入消息队列中异步处理,确保在高并发情况下系统的稳定性和响应速度。
- 监控和预警 :实时监控库存数量和秒杀请求的处理情况,当库存接近临界值或出现异常时及时进行预警和处理。
快速定位五分钟内重复登录两次的QQ号
问题分析 :需要快速定位到五分钟内重复登录了两次的QQ号,选择合适的数据结构来记录和查询登录信息。
数据结构选择及实现 :
- 哈希表+时间戳列表 :使用哈希表,键为QQ号,值为一个按时间戳排序的列表,记录该QQ号每次登录的时间戳。当需要查询时,对于每个QQ号,检查其列表中是否存在两个时间戳,其时间差在五分钟以内。
- 滑动窗口队列 :对于每个QQ号,维护一个队列,保存最近的登录时间戳。每当有新的登录时,将时间戳加入队列,并移除队列中超过五分钟的时间戳。若队列的大小达到2,则说明存在重复登录两次的情况。
- 布隆过滤器+计数器 :若只需要判断是否存在重复登录两次的情况,可结合布隆过滤器和计数器。布隆过滤器用于快速判断某个QQ号在近五分钟内是否登录过,若通过布隆过滤器判断可能登录过,则再通过精确的计数器或时间戳记录进行验证,避免误判。
大数据join操作
问题分析 :假设有100G的数据a和100G的数据b,需要用a join b得到c,而一个mysql数据库只能操作10G的数据,需要采用分步处理和分布式计算的方法。
处理方法 :
- 数据分片 :将数据a和数据b按照一定的规则(如哈希分片、范围分片)分成多个小块,每个小块的大小不超过10G。确保在分片时,join的关键字分布均匀,以便后续的join操作能在各个分片上正确执行。
- 分片join :在每个分片对(a的分片和对应的b的分片)上,使用mysql进行join操作,得到中间结果。
- 合并结果 :将所有分片join得到的中间结果进行合并,得到最终的join结果c。在合并过程中,注意处理可能的数据重复和排序问题。
- 分布式计算框架 :可采用分布式计算框架如Hadoop的MapReduce或Spark,将join操作分布到多个节点上并行处理。在Map阶段,对数据进行分片和预处理;在Reduce阶段,对分片后的数据进行join操作,并最终合并结果,提高处理效率。
腾讯会议通信实现
问题分析 :腾讯会议需要实现多方实时通信,涉及到音视频数据的传输、同步等问题。
实现思路 :
- 网络架构 :采用P2P(对等网络)或通过服务器中转的方式进行通信。P2P方式可减少服务器负担,但需要解决NAT穿透等问题;服务器中转方式则需要建立高并发、低延迟的服务器架构。
- 音视频编解码 :使用高效的音视频编解码技术,如H.264、H.265、opus等,对音视频数据进行压缩和解压缩,降低数据传输量,提高传输效率和质量。
- 实时传输协议 :采用RTP(实时传输协议)等协议进行音视频数据的传输,确保数据的实时性和顺序性。同时,使用RTCP(实时传输控制协议)进行传输质量的监控和反馈,动态调整传输参数。
- 同步机制 :为保证多方音视频的同步播放,采用时间戳同步、唇音同步等技术,确保各方的音视频数据在播放时保持同步。
- QoS保障 :实施QoS(服务质量)策略,如带宽自适应调整、丢包重传、前向纠错等,提高通信的稳定性和流畅度,尤其在网络状况不佳时仍能保持较好的通信质量。
分布式数据库订单号方案
问题分析 :设计一个QPS一百万的分布式数据库的订单号方案,需要确保订单号的唯一性、生成的高效性以及可扩展性。
方案设计 :
- 雪花算法(Snowflake) :结合时间戳、机器ID和序列号生成唯一订单号。时间戳可精确到毫秒,机器ID标识不同的生成节点,序列号在同一节点同一毫秒内的不同请求进行区分。该算法生成的订单号具有全局唯一性,且有序性有利于数据库的索引和查询。
- Twepoch优化 :对雪花算法进行优化,调整时间戳的起始时间(epoch)为业务开始的时间,减少时间戳部分的位数占用,增加序列号部分的位数,适应更高的并发请求。
- 分布式ID服务 :建立独立的分布式ID生成服务,采用上述算法生成订单号,通过高性能的RPC框架供分布式数据库和其他业务系统调用。确保ID生成服务的高可用性和可扩展性,如采用集群部署、负载均衡等技术。
- 缓存和预生成 :在ID生成服务中,可预生成一批订单号并缓存,当有请求时直接分配缓存中的订单号,减少生成时间,提高响应速度。同时,监控缓存的使用情况,及时进行补充。
海外数据延迟改善
问题分析 :从国内将数据发送到海外延迟比较大,需要采取措施改善数据传输的延迟问题。
改善方法 :
- CDN加速 :使用内容分发网络(CDN)服务,在海外部署CDN节点,将数据缓存到离用户较近的节点,减少数据传输的距离和延迟。用户请求数据时,可从最近的CDN节点获取,提高访问速度。
- 数据压缩 :对传输的数据进行压缩,减少数据量,从而降低传输时间和延迟。可采用通用的压缩算法如gzip、zlib等,或针对特定数据类型的专用压缩算法。
- 优化传输协议 :采用更高效的传输协议,如QUIC协议,它结合了TLS加密和UDP传输的优点,减少了连接建立的延迟和数据传输的延迟,提高传输效率。
- 建立海外数据中心 :在海外建立数据中心,将部分数据和业务逻辑部署到海外,实现数据的本地化存储和处理,从根本上减少跨境数据传输的延迟。
- 数据预取和缓存 :根据用户的行为和偏好,预测可能需要访问的数据,提前将数据预取并缓存到海外的缓存服务器中,当用户实际请求时可快速返回。
大文件存储用户详情系统设计
问题分析 :一个大文件存储了上亿个或者更多的用户id,客户端要根据id请求用户详情,需要设计一个高效、可扩展的系统。
设计思路 :
- 数据分片和存储 :将大文件按照一定的规则(如哈希分片、范围分片)分成多个小文件,存储在分布式文件系统(如HDFS、Ceph)或对象存储(如S3)中,提高数据的存储和访问效率。
- 索引建立 :为用户id建立索引,如采用分布式搜索引擎(如Elasticsearch)对用户id进行索引,客户端请求时可通过索引快速定位到对应的用户详情数据所在的分片。
- 缓存策略 :将热点用户详情数据缓存在内存缓存系统(如Redis、Memcached)中,提高频繁访问数据的响应速度。采用合理的缓存淘汰策略,如LRU(最近最少使用),确保缓存的有效性和利用率。
- API设计和负载均衡 :设计RESTful API供客户端请求用户详情,后端通过负载均衡器将请求分发到多个处理节点,每个节点负责处理一部分数据分片的请求,提高系统的并发处理能力和扩展性。
- 数据更新和同步 :当用户详情数据发生更新时,采用消息队列(如Kafka)通知各处理节点和缓存系统,及时更新数据和缓存,确保数据的一致性和准确性。
大表数据插入设计
问题分析 :一个表每天有1000万条数据插入,需要设计数据库表以应对海量数据的高效插入和存储。
设计方法 :
- 分区表设计 :对表进行分区,如按日期、业务类型等进行范围分区或列表分区,将数据分散到不同的分区中,提高插入和查询的效率。在插入数据时,可直接定位到对应的分区,减少锁表和数据检索的时间。
- 索引优化 :合理建立索引,对于插入操作频繁的字段,可暂时不建立索引,待数据批量插入完成后统一建立索引,减少插入过程中的索引维护开销。
- 批量插入 :采用批量插入的方式,将多条数据组装成一个批次进行插入操作,减少I/O操作和事务提交的次数,提高插入效率。
- 异步处理和消息队列 :将插入请求放入消息队列中,通过消费者线程异步进行数据插入操作,避免高并发插入请求直接冲击数据库,同时可进行数据的预处理和校验,提高数据质量。
- 硬件和数据库参数调优 :升级数据库服务器的硬件配置,如增加内存、使用SSD硬盘等,提高数据库的存储和处理性能。同时,优化数据库的参数配置,如调整缓存大小、连接数等,适应高并发插入的场景。
高并发抢票系统设计
问题分析 :设计一个类似12306的高并发抢票系统,需要应对海量用户的并发访问和复杂的业务逻辑。
设计思路 :
- 用户队列和预处理 :用户请求先进入队列系统,如使用Kafka等消息队列,对请求进行缓冲和预处理,如验证用户身份、检查票源等,避免直接冲击数据库。
- 票源管理和并发控制 :采用分布式缓存(如Redis)存储票源信息,使用分布式锁或乐观锁机制控制票源的并发访问,防止超卖和重复售票。同时,对票源进行实时监控和预警,及时发现余票不足等情况。
- 异步任务和分布式架构 :将订单生成、支付处理等操作设计为异步任务,通过分布式任务调度框架(如xxl-job)进行管理和调度,提高系统的响应速度和吞吐量。采用微服务架构,将系统拆分成多个独立的服务模块,如用户服务、票源服务、订单服务等,每个模块可根据自身的特点进行优化和扩展。
- CDN和静态资源优化 :将静态资源(如图片、CSS、JS文件等)部署到CDN上,提高静态资源的加载速度。对前端页面进行优化,如采用懒加载、分页加载等技术,减少页面加载时间和数据传输量。
- 数据一致性和容错性 :建立完善的数据一致性和容错性机制,如采用分布式事务、消息补偿等技术,确保在高并发和复杂网络环境下数据的准确性和完整性。对系统可能出现的故障进行预演和演练,制定相应的应急预案,提高系统的可靠性和稳定性。
QQ群禁言功能设计
问题分析 :设计QQ群的禁言功能,需要考虑如何有效地管理和执行禁言操作。
设计思路 :
- 权限控制 :只有群主和管理员具有设置和解除禁言的权限,普通群成员不能进行此类操作。在系统中,对用户的身份和权限进行严格验证,确保只有合法用户才能执行禁言操作。
- 禁言数据结构 :建立禁言列表,记录被禁言的用户ID、禁言开始时间和禁言时长等信息。可采用数据库或缓存进行存储,缓存用于快速查询和判断用户是否被禁言,数据库用于持久化存储,防止数据丢失。
- 消息过滤和拦截 :当群成员发送消息时,系统先检查该用户是否在禁言列表中。如果是,则拦截消息,不进行转发,并可根据需要向发送者返回提示信息;如果不是,则正常处理和转发消息。
- 定时任务和提醒 :设置定时任务,定期检查禁言列表中用户的禁言时长,当禁言时间到期时,自动解除禁言,并通知相关人员(如被禁言者、群主等)。同时,可提供提醒功能,在禁言时间即将到期时提醒相关人员。
- 日志和审计 :对禁言操作进行日志记录,包括操作人、被操作人、操作时间和原因等信息,方便后续的审计和查询,确保群管理的透明性和规范性。
网络服务器设计
问题分析 :设计一个网络服务器,有【多线程 + 每个线程内部阻塞IO】和【单线程 + Epoll】这两种方案,需要比较它们的特点并进行优化。
方案特点分析 :
- 多线程 + 每个线程内部阻塞IO :CPU负载 :在高并发情况下,创建大量线程可能导致CPU负载过高,线程切换频繁,消耗较多CPU资源。时间效率 :阻塞IO在等待数据时线程处于阻塞状态,无法利用CPU时间,可能降低时间效率。内存资源占用 :每个线程都需要占用一定的内存资源(如线程栈),大量线程可能导致内存资源占用较高。
- 单线程 + Epoll :CPU负载 :单线程运行,CPU负载相对较低,但在高并发大量就绪socket需要处理时,单线程可能成为瓶颈,导致CPU负载升高。时间效率 :Epoll能高效地处理大量socket的就绪状态,减少不必要的轮询,提高时间效率。内存资源占用 :单线程占用的内存资源相对较少,但需要考虑Epoll相关数据结构的内存开销。
优化方法 :
- 采用多线程 + Epoll结合的方式 :在服务器中创建多个工作线程,每个线程维护一个Epoll实例,负责处理一部分socket连接。通过合理分配连接到不同的线程,既能利用多核CPU的优势,又能高效地处理IO事件,提高整体性能。
- 线程池管理 :建立线程池,根据服务器的负载情况动态调整线程数量,避免线程过多导致资源耗尽或线程过少导致处理能力不足。同时,对线程的生命周期进行管理,减少线程创建和销毁的开销。
- 异步非阻塞IO :在可能的情况下,采用异步非阻塞IO模型,如在Linux中的io_uring,进一步提高IO操作的效率和并发能力。
- 连接复用和缓冲区优化 :对socket连接进行复用,减少连接建立和断开的开销。同时,优化读写缓冲区的大小和使用方式,减少数据拷贝和系统调用的次数,提高数据传输效率。
日志分析最大在线人数
问题分析 :给定大量日志,每条日志记录了用户id、登陆时间、登出时间,要求求出一天内的最大在线人数及维持在最大在线人数的最长持续时间。
解决思路 :
- 事件排序 :将所有用户的登陆和登出事件按照时间顺序进行排序,形成一个时间线。
- 遍历事件计算在线人数 :初始化当前在线人数和最大在线人数变量。遍历时间线上的每个事件,若为登陆事件,则当前在线人数加1;若为登出事件,则当前在线人数减1。每次更新当前在线人数后,与最大在线人数进行比较,若超过则更新最大在线人数,并记录此时的时间点和持续时间的起始时间。
- 计算最长持续时间 :在遍历过程中,当当前在线人数等于最大在线人数时,记录时间区间,直到当前在线人数不等于最大在线人数时,计算该区间的持续时间,并与之前记录的最长持续时间进行比较,保留最长的持续时间。
- 处理同时登录和登出的情况 :在事件排序时,若多个事件的时间相同,需按照事件类型进行排序,如登陆事件优先于登出事件,确保计算的准确性。
Spring Boot上传大文件配置
问题分析 :在Spring Boot中,如果不做配置等优化,post接口上传1个G文件可能会失败,需要了解其原因及解决方法。
原因及解决方法 :
- 默认配置限制 :Spring Boot的默认配置对上传文件的大小有限制,通常最大为1MB或10MB左右,超过限制的文件会被拒绝上传。
- 配置修改 :在application.properties或application.yml文件中,修改spring.servlet.multipart.max-file-size和spring.servlet.multipart.max-request-size属性,将其值设置为合适的大小,如1GB,以允许上传大文件。
- 服务器和容器限制 :除了Spring Boot的配置,还需检查服务器(如Tomcat、Jetty)的相关配置,确保其支持大文件上传,如调整Tomcat的maxPostSize参数。
- 内存和性能考虑 :上传大文件可能会占用较多的内存和系统资源,可能导致内存溢出或性能下降。可采用流式上传的方式,避免将整个文件加载到内存中,同时对服务器的资源进行监控和优化,确保系统的稳定运行。
查找两个大文件共同的URL
问题分析 :给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,要求找出a、b文件共同的url。
解决方法 :
- 哈希分片 :将两个文件按照一定的哈希函数进行分片,如对url进行哈希运算,根据哈希值的范围或取模操作将url分配到不同的分片文件中。确保相同的url在两个文件中被分配到相同的分片。
- 分片处理和比较 :对每个分片对(a的分片和b的分片)进行处理,将分片中的url读入内存,建立哈希表或排序后进行比较,找出共同的url。由于每个分片的大小在内存限制范围内,可高效地进行比较操作。
- 合并结果 :将所有分片处理得到的共同url进行合并,得到最终的共同url集合。
- 分布式计算框架 :可采用分布式计算框架如MapReduce,将分片处理任务分布到多个节点上并行执行,提高处理效率。在Map阶段,对url进行分片和预处理;在Reduce阶段,对分片后的url进行比较和合并操作。
朋友圈设计:a发朋友圈后,b是怎么知道有人发朋友圈的
- 异步发布:用户A发圈后,内容先本地缓存并异步上传至服务端,再插入好友(如用户B)的时间轴。
- 双重权限过滤:服务端校验可见范围(如分组、屏蔽规则),客户端二次过滤无权限内容。
- 实时红点通知:Redis状态管理:记录用户B的未读动态状态(如feed_unread:1)。长连接推送:通过WebSocket主动推送红点更新,触发朋友圈入口角标提示。