面经整理:
3.请阐述从用户"开团"或"参团",到"拼团成功"或"失败"的完整后端流程。
点击拼团 -> 选择商品数量提交订单并支付 -> 付款后进入 拼团中 -> 在规定时间内,支付人数达到参团人数,则拼团成功,否则失败
核心难点在于,如何在高并发下原子化地处理参团操作,并实时、准确地更新拼团状态给所有参团者?
用户在支付的时候,如果有别的团,则加入,否则自动开团。也可以选择已有的团加入。
1、在提交订单的时候都要判断是否有库存,没有库存开团/参团失败。
可以先在redis中扣减库存,再使用MQ异步下单,避免大数量冲击数据库。数据库最后扣减库存时使用乐观锁避免超卖。
2、付款成功才算参团成功
订单提交之后,若规定时间内未付款,需要自动取消订单。比如设置5分钟。取消订单后需要通知用户。
拼团失败需要通知用户(短信+推送),告知用户退款方式。
实现方案:可以使用MQ的延时消失,订单超时未支付则库存数量+1
4.针对"百亿补贴"频道的秒杀活动,如何设计库存扣减方案?
要求绝对防止超卖,同时承受瞬时极高并发。你会选择"下单扣库存"还是"支付扣库存"?为什么?
在Redis和数据库之间如何保证最终数据一致?
1、缓存预热:活动开始前,将数据库中的商品和库存数量写入Redis,注意redis扣减库存需要是原子操作,可使用Lua
2、消息队列:Redis扣减成功后,将"订单创建"或"库存扣减记录"的事件放入消息队列(如 RocketMQ、Kafka)
3、分布式锁(Redis Setnx):
场景:用于真正扣减库存的那一刻,防止多个线程同时操作导致超卖。
粒度:一定要使用细粒度锁。例如,锁的Key应该是 product_stock_lock:{商品ID},锁住当前商品即可,不要锁整个表或使用公共锁。
4、数据库:使用乐观锁兜底,避免超卖
-- 必须是下单扣库存:下单扣库存,100万人同时点购买,只有库存数量(比如1万)的人能拿到下单资格,剩下99万直接被挡在外面,系统压力瞬间释放。
-- 如果是支付扣库存:意味着系统需要承受100万笔订单的创建压力、数据库写入压力、后续99万笔支付失败的回滚处理压力
数据一致性: 消息队列 + 定时对账
--
5."砍价免费拿"活动的后端逻辑如何设计?
重点不只是计算砍价金额,更在于如何有效识别和防御机器刷单、模拟砍价等黑产行为?
从架构层面可以有哪些设计?
业务分析:
用户A发起(帮砍):用户A想免费拿商品,发起砍价 -> 生成一个唯一的砍价记录(关联用户A和商品)。
用户A分享(裂变):系统生成一张带有唯一标识(砍价ID/链接)的海报,用户A分享给好友B。
好友B助力(帮砍):好友B点击链接 -> 系统校验B的资格 -> 执行砍价逻辑 -> 反馈砍掉了X元。
进度反馈:用户A实时看到价格从100元降到了80元。
结算:当价格降到0元时,用户A获得免费领取资格。
前端:只能点击一次砍价
反作弊设计:
同一好友只能帮同一人砍一次
同一IP每天帮砍次数限制
安全防护:分析刷单ip,自动加入黑名单
6.用户下单可能同时使用平台补贴、优惠券、拼团折扣。
如何保证"扣减库存"、"核销优惠券"、"创建订单"这三个操作要么全部成功,要么全部失败?
如果使用TCC(Try-Confirm-Cancel)模型,Cancel阶段失败如何兜底?
分析:分别涉及三个不同的系统(甚至可能是三个独立的微服务)
扣减库存 -> 库存服务(操作Redis + DB)
核销优惠券 -> 营销服务/优惠券服务(操作DB)
创建订单 -> 订单服务(操作DB)
在微服务架构下,无法用一个本地数据库事务(@Transactional)来同时保证这三个服务的数据一致性。
为了保证"全成功或全失败",需要引入分布式事务解决方案。
TCC:
操作 --------------> try(预留资源) --------------> Confirm(确认) --------------> Cancel(取消)
库存服务 冻结库存 真正扣减库存 释放库存
优惠券服务 标记优惠券为"使用中" 核销优惠券(状态改为已使用) 释放优惠券(状态改为未使用)
订单服务 创建"预订单"(状态=待确认) 更新订单为"有效/待支付" 取消订单(状态=失效)
7.某个商品因内容平台推荐突然爆火,流量短时间内飙升数百倍。
如何从监控告警、自动弹性扩容、服务保护(熔断/限流) 等方面快速响应,避免系统被击垮
1、限流机制:在网关层ALB层做好限流,避免大流量冲击系统
2、缓存机制:热点Key自动探测,如果某个商品的访问频次超过阈值(如每秒1万次),自动将其推入reids缓存或本地缓存中
3、系统监控:流量超过指定预置 触发告警机制; 比较好的方案是:环比增长率告警。例如:最近5分钟的平均QPS较前5分钟增长超过300%,立即触发P0级告警
4、弹性伸缩:后台节点数据,根据CPU使用率自动扩容,增加节点数
5、熔断降级:关闭非核心功能:比如商品详情页的"你可能还喜欢"、"历史评论"等非核心功能,全部降级,集中计算资源给核心的"加购"和"下单"接口
8.手撕:实现一个简单的键值存储,要求支持设置过期时间(TTL),并能够高效地自动删除过期的键。
思路:使用两个ConcurrentHashMap,一个存储<key, value>,另一个存储<key, 过期时间>,使用一个定时线程去检测过期时间,过期则删除数据
一点小经验
面试官非常关注你是否能快速理解并解决拼多多业务中真实、高频发生的工程问题。
重点不在于你是否做过亿级流量项目,而在于你能否从高并发、强一致、低成本、快迭代这些拼多多的典型技术氛围中,抓住问题的本质。
错误回答:"用Redis原子操作防止超卖,用消息队列异步更新数据库。"
正确回答:"我将高并发拼团抽象为一个 '资源预定'问题。参团时,首先在Redis中使用 SETNX 或 Lua 脚本原子化检查并占用一个'虚拟席位',确保不会超员。
同时,将参团事件写入可靠消息队列。后台服务消费队列,异步持久化到数据库并推动状态机流转。
这个设计将实时一致性需求限制在Redis层面(高性能),而将最终正确性交给数据库和异步任务(高可靠),
通过状态对账弥补中间态可能的不一致,在体验和系统复杂度之间取得了平衡。"
redis的主从一致性问题你了解吗?
主节点 (Master):可读可写
从节点 (Replica):通常是只读的。它通过接收并执行主节点同步过来的写命令,来保持与主节点数据一致
在默认情况下是异步的。也就是说,主节点执行完一个写命令后,会立即返回结果给客户端,并不会等待从节点确认收到该命令。这个异步的过程,就是主从数据可能出现不一致的根源。
追求更强一致性:
方案一:强制读主库: 对于一致性要求极高的业务,强制读取主库,不通过从库读取。
方案二:WAIT 命令,等待从库确认收到并同步了该命令,性能问题
方案三:使用redis集群
分布式锁的具体实现方式?
主要通过 Redis(高性能)、ZooKeeper(高可靠)和数据库(简单但性能低)实现
threadlocal的原理、内存泄漏?
原理:
数据结构:threadlocal底层是ThreadLocalMap,每个 Thread 线程对象内部都维护了一个属于自己的 ThreadLocalMap
set(v)的时候,每个线程都把值放在自己的ThreadLocalMap中,key就是当前的ThreadLocal对象
内存泄漏原因:ThreadLocalMap 的 Entry 的 Key 是弱引用,Value 是强引用。
当 ThreadLocal 外部强引用消失,Key 被 GC 回收,
但 Value 因被 Entry 强引用而无法释放,且线程长期存活,导致 Value 一直无法回收。
根本原因:线程生命周期过长(如线程池) + 忘记调用 remove()。
解决方案:用完务必在 finally 块中调用 remove() 方法。
websocket的应用场景?
本质上是需要将"等待客户端的轮询"转变为"服务端的主动推送"
高频数据交互(如最新行情、游戏)
拦截器使用的具体的类?
请求顺序由上到下依次为:
1、拦截 HTTP 请求(Controller):用 Spring MVC 的 HandlerInterceptor。
2、修改请求/响应流、处理字符编码:用 Java 原生的 Filter。
3、拦截任意 Service 层方法:用 Spring AOP(如 @Around 注解)
4、拦截 SQL 执行:用 MyBatis 的 Interceptor。
数据库和redis的使用场景各自有哪些优劣势;为什么redis的读取的性能更快?
redis是多线程还是单线程的呢?
虽然核心命令执行(命令执行)是单线程,处理客户端请求(如 GET、SET、LPUSH 等命令)的部分,始终是由主线程串行执行的。
在 4.0 版本,Redis 开始使用多线程来处理一些慢速的、非核心的后台任务,目的是不让这些操作阻塞主线程
为什么设计成单线程?
不需要多线程:Redis 是基于内存的操作,CPU 不是瓶颈,瓶颈主要是网络 I/O。即使使用单线程,也能达到每秒十万甚至百万的 QPS。
避免了锁竞争:多线程编程需要处理共享资源的并发访问,引入锁会带来性能开销和复杂性。单线程模型天然避免了这一点,这也是 Redis 实现原子操作的基础。
实现简单:代码清晰,维护成本低。因为所有命令在服务端是串行执行的,所以不需要考虑并发冲突等线程安全问题。
CPU最快(纳秒级),内存次之(百纳秒级),网络IO最慢(毫秒级)
1毫秒 = 1 000 000 纳秒
使用单线程的缺陷:
1、CPU利用瓶颈 : 无法充分利用多核CPU的计算能力,一个Redis实例只能使用一个CPU核心, -- 集群模式:通过将数据分片到多个Redis实例上,从而利用多个CPU核心
2、请求阻塞风险:如果某个命令执行时间过长,会阻塞后续所有请求; -- 异步化:Redis 4.0引入UNLINK、FLUSHALL ASYNC等命令,将大Key删除等耗时操作交给后台线程处理,避免阻塞主线程
3、IO读写瓶颈:在并发量极高(如数百万QPS)的情况下,单线程处理网络I/O的能力会成为性能天花板,无法利用多核优势进一步提升吞吐量
-- Redis 6.0引入多线程来处理网络数据的读写和协议解析,而命令执行依然由单线程负责,以此提升I/O吞吐量,同时保持核心操作的原子性
java里多线程场景怎么使用,需要规避什么问题?什么时候用多线程?
java里多线程场景怎么使用,需要规避什么问题?
1、资源消耗:创建线程需要消耗内存和 CPU 资源,线程过多会导致上下文切换频繁,降低性能。一般建议使用线程池
2、死锁:不当的锁使用会导致线程永久等待。 -- 可以使用锁超时:指定时间内获取不到锁则放弃,锁的时间要有超时限定
3、线程安全: 多个线程访问共享资源(如共享变量、文件)可能导致数据不一致,需要使用锁机制。
什么时候用多线程?
1、提高资源利用率: 减少 CPU 空闲时间,特别是在多核 CPU 环境下。 -- 提高cpu资源利用率:比如解析excel后,10w条数据分多个线程去处理
2、提升用户体验: 保持界面响应,避免耗时操作导致程序"假死"。 -- 提高接口响应,比如耗时的操作异步处理
3、提高程序吞吐量: 同时处理更多请求。
hashmap线程安全吗?有哪些是线程安全的?
HashMap 为什么线程不安全?
数据覆盖:多线程下,同时执行 put 操作,一个线程的写入可能会被另一个线程覆盖。
哪些 Map 是线程安全的?
ConcurrentHashMap、Hashtable、Collections.synchronizedMap(返回一个包装好的同步 Map,内部也使用 synchronized 锁)
数据库事务的实现原理? Mysql的引擎?Innodb的原理?索引?
数据库事务的实现原理基于ACID特性,通过日志和锁机制保障数据一致性
原子性 底层就是通过 undo log 实现的
持久性(Durability)- redo log (重做日志) -- 当事务提交时,先将修改记录在redo log文件中,再定期刷新到磁盘
隔离性(Isolation)- 锁机制 + MVCC (多版本并发控制)
锁机制:通过写锁(排他锁)和读锁(共享锁)来防止并发冲突,如读写冲突、写写冲突。
MVCC:通过保存数据的历史版本(基于undo log版本链和ReadView),实现一致性非锁定读,在读取数据时不需要加锁,提高了并发能力。
隔离级别的支持:
1、RC级别:MVCC + 行锁 RR级别:MVCC + (行锁+间隙锁)
MVCC只在读已提交和可重复读这两个隔离级别下工作
一致性:一致性是最终目的。通过原子性保证事务不部分失败,持久性保证提交后数据可查,隔离性保证不被其他事务干扰,从而使数据库从一个一致状态转换到另一个一致状态
幻读是否被 MySQL 可重复度隔离级别彻底解决了? (有些场景没有)
MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读现象(并不是完全解决了),解决的方案有两种:
针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读
针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读
未解决的场景:
事务 A 更新 id = 5 这条记录,对没错,事务 A 看不到 id = 5 这条记录,但是他去更新了这条记录,这场景确实很违和
https://blog.csdn.net/m0_71777195/article/details/126968432
12.数据库缓存架构的数据一致性问题怎么解决
解决方案:常见的解决方案包括双写策略、延迟双删、分布式锁等。在数据更新时,需同时更新数据库和缓存,以确保一致性。
13.快排,堆排,归并效率对比,为什么快排最优; B+树怎么排序的,大文件排序怎么做
大文件排序怎么做:
方法一:外部归并排序
步骤:
分块读取与排序 将大文件按内存可承受的大小分块读取,每块在内存中使用快速排序或归并排序完成排序后写入临时文件。
多路归并合并结果 使用多路归并算法同时读取多个已排序的临时文件,逐步合并为一个有序的大文件。
输出最终结果 将合并后的数据写入目标文件。
14.手撕力扣31.下一个排列(中等)
3.自己挑一个项目中的难点,进行详细说明,追问细节实现
4.对拼多多的文化理念看法
5.mysql的事务(自己讲有哪些基础知识,回答了ACID、事务执行流程、MVCC、事务隔离级别、三种log)
3.项目难点,一直追问
4.HashMap底层结构;链表转红黑树的阈值; 为什么要红黑树,为什么不是AVL树;红黑树的弱平衡为什么是2倍关系;为什么mysql用B+树,不用红黑树
核心原因是红黑树在插入和删除操作上性能更优。AVL树是严格平衡二叉树,追求查找效率最大化,但频繁旋转导致插入删除性能低;
而红黑树维持"黑色平衡",是一种近似平衡,通过更少的旋转开销在插入、删除和查询之间达到了更好的综合性能平衡。
5.mysql什么字段适合加索引慢查询怎么排查,为什么redis这么快
以下是具体适合加索引的字段类型:
高区分度的列:例如用户ID、手机号、账号等唯一或几乎不重复的字段。
频繁查询的 WHERE 条件列:在 SELECT 语句的 WHERE 子句中经常使用的字段。
JOIN 连接条件列:经常用于连接两个表的字段(外键)。
排序、分组、统计字段:出现在 ORDER BY、GROUP BY 或 DISTINCT 后面的字段。
组合索引字段:遵循最左前缀原则,将区分度最高的字段排在最左边的联合索引
6.对序列化的理解
序列化(Serialization)是将对象的状态信息(内存中的数据结构)转换为可存储或传输的格式(通常是二进制字节流或JSON/XML文本)的过程。
其核心目的是持久化存储(存入文件/数据库)和网络传输。其逆过程为反序列化,即从序列化后的数据重建原对象
7.对反射的理解,为什么需要反射,反射的应用场景,是否有安全问题
允许程序在运行期间获取类型信息、调用方法、访问属性,并使用这些信息。
为什么需要反射 (核心作用)
增加灵活性和扩展性:使程序在运行时加载和使用未知的类,无需重新编译
处理动态类型:在编译时无法确定具体类型,需要根据配置或外部输入来动态实例化对象。
反射的应用场景
开发通用框架:如 Spring 容器利用反射实例化 Bean,实现依赖注入(DI)。
动态代理:AOP(面向切面编程)的实现基础,运行时动态生成代理类。(代理 + 反射)
ORM(对象关系映射):如 Hibernate/MyBatis 利用反射将数据库结果集自动映射为 Java 对象。
序列化与反序列化:JSON 框架(如 Jackson/Fastjson)利用反射读取和设置对象字段。
8.Java异常体系执行sql要在编译期抛出吗
1、自己写JDBC时的SQLException异常必须处理
2、Mybatis的每个SQL执行时,不需要处理
9.对spring aop的理解,为什么需要aop
Spring AOP(面向切面编程)是Spring的核心模块,它在运行时通过代理机制,将日志记录、事务管理等通用功能(切面)与核心业务逻辑解耦并无侵入地"织入"。这使得开发者能
10.JVM内存模型划分,每个区域存储什么数据;为什么划分为堆和栈,不放一起,性能上会不会更好;new的对象一定在堆上吗;栈为什么小
new的对象一定在堆上吗?不一定。在现代JVM(如HotSpot)中,基于逃逸分析,若对象未逃逸出方法范围,可能在栈上分配
栈追求速度,所以空间紧凑;堆追求容量,用于管理复杂的长生命周期对象。
11.输入url做了哪些网络活动;tcp的第三次握手能丕携带数据为什么设计成可以携带
DNS 解析 (Domain Name System): 浏览器检查域名对应的IP地址(先后查找本地缓存、本地hosts文件、DNS服务器),将网址映射为具体的服务器IP地址。
TCP 连接 (TCP/IP): 浏览器与服务器之间进行三次握手,建立可靠的连接通道。如果是HTTPS,还会进行SSL/TLS握手以加密传输数据。
发送 HTTP 请求: 浏览器向服务器发送HTTP请求报文(如GET请求特定资源),若服务器有重定向配置(如301/302),浏览器需重新发送请求。
服务器处理并返回响应: 服务器收到请求后,解析URL、查询数据库、处理资源,生成HTTP响应报文(包含状态码、文件内容等)发回给浏览器。
页面解析与渲染: 浏览器接收到HTML代码后,开始进行渲染:解析HTML生成DOM树,解析CSS生成CSSOM树,将二者合并为渲染树,最后布局和绘制网页。
资源请求与加载: 解析过程中若遇到图片、JS、CSS等外部资源,浏览器会再次发送请求获取这些资源并加载。
TCP 连接断开: 资源传输完成后,浏览器与服务器通过四次挥手断开连接
TCP的第三次握手可以携带数据。虽然前两次握手(SYN和SYN+ACK)按照协议标准通常不能携带数据,但第三次握手(ACK)在客户端进入ESTABLISHED状态后,允许包含应用层数据一并发送,从而稍微提高连接效率。
具体细节:
第一次握手 (SYN):不能携带数据。如果允许,恶意攻击者会在SYN包中加入大量数据,导致服务器消耗资源。
第二次握手 (SYN+ACK):不能携带数据。
第三次握手 (ACK):可以携带数据。客户端发送ACK时,状态已变为已建立(ESTABLISHED),此时可以把数据"捎带"在ACK包中发给服务器。
实际情况:许多基于TCP的协议(如HTTP/1.1)通常在三次握手之后才发送数据。但理论上和操作系统协议栈层面上,第三次握手携带数据是合法
2.拷打项目,项目介绍,为什么做这个项目,项目模块,测试环境,数据库表字段,项目难点和亮点
3.HashMap底层结构
4.mysql覆盖索引
5.redis缓存穿透/击穿/雪崩
6.redis的key过期是怎么实现的
核心数据结构:过期字典 -- Redis在保存Key-Value的内存结构之外,额外维护了一个字典,称为过期字典,Value:是一个long类型的整数,保存了该Key的精确过期时间
过期删除策略:Redis使用两种策略结合来实现Key的按时删除;
惰性删除:当客户端尝试访问某个Key时,Redis会先检查它是否在过期字典中并已过期。如果过期,则立即删除并返回空
定期删除:Redis默认每秒进行10次(100ms一次)过期扫描,从过期字典中随机取20个Key,删除其中已过期的Key
数据持久化中的处理:
RDB:在生成RDB文件时,主节点会校验Key是否过期,过期的Key不保存。
AOF:当Key过期被删除后,AOF文件会追加一条DEL命令。
内存淘汰机制: 内存耗尽,Redis会触发内存淘汰策略
-Redis的持久化方式
RDB 持久化: 将某个时间点的内存数据,以二进制形式压缩写入磁盘,默认文件为 dump.rdb。
AOF 持久化: 数据更安全,数据丢失率极低(通常为1秒内)
混合持久化:RDB 和 AOF 的结合。在AOF重写时,将当前内存数据以RDB格式写入AOF文件头部,再接着追加后续写命令。
7.一段redis加锁代码,有哪些问题加锁失败不能解锁/需要加过期时间防宕机/长业务用看门狗
8.手撕力扣480.滑动窗口中位数(困难)
手撕: 线程安全的单例模式
三数之和(lc hot100里有)
-AQS的实现原理
AQS(AbstractQueuedSynchronizer,抽象队列同步器)是Java并发包(java.util.concurrent)的基石
AQS的核心是管理一个共享资源(state)和一个等待线程队列。
资源状态(state):使用一个volatile int类型的变量表示同步状态。
同步队列(CLH队列锁的变体):一个FIFO的双向队列。当线程获取资源失败时,AQS会将当前线程以及等待状态等信息构造成一个节点(Node),并加入队列的尾部,同时阻塞该线程。
当持有资源的线程释放资源后,AQS会从队列头部唤醒下一个等待的线程。
-synchronized锁升级过程 -synchronized是通过什么操作获取到轻量级锁?(CAS)
偏向锁:同一个线程反复进入同步块。当一个线程第一次进入同步块时,锁会记录这个线程的ID,
轻量级锁:有少量线程竞争,但线程交替执行,没有长时间阻塞。-- 线程会自旋,尝试获取锁,
重量级锁:多线程同时竞争,且竞争激烈。-- 如果自旋超过一定次数,轻量级锁就会升级为重量级锁
synchronized 获取轻量级锁的核心是 CAS 操作。它本质上是一种乐观锁机制,假设没有竞争,通过 CAS 快速设置对象头来获取锁,避免了线程阻塞和唤醒带来的操作系统内核态切换开销。
只有当 CAS 真正失败时,才会退化为使用互斥量的重量级锁。
-CAS算法可能会遇到ABA问题,怎么解决?
解决ABA问题的核心思想是:引入一个额外的、能够标记或版本化的机制,让CAS不仅能检查值,还能检查这个值是否被改动过。
使用版本号/时间戳(推荐方案): AtomicStampedReference 和 AtomicMarkableReference
Java集合
-HashMap的扩容是怎么实现的
什么时候触发扩容?
capacity:当前数组(桶数组)的长度,默认初始为16。
loadFactor:负载因子,默认0.75。
threshold:扩容阈值,计算公式为 capacity * loadFactor。当HashMap的元素数量(size)超过这个阈值时,就会触发扩容。
- HashSet怎么保证数据不重复
HashSet保证数据不重复,核心在于它底层依赖了HashMap,利用了HashMap中Key必须唯一的特性。
HashSet的去重能力,本质上是依赖于我们放入的对象的hashCode()和equals()方法。如果这两个方法没有正确重写,HashSet就无法正确判断对象是否重复。
7 Spring
-Spring容器的启动流程-怎么理解loC? -Bean的循环依赖-怎么理解AOP 8数据库&Redis -事务并发隔离等级-缓存三剑客
简言之,即加载配置、构建工厂、生成Bean :
Spring容器的启动流程,可以概括为:
加载配置:读取和解析配置信息。
扫描定义:找到所有Bean的"设计图纸"BeanDefinition。
加工定义:BeanPostProcessor等增强
实例化Bean:按照图纸生产Bean实例,并处理好它们之间的依赖关系。
Spring的BeanPostProcessor(Bean后置处理器)是框架提供的关键扩展点,允许在Bean实例化、属性设置之后、初始化前后(及销毁前后)自定义处理逻辑
容器就绪:发布事件,完成启动。
-Java类的加载过程
系统加载 Class 类型的文件主要三步:加载->连接->初始化
加载这一步主要是通过我们后面要讲到的 类加载器 完成的
连接:确保被加载的类的字节流符合JVM规范、分配内存并设置初始值、将常量池内的符号引用替换为直接引用
初始化阶段是执行初始化方法 <clinit> ()方法的过程,是类加载的最后一步
-JVM是怎么判断一个对象是垃圾对象的
如果从GC Roots出发,通过引用链无法到达某个对象,那么这个对象就被判定为可回收的垃圾。
-一般什么样的对象会被标记成GC Root?
虚拟机栈(栈帧中的本地变量表)引用的对象:也就是当前正在执行的方法里,用到的那些参数、局部变量等。只要方法还没执行完,这些对象就"正在被使用"。
方法区中静态属性引用的对象:类的静态变量(static修饰的)引用的对象。只要类没被卸载,这个对象就可能被访问。
方法区中常量引用的对象:比如字符串常量池(String Table)中的引用。
本地方法栈中JNI(Native方法)引用的对象:Java调用底层C/C++代码时,传给那些代码的对象。
Java虚拟机内部的引用:基本数据类型对应的Class对象、常驻的异常对象(如NullPointerException)、系统类加载器等。
所有被同步锁(synchronized)持有的对象。
-垃圾回收算法
标记-清除算法
优点:实现简单,是后续很多算法的基础。
缺点:内存碎片化
标记-复制算法:
它将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块用完了,就将还存活的对象复制到另一块上,然后把已使用的空间一次性清理掉
优点:实现简单,运行高效。只需移动堆顶指针,按顺序分配即可,且没有内存碎片
缺点:将可用内存缩小为原来的一半,空间利用率降低,如果对象存活率较高,需要执行大量的复制操作,效率会变差。
标记-整理算法:标记过程与"标记-清除"一样,但后续不是直接清除,而是让所有存活的对象向内存空间的一端移动(整理),然后直接清理掉边界以外的内存。
优点:解决了"标记-清除"的内存碎片问题,也避免了"标记-复制"的空间浪费。
缺点:移动存活对象并更新所有引用,需要暂停所有用户线程(Stop The World,即停止所有应用线程),暂停时间会随存活对象数量增加而变长。
CMS回收器工作步骤(阶段) G1工作步骤
CMS(Concurrent Mark Sweep)回收器是 Java 8 中一款非常重要的垃圾回收器,它的设计目标是获取最短的停顿时间,非常适合那些对延迟敏感、需要与用户交互的应用
CMS 回收器主要作用于老年代
内存碎片问题
原因:CMS 使用的是 标记-清除 算法,而不是整理算法。这意味着回收后,内存中会留下大量不连续的碎片。
其工作流程主要分4个阶段:初始标记(STW)、并发标记、重新标记(STW)、并发清除,旨在缩短STW时间
初始标记 (Initial Mark, STW):
动作:快速标记GC Roots能直接关联到的对象。
特点:需要 Stop The World (STW),但速度很快
并发标记 (Concurrent Mark):
动作:进行GC Roots Tracing,遍历整个堆中可达的对象图。
特点:与用户线程同时工作,无需停顿,耗时较长
重新标记 (Remark, STW):
动作:修正因并发标记期间用户线程运行而导致标记产生变动的那一部分对象。
特点:需要STW,停顿时间略长于初始标记。
并发清除 (Concurrent Sweep):
动作:清除未标记的垃圾对象。
特点:与用户线程同时工作
5 Java并发
-进程状态以及状态之间的转换-Java线程池的作用-线程池处理任务的流程
进程状态主要分为创建、就绪、运行、阻塞、终止五态。
转换核心在于资源分配与调度:就绪进程通过CPU调度获得CPU转为运行态;
运行进程因资源不足(如I/O)转为阻塞态,资源满足后转为就绪态;运行进程时间片用完则转为就绪态
Java线程池的作用
复用已有的空闲线程,避免了频繁销毁与创建线程造成的系统时间与内存开销
线程是稀缺资源,线程池能够限制并发线程的总数,防止因无限创建线程导致系统因过分调度而崩溃。
线程池处理任务的流程
Java线程池处理任务流程如下:
-
核心线程若未满,创建核心线程执行。
-
核心线程满,尝试放入阻塞队列。
-
队列已满,判断是否小于最大线程数,是则创建临时线程处理任务。
-
达最大线程数且队列已满,则执行拒绝策略
-synchronized跟ReentrantLock有什么区别?
1微服务多点部署其中一个宕机了怎么办
要是mg占据大量CPU该怎么排查? MySQL占据大量CPU该怎么排查?
开启并分析慢查询日志:确认慢查询日志已开启 -- 定位最耗资源的 SQL
使用 Performance Schema:MySQL 5.6+ 可以通过 performance_schema 查询当前最耗时的 SQL。
如果 CPU 负载过高导致应用卡死,可找到对应的 Id 并结束连接:KILL [connection_id];
3假如说让你实现视频点赞功能,你打算怎么设计?讲讲思路(我知道多级缓存, 但是碰巧没背......寄)
Redis延迟双删是什么,分布式锁, 哨兵模式
Redis延迟双删是一种用于解决数据库(MySQL)与Redis缓存数据不一致的缓存更新策略。
其核心流程为:更新数据库前先删除缓存、更新数据库、延迟(如数百毫秒)后再次删除缓存。
该策略旨在清除更新期间因高并发读写产生的脏数据,确保最终一致性
Redis哨兵模式:
Redis 哨兵(Sentinel)模式是Redis实现高可用性(HA)的解决方案。
它由一个或多个哨兵进程组成的系统,通过监控、自动故障转移、通知机制,在主节点故障时自动将从节点提升为主节点,无需人工干预,确保Redis服务不间断。
其核心是确保Redis系统的高可用,支持故障自动切换
5 MySQL到es同步的延迟该怎么优化
前端:定时查询,确保查询的数据能定时刷新
后台: 同步:写入数据库中后,当前线程里面插入数据到es中
异步:数据分配多线程处理,数据快速插入到mysql中
6 Rabbit mq的队列是怎么实现的?
1.算法题三道
1)leetcode124二叉树中最大路径
hard题因为不久前才刷过撕出来了,又来了一道
(2) leetcode300最长递增子序列变
除了递增之外还加了一个权重因素,但是思路没变,dp就行
(3)寻找词汇库里符合固定长度前缀的匹配单词
15.最后问了个简单的,如何实现乐观锁
16.最后手撕是最长不重复子串
10.接下来是mybatis的:
·mybatisplus和mybatis的区别·然后先问我有没用过jpa,我说没有
·然后说mybatispluS用到了很多queryweb?(这个也没准备好,不太会,甚至不知道说的是什么) ·最后一个是mybatisplus如何实现分类的
MyBatisPlus (MP) 是在 MyBatis 基础上增强的工具,核心区别在于开发效率与 SQL 编写方式。MP 无需手写通用 CRUD 的 SQL,内置了通用 Mapper、分页插件、条件构造器,自动填充等功能,旨在"只做增强不做改变",主要通过注解和自动化手段大幅减少 XML 配置和繁琐代码。
MyBatis-Plus实现分页主要通过配置分页拦截器,MyBatis-Plus会在后台自动在SQL后拼接 LIMIT 语句
11.接下来是sql语句优化: ·多表连接怎么优化
·多变连接的情况下,如果要分页查询该怎么改造? ·多表连接如何创建索引?联合索引是作用在哪里?
12.然后就是join from group by order的执行顺序
13.MySQL内部如何提高扫描效率?(然后补充说有什么优化器)
MySQL通过优化器选择最优执行计划、合理使用索引、利用缓冲池减少磁盘I/O、表分区以及重写查询语句来提高扫描效率
14.然后又回到了redis:
点赞排行如何实现?用zset的话是不是只能存储短期的数据?那如果一周之后没了该怎么办?要如何刷新?
6.starter的设计模式有哪些?
以下是 Spring Boot Starter 中常用的设计模式:
工厂模式 (Factory Pattern):自动配置类(Auto-configuration)实质上是工厂,通过 @Bean 注解根据条件生产并管理Bean实例。
策略模式 (Strategy Pattern):结合 @Conditional 系列注解(如 @ConditionalOnClass, @ConditionalOnProperty),根据项目依赖或配置存在与否,灵活选择启用的配置策略。
模板方法模式 (Template Method Pattern):Starter 通常遵循标准的 spring.factories 加载流程和配置顺序,底层框架(Spring Boot)定义启动骨架,具体的配置细节由各Starter自行实现。
代理模式 (Proxy Pattern):在 AOP 场景下,Starter 自动配置代理类来实现功能的增强(如事务管理、日志记录)
7.然后就是认证和鉴权的区别是什么?
认证 (Authentication - 你是谁)
鉴权 (Authorization - 你能做什么)
8.接下来就是springboot模块:
第一个是为什么开发者要自创线程池而不是直接用springboot的线程池??
个人理解可以更好的区分线程名,方便排查问题;自定义线程池各种参数
以及自建线程池有哪些指标需要去监控的?
任务拒绝次数
任务执行时间超过某个阈值(如 5 秒)的次数或任务执行失败
9.redis模块:
第一个是集群相对于单机来说,有什么不同点集群是如何进行同步数据的
Redis 集群通过主从复制(Master-Slave Replication)机制同步数据,主要分为全量复制(首次同步)和增量复制(日常同步)。主节点(Master)负责读写,将数据异步同步给从节点(Slave)。同步基于 RDB 快照和命令传播,利用复制偏移量(Offset)和复制积压缓冲区确保数据一致性
集群模式是否容易丢失数据?及如何解决丢失数据的情况?
集群模式为何会丢失数据?
数据丢失通常发生在主从(Master-Slave)集群结构中,主要原因包括:
异步复制延迟:主节点在将数据同步给从节点前挂掉,主节点上的新数据未持久化或未能同步到从节点,导致数据丢失。
网络分区(脑裂):网络故障导致集群分裂,此时主从节点都可能写入数据,在恢复后由于数据冲突而被迫舍弃部分数据。
主从切换失败:主节点故障后,如果从节点数据较老,强制将从节点升级为主节点,会导致主节点旧数据丢失
如何解决集群模式下的数据丢失问题?
配置同步复制(Synchronous Replication)
原理:要求主节点写入数据后,必须等待至少一个从节点确认收到数据,才向应用端返回成功。
优点:极大提高数据安全性。
缺点:增加了写操作延迟,降低了系统写入性能。
设置持久化策略
在集群各节点上合理配置持久化(如RDB+AOF),缩短数据落盘的时间间隔。确保即使故障,数据也已存入磁盘,而不仅在内存中。
rdb和aof在使用的时候应该怎么选
还有一个问题是,缓存穿透如果用空值法的话,如何
避免大面积的内存被白白占用?
redisson相比于setnx灵活在哪?
采用延迟双删的情况下,如果MySQL数据库操作失败
了怎么办?
redis刷新token是如何保证用户无感的?
1.上来第一个问题是过滤器和拦截器的区别是啥?执行顺序是啥?以及实现原理的区别?
过滤器(Filter)和拦截器(Interceptor)的主要区别在于规范、作用范围和实现原理:Filter基于函数回调,依赖Servlet容器(如Tomcat),对所有请求生效;Interceptor基于Java反射(动态代理),依赖Spring框架,仅针对Controller请求生效。执行顺序为:Filter -> Interceptor -> Controller
过滤器 (Filter) 实现原理:
基于函数回调。实现 javax.servlet.Filter 接口,Web 容器会在请求进入 Servlet 前后调用 doFilter 方法。它基于 FilterChain 实现,可以在请求处理前后对 Request 和 Response 进行处理。
拦截器 (Interceptor) 实现原理:
基于 Java 的反射机制和代理模式。实现 org.springframework.web.servlet.HandlerInterceptor 接口。Spring MVC 框架通过 AOP 思想,在请求分发到 Controller 前后调用 preHandle、postHandle 和 afterCompletion 方法。
2.然后又大概问了springsecurity是基于过滤器还是拦截器(哎,血的教训。本身就没怎么了解过springsecurity 结果硬着头皮写上去,一问直接穿帮)
3.然后还问了springsecurity有什么过滤器4.springboot如何自定义一个starter?比方说像MySQLstarter之类的
Spring Security通过一个名为 FilterChainProxy 的Bean维护一个过滤器链,核心过滤器包括用于身份认证的 UsernamePasswordAuthenticationFilter、SecurityContext 持久化的 SecurityContextPersistenceFilter、CSRF防护的 CsrfFilter、异常处理的 ExceptionTranslationFilter 以及最终权限校验的 FilterSecurityInterceptor
5.Metainfo的配置以及用来做什么的
1、有接触过哪些消息队列中间件?如何来选型? 2、谈谈你对SOA以及微服务的理解? 3、具体谈谈Spring Cloud服务和注册? 4、分布式的cap了解么,分别指什么? 5、网络编程nio和netty相关,netty的线程模型, 零拷贝实现?
6、spring和springboot的关系你是怎么理解的? 7、Spring IOC和AOP说一下你的理解8、为什么用消息队列?
9、对于消息的重复消费你有什么设计方案吗?
1、HashMap和HashIable以CocurrentHashMap 详细说明
2、网络IO模型?什么是多路复用IO?select和epoll 的差别?
网络IO模型
阻塞IO (Blocking IO):用户线程在读写数据时会被阻塞,直到数据准备好。
非阻塞IO (Non-blocking IO):用户线程不断主动查询内核数据是否准备好,未准备好则立即返回。
IO多路复用 (IO Multiplexing):单线程监控多个连接,内核监测FD状态,高效。
信号驱动IO (Signal Driven IO):数据准备好时内核发送信号通知进程。
异步IO (Asynchronous IO):内核完成所有操作后通知用户进程,性能最高。
多路复用IO是一种通过一个线程管理多个网络连接(Socket)的机制
当多个连接中有任意一个或多个连接出现读写事件时,系统会通知进程处理,避免了为每个连接创建线程的巨大开销。
select、poll 和 epoll 都是多路复用技术,但实现方式和性能差距明显
select: 全量轮询:需遍历所有FD找到就绪者
epoll : 回调机制:仅需处理就绪链表
3、TCP三次握手的过程,如果没有第三次握手有什么问题?
4、常用的线程池有哪些?各自的应用场景? 5、Java类加载机制?双亲委派模型的好处? 6、JAVA并发包组件了解多少?
7、什么时候多线程会发生死锁?怎么来预防8、操作系统的用户态和核心态切换条件以及为什么要切换
9、数据库事务特点?事务隔离级别?项目中的事务实现?脏读,不可重复读、幻读各举个例子?