
Java常见场景题1、如何实现10亿条数据判重1、如果这是实时流如日志、点击流且内存有限允许极低概率的误判可以选择 布隆过滤器本地内存Guara或Redis版。-- 思路1布隆过滤器存在不一定存在不存在则一定不存在2、如果是离线处理一批文件需要100%准确可以选择 哈希分片 文件Set或者使用 Spark 进行离线清洗。-- 思路2 分块处理将10亿数据分成多个小块每块在可接受的内存范围内。然后对每个小块进行判重并将结果保存到另一个集合中。最后对这个集合进行判重以得到最终的不重复数据。3、如果这是业务数据库导入需要确保唯一性并且数据是增量的可以考虑 分库分表的数据库 或 支持去重的大数据OLAP引擎。-- 思路3可以使用大数据组件HDFS、clickhouse等。如果是使用数据库的话需要分表存储2、线程池核心线程数为0任务队列为10亿最大线程数为10.。。当核心线程数为 0 时当来了一个任务之后会先将任务添加到任务队列同时也会判断当前工作的线程数是否为 0如果为 0则会创建线程来执行线程池的任务3、100万条数据插入redis如何设计实现方案思路数据分批处理、多个线程异步同事处理 插入前可以去重或压缩 (根据场景来定义)1批处理 -- redis客户端使用管道技术批量发送命令到服务端。Redis 批处理的手段有以下两个使用管道技术Pipeline允许客户端发送多个命令到服务器而不需要等待每个命令的回复。这减少了网络延迟的影响提高了写入速度。使用批量操作指令如使用 MSET 或 HMSET 命令可以一次设置多个键值对或哈希表字段这比单独使用 SET 或 HSET 要快得多。2数据分片 -- 服务端使用redis集群数据会分片存放。数据分片指的是使用 Redis 的分片功能将数据分布在多个 Redis 实例或节点上可以考虑使用 Redis 集群。集群模式下数据可以分布在多个节点上从而分散负载并提高写入吞吐量。3使用 Lua 脚本也可以通过 Lua 脚本将多个操作组合成一个原子操作减少客户端与服务器之间的通信次数。4异步加载将一个大任务分成多个小任务然后再通过异步加载的方式批量写入 Redis这样可以避免阻塞主线程提高应用的整体响应性。4、线上如何发现Kafka消息积压问题导致消息积压的原因有哪些原因消费者处理速度慢消费者的处理逻辑复杂或资源不足。生产者速度过快生产者生成消息的速度超过消费者的处理能力。生产者key分布不均造成数据倾斜。对于线上kafka 消息大量积压的问题我总结了这几点我们要做好监控和告警当消息积压到一定程度的时候就要告警通知负责人提前处理。不要上来就新建临时topic去快速处理大量积压问题。应该先排查是不是bug优化消费者的代码。如果消息设置了超时时间因为百万消息积压没来得及处理就过期清理可以设置定时任务拉起来重发一下。5、使用缓存有哪些坑你是怎么解决的防止雪崩 - TTL随机化 熔断。-- 定义大量的缓存数据在同一时间过期失效或者Redis节点宕机。此时大量请求直接打到数据库导致数据库压力剧增甚至崩溃。-- 解决方案: 1、过期时间打散 2、本地缓存 3、redis采用集群方式 4、限流防止击穿 - 互斥锁(分布式锁)。-- 定义:某一个热点Key失效某个爆款商品的缓存正好过期-- 解决方案-- 更新数据库加锁只让一个线程去数据库查询数据并重建缓存其他线程等待重建完成后从缓存获取-- 过期时间不设置物理过期时间而是在Value中存储一个逻辑过期时间字段。当检测到逻辑过期时尝试获取分布式锁由拿到锁的线程去更新缓存其他线程直接返回旧数据短暂的脏读保证系统可用防止穿透 - 布隆过滤器 空值缓存。-- 定义查询一个根本不存在的数据。由于缓存中没查到请求直接打到数据库比如恶意攻击-- 解决方案缓存空对象 即使数据库查询结果为空也在缓存中设置一个空值例如 null 或特殊标识并设置一个较短的过期时间如30-60秒。这样可以避免恶意请求穿透到DB布隆过滤器Bloom Filter 在缓存之前加一层布隆过滤器存储所有可能存在的Key。当请求过来时先判断Key是否可能存在。如果布隆过滤器说“不存在”则直接返回保证一致性 - 先更新DB后删缓存 兜底binlog。需要容忍redis和数据库短暂的数据不一致问题。6、如何避免超卖和少卖问题超卖卖出了超过库存数量的商品少卖本来有货但被限制购买影响用户体验方案一数据库乐观锁-- 适用场景适用于小型应用或低频场景-- 原理在数据库表如 stock 表中增加一个版本号字段 version优点实现简单无锁冲突缺点不适合高并发场景并发高时大量请求会失败。用户体验就是一直抢不到少卖方案二利用Redis单线程 Lua脚本主流方案这是目前应对高并发秒杀、抢购最成熟的技术方案。利用 Redis 的单线程特性和 Lua 脚本的原子性将所有判断和扣减逻辑封装在脚本中执行库存预热活动开始前通过后台将库存数量写入 Redis通过redis来扣减库存适用lua脚本保证操作的原子性执行与落库前端请求进来后端执行该 Lua 脚本。如果 Redis 返回 1说明扣减成功此时可以异步发送消息给 MQ或写入队列由后端服务慢慢去创建真正的数据库订单。方案三分布式锁Redisson—— 适用于非秒杀的普通下单如果并发不是极端高例如 QPS 几千也可以用分布式锁来保护库存资源。如何避免“少卖”库存预热要准Redis 中的初始库存必须和数据库一致兜底Redis 扣减成功了但后续数据库订单创建失败了怎么办这会导致少卖解决方案1设置库存补偿。如果数据库落单失败发送一条消息重新加回 Redis 库存需要做好幂等解决方案1在解决方案1的基础上添加解决方案2定时对比redis和数据库的数据做好数据一致性保证。7、订单支付失败且支付客户端未响应该如何处理问题一要避免重复支付在支付超时场景下用户可能会因为不确定是否支付成功而尝试重新支付。这可能导致一个订单支付了两次。解决方案幂等性设计支付系统需要保证一笔订单只能被支付一次金额不能被累加前端轮训查询结果比如每隔几秒查一次查询订单状态定时对账订单系统 和 支付系统 要提供支付结果状态查询接口需要定时对账查询两个系统的支付状态是否一致。如果订单支付失败扣款成功那么就需要退款如果订单支付失败扣款失败则只需要通知用户重新下单即可8、如何实现百万级数据导入数据库风险如果一次性读取100万条数据到内存再处理可能会OOM。如果直接使用逐条INSERT可能会因为事务过大、网络开销或索引维护导致性能极差甚至影响线上业务方案一业务代码处理的话分批提交 批量INSERT 多线程并发插入通用方案如果你无法直接操作数据库文件如云数据库限制或者数据需要先经过业务逻辑处理只能使用INSERT语句那么推荐采用分批 批量的方式。方案二使用数据导入工具(数据迁移工具)直接操作数据库方案三插入一张临时表插入完成后再命名为业务查询的表这样可以避免插入过程耗时过长导致业务中断。9、百万级数据库分页查询比较慢该如何优化方案一延迟关联Deferred Join—— 最推荐思路先快速定位主键再回表查询完整数据原理利用覆盖索引快速查到需要的行的主键 ID然后用这些 ID 去关联原表获取所有列。这样可以避免数据库在扫描大量数据时加载不需要的行数据。方案二使用搜索引擎Elasticsearch如果分页条件非常复杂多字段组合排序、全文搜索且数据量巨大千万级以上数据库很难满足要求。10、某个频繁查询数据库的场景要怎么优化-- 分析性能是否达标-- 考虑使用缓存避免多次查询数据库-- 多次单条查询 - 单词批量查询11、设计数据库时表数据量过大有什么解决方案1、数据表分区将数据按条件分布到不同文件降低查询范围提高查询效率。 表拆分水平拆分 垂直拆分2、读写分离增加多个读实例应对读多写少的互联网场景。3、数据异构根据不同业务需求保存多份数据。 -- 冷热数据分离ES MySQL-- 热数据/事务数据放在MySQL支撑业务交易-- 引入ES需要模糊搜索、聚合统计4、热点数据处理注意处理经常查询的数据。12、有100万请求同时访问你的数据库该怎么办假设这是秒杀下单场景下的100万请求第一步请求先到网关进行IP限流和User-Agent校验过滤掉一部分爬虫。第二步请求进入应用集群尝试读Redis缓存例如校验库存。第三步校验通过后不直接写数据库而是将一条“创建订单”的消息发送到RocketMQ然后直接告诉用户“正在处理中”。第四步后端一个单独的消费者服务以固定的速率例如每秒1000条从MQ拉取消息批量写入数据库。13、实际开发中遇到过哪些慢查询的场景最经典的错误深分页Offset 过大索引失效函数操作或隐式转换关联查询过多未使用索引查询了不必要的行和列14、100万条数据插入HashMap会有什么问题-- 内存溢出OOM—— 最直接的风险-- 哈希冲突加剧 -- 如果哈希冲突严重链表会变得非常长一定要用HashMap的话指定初始容量避免频繁resize。估算内存确保JVM堆内存足够设置-Xmx。确保Key的hashCode均匀。15、高TPS问题: 一个EXCEL文件有千万条数据如何上传1、分片上传切片 多线程上传2、流式读取不要一次性加载所有数据到内存中3、批量入库高效写入4、异步处理 前端轮训查询结果16、如何防止短信盗刷和短信轰炸添加图形验证码用户发送短信前需要先输入正确的图形验证码或拖动验证码等验证验证通过之后才能正常发送短信验证码。因为图形验证码的破解难度非常大所以就避免了自动发送短信程序的执行。添加 IP 限制对请求 IP 的发送次数进行限制避免短信盗刷和短信轰炸的问题。例如每个 IP 每天只能发送 10 条短信。开启 IP 黑名单限制某个 IP 短信发送功能从而禁止自动发送短信程序的执行。限制发送频次一个手机号不能一直不停的发送验证码即使更换了多个 IP 也不行设置一个手机号每分钟内只能发送 1 次验证码一小时之内只能发送 5 次验证码一天之内只能发送 10 次验证码。开启短信提供商的防控和报警功能几乎所有的短信提供商都提供了异常短信的防控和提醒功能开启这些保护措施可以尽可能的避免短信盗刷的问题。17、如何设置生产环境下线程的数量第一步区分任务类型核心依据 -- 一般CPU密集型任务线程数 CPU核心数 1IO密集型任务CPU核心数 * 218、某个后端接口使用了多线程执行任务怎么样才能保证所有的线程任务已完成方案一使用 CountDownLatch计数器初始化一个计数器数值等于线程数。每个线程执行完毕后调用countDown()让计数器减一。主线程调用await()阻塞直到计数器归零方案二使用 Thread.join()如果自己创建和管理Thread对象可以使用join()方法。方案四使用 CompletableFuture最现代、最推荐原理使用allOf()等待所有异步任务完成然后组合结果。19、如何实现多级缓存前端资源缓存CDN 缓存后端缓存redis分布式缓存、本地缓存20、你是如何设计限流功能的方案A单机限流小规模/基础组件 -- Guava的RateLimiter令牌桶或Java Semaphore信号量控制并发数。方案B分布式限流微服务/集群-- nginx层做限流ALB 层 ---- 微服务网关层限流Sentinel21、项目中是如何使用多线程的-- 接口性能不达标异步处理任务-- 处理大数量量如导入excel时多个线程并行处理数据线程池 CompletableFuture 使用。22、如何设计一个高并发系统层级 核心技术 解决的问题接入层 CDN、Nginx、LVS 流量分发、静态加速、安全防护应用层 无状态化、集群、限流熔断 水平扩容、自我保护缓存层 多级缓存、Redis集群 加速读请求、保护数据库异步层 消息队列、线程池 削峰填谷、解耦数据层 读写分离、分库分表、NoSQL 突破单库存储与写入瓶颈一个典型混合使用的例子在秒杀系统中线程池在接收到请求后用线程池并发验证用户资格和库存都在本地缓存和本地JVM内。MQ验证通过后将创建订单的请求发往MQ由另一个服务异步处理真正的订单入库23、怎么避免重复下单的问题前端控制通过前端脚本防止用户重复提交表单但这并不能完全解决问题尤其是在网络错误的情况下。使用唯一请求 ID在用户进入订单提交界面时调用后端获取唯一 ID并在提交时检查该 ID 是否已使用。如果未使用则继续处理如果已使用则提示重复提交。数据库约束在数据库中设置主键唯一约束确保每个订单的唯一性。如果重复下单插入操作将失败。使用 Redis 锁在用户下单时为每个用户分配一把锁确保同一用户的多次请求中只有一个请求能成功执行。幂等性设计确保多次请求的效果与一次请求相同从而避免重复下单。24、线上环境偶尔出现重复的数据他的原因是啥如何分析和解决26、出现OOM的场景有哪些如何解决堆内存溢出Heap OOM原因堆内存不足通常由以下情况引起创建了大量对象且未及时释放、内存泄漏导致对象无法被GC回收。元空间溢出Metaspace OOM动态生成大量类如使用CGLIB动态代理减少动态类生成优化类加载逻辑。检查类加载器是否正确实现资源释放。栈内存溢出Stack OOM原因线程栈内存不足通常由以下情况引起递归调用过深。堆外内存溢出Direct Memory OOM原因使用NIO分配了过多的直接内存Direct Memory未及时释放27、如何排查生产环境JVM问题生产环境中的JVM问题通常表现为应用变慢、CPU飙升、频繁GC、甚至直接宕机。这些问题往往不是单一原因导致而是多种因素叠加的结果。28、如何排查CPU间接性100%的问题top - 进程top -Hp 找线程打印线程快照信息使用jstack29、线上用什么命令可以看JVM使用的是什么垃圾收集器jinfo -flags [pid] | grep -i gc30、项目再本地能运行但发布线上就运行不了你分析一下原因1、配置问题1.1. 配置文件未生效或配置错误2、环境问题如第三方服务无法连接、数据库连接不上31、线上某个接口执行非常慢应该如何排查和处理运维监控 链路追踪32、如果有大量请求访问redis中一个很大的value这种情况拆分成多个小Value将大Value 拆分成多个小Value使用 mget 批量读取数据压缩启用内存淘汰策略33、redis宕机后过期数据是否存在34、Springboot某个接口返回值有敏感字段怎么脱敏方式一AOP切面处理方式二写一个公共的工具类35、什么机制能保证任务只被集群中的一台机器执行-- 分布式锁-- 分布式任务调度框架36、推送平台对接多家厂商使用哪些设计模式策略模式 工厂模式最核心适配器模式模板方法模式37、SQL稳定性问题同一个SQL查询where中查询值不同38、开发中遇到某个接口返回结果很慢如何排查和解决问题告警监控 链路追踪 SQL优化39、如何设计10万级的并发系统41、如何设计一个分布式框架下的日志系统42、系统上线一年后发现运行变慢了如何排查和解决第一步确认变化与范围变慢是什么时候开始的 和那次大版本发布有关吗还是慢慢变慢的是所有操作都慢还是特定功能慢例如用户查询慢但订单提交不慢是白天高峰期慢还是一直慢如果是高峰期慢可能是容量问题一直慢可能是数据量问题运维系统查看哪些接口不达标 链路最终看慢在哪里43、工作中有没有遇到OOM的问题你是如何排查和解决的44、CAS会不会出现两个线程同事比较都成功更新的场景ABA问题会45、调用某个三方支付(微信、支付宝)回调接口未响应或出错应该怎么解决-- 重试调用-- 调用支付结果查询接口-- 接口回调时需要记录日志处理状态必须做幂等重复回调是常态。必须有对账机制异步处理和重试并不能保证100%一致日终对账是最后一道防线。监控告警监控回调接口的成功率、异常率。如果成功率突然掉到90%以下立刻告警人工介入。46、100w骑手小哥存活状态更新问题分析难点在于100万骑手假设每隔几秒就上报一次GPS和状态对后端存储和计算是巨大的压力。高并发写入每秒处理数万甚至数十万次的状态更新。最终一致性可以容忍几秒钟的数据延迟但最终必须准确。节省存储成本历史轨迹数据巨大需要冷热分离。第一层接入层门面 使用TCP长连接或WebSocket而不是HTTP短连接。因为HTTP的握手开销太大无法支撑百万长连。第二层消息队列削峰填谷 瞬时高峰如整点上报可能高达几十万TPS后端服务可能扛不住数据库更扛不住第三层核心处理层流式计算第四层存储层冷热分离热数据最新状态存放在 Redis响应毫秒级支撑C端用户查看骑手位置。冷数据历史轨迹存放在 HBase 或 时序数据库如InfluxDB、TDengine。这些数据库专门为海量时序数据设计写入性能极高压缩比好。主要用于事后回溯、运力分析、费用计算。47、未支付订单到期之后如何关闭订单方案一被动关闭用户访问时触发-- 用户查询订单时在查询逻辑中判断订单是否超时未支付。如果超时则执行关闭操作更新订单状态和释放库存。方案二定时任务扫表最常见-- 每分钟执行一次扫描订单表中所有超时未支付的订单批量关闭。方案三延迟消息 / 时间轮最优雅使用RocketMQ延迟消息综合方案推荐在实际生产中通常采用方案二定时任务 方案三延迟消息的混合模式主力使用RocketMQ延迟消息实现订单的准时关闭最理想。兜底同时保留一个定时任务频率可以很低比如每小时跑一次扫描那些因为各种原因如MQ消息丢失、消费失败漏掉的订单进行状态补偿。48、假设有40亿QQ号但是内存只有1G如何实现去重-- 分批去重-- 布隆过滤器-- 位图Bitmap-- 大数据组件 如Hbase49、如何实现查询附近的人?-- 方案一Redis GeoHash中小型应用实时性高-- 方案二MySQL 空间索引中小型应用数据持久化-- 方案三GeoHash 算法 数据库扫描通用方案核心思想将二维的经纬度如 116.397, 39.913通过算法转换为一个一维的字符串。这个字符串有一个特点字符串越相似代表地理位置越近。50、如何实现亿级别页面的IP统计功能方案一实时UV使用 Redis HyperLogLog -- 误差标准误差约为 0.81%。方案二位图法精确统计内存可控100%准确但是每个页面都需要独立的512MB位图页面多了内存爆炸-- 开源实现jdk中的Bitset、redis中的bitmapredis的话key为状态offset为uid一个用户占用1bit10亿用户就是125M左右方案三离线/全量分析: 将日志接入 Doris/ClickHouse利用其高效的聚合能力做日报、周报。51、redis内存会被用完吗redis的最大内存是多少默认行为在64位操作系统上maxmemory的默认值是0这意味着不限制内存使用理论上可以用到机器所有的物理内存通用建议通常可以设置为系统可用内存的 70% ~ 75%52、Redis内存满了之后会崩溃吗Redis内存满了之后通常不会直接崩溃情况一正确配置了淘汰策略正常情况不开启的话默认新的请求存入数据会失败53、你项目有什么亮点54、项目开发中遇到了哪些记忆深刻的问题你是怎么解决的55、使用MQ时怎么才能保证消息一定不丢失56、怎么快速判断两个文件是否一致快速判断一致性 使用哈希值比较。逐行对比 适合文本文件能发现具体行的差异。57、如何优化一个高并发的JavaWeb应用先看监控定位瓶颈是CPU、数据库还是IO。如果是慢SQL优先数据库层优化索引、SQL改写。如果是读压力大强化缓存层。如果是写压力大引入异步层。如果单机撑不住做应用层的水平扩容。58、如何优化数据库查询性能第一层SQL与索引优化性价比最高SQL重写第二层数据库结构优化 -- 适当冗余用空间换时间减少JOIN, 分区表,第三层缓存层第四层读写分离使用ES59、分布式下如何设计一个ID生成器UUID、雪花算法、数据库自增60、MySQL机器故障怎么排查看进程ps 检查MySQL是否活着。看磁盘df -h 和 df -i这往往是第一元凶。看日志tail -100 /var/log/mysql/error.log。看连接SHOW PROCESSLIST; 抓出正在执行的SQL。看慢查询分析是否存在需要优化的SQL语句。看锁分析是否存在锁等待或事务未提交的情况。61、like前缀模糊查询不走索引怎么优化-- 索引是按照从左到右的顺序建立搜索树的当通配符 % 出现在最左边时数据库无法判断需要从树的哪个节点开始查找只能进行全表扫描。方案一覆盖索引最推荐方案二反向存储适用于后缀查询方案三Elasticsearch专业搜索引擎 -- 复杂度最高62、如何设计一个高效的缓存机制核心在于平衡三个关键要素命中率节省的时间、一致性数据的准确性和成本资源与复杂度。没有万能的方案只有根据业务场景做出的最佳权衡。本地缓存不会变化的数据查询最快大小受限制数据一致性问题分布式缓存: 容量大所有节点共享数据库最终存储:读操作优先读本地 - 未命中读分布式 - 未命中读数据库。回写读数据库后逐级回写。63、如何保证多级缓存的数据一致性追求数据强一致性就不要使用缓存直接查数据库能容忍短暂的不一致方案一:定时刷新方案二主动失效 广播通知最常用原理当数据更新时不仅删除Redis缓存还通过MQ广播通知所有服务节点删除本地缓存。方案三Canel 监听 Binlog终极方案64、如何实现亿级用户的状态统计分析亿级用户状态统计的核心难点在于数据量巨大且状态往往是实时变化的比如在线/离线、签到/未签、VIP等级等方案一基于 Redis 的实时统计适用于在线状态、实时计数这是最常用的方案利用 Redis 纯内存、高并发的特性来承载读写。方案二: 亿级用户在线状态压缩BitMap以日期为Key如 user:online:20231027。每个用户ID对应 BitMap 中的一个偏移量Offset -- 需要维护一个 用户ID 到 Offset 的映射关系通常直接用数字ID作为偏移量即可方案三基于数据库分表 异步批量适用于持久化统计Flink 消费 MQ 里的用户状态日志。将结果写入 OLAP 数据库如 ClickHouse供运营分析65、怎么在1亿条数据中判断一个手机号是否为新用户分析核心在于解决两个问题存储成本1亿条数据占多大空间和查询速度如何快速判断方案一布隆过滤器最推荐适用于允许极小误判的场景 数据库查询 不存在则一定不存在方案二Redis 位图精确判重适用于手机号可转为数字的场景方案三Redis Set精确判重适用于内存足够的场景内存计算1亿个手机号每个手机号按字符串存储约11字节。Redis的Set有额外开销dict entry、指针等实际占用约 50-70字节/个。1亿 × 50字节 ≈ 5GB。66、如果有100w数据批量写入到数据库会发生什么怎么解决方案一分批 批量插入最常用 多线程处理(可选对性能要求可以加)方案二:专用的数据导入工具67、让你设计一个消息队列你会怎么设计任何MQ最核心的抽象只有两个角色生产者 和 消费者中间是Broker服务端。基本功能生产者建立长连接或短连接发送消息到BrokerBroker返回写入结果成功/失败。消费者有两种基本的消费模式Pull拉模式客户端主动来拉取数据主动权在客户端如 Kafka。Push推模式服务端有数据就主动推给客户端实时性好但需要处理背压问题如 RocketMQ 的 Push 模式底层也是 Pull 的封装68、有100G的数据需要排序但内存只有12G如何处理方案一第一步分割Split——生成有序子文件第二步归并Merge——多路归并方案二如果数据量级达到TB/PB可以考虑大数据框架hadoop、spark69、设计三方接口时需要考虑什么问题1、认证和鉴权2、https数据加密传输3、幂等4、处理结果查询 -- 新增或更新 5、查询的话如果是列表查询需要注意分页6、限流70、如何实现上亿用户的积分实时排行榜功能方案一Redis 有序集合千万级如果用户量在几千万以内Redis 的 Sorted Set 是最简单直接的方案。方案二分区排序 合并亿级核心思想将用户按积分范围或用户ID哈希分到多个桶Bucket中每个桶独立排序查询 TopN 时从各个桶取数据合并。方案四大数据组件 异步计算百亿级当用户量达到十亿级且积分变化频繁时实时计算已不现实需要引入大数据组件进行准实时计算。流式计算Flink/Spark Streaming方案五预计算 结果缓存每小时/每天离线计算全量排名Spark/Hive将 TopN 结果缓存到 Redis71、在某个大文件中如何查询出现次数最多的10个url ?第一阶段预处理与切分Divide目标将大文件拆分成小文件确保每个小文件可以被加载到内存中处理。第二阶段逐个击破与聚合Conquer目标统计每个小文件中每个URL的出现次数然后汇总得到全局Top 10。