news 2026/5/9 4:46:35

AI面传统Java问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI面传统Java问题

Redis

SDS相对C字符串优势

Dict触发hash扩容的两个条件

String三种底层编码是什么,什么区别

触发List底层结构由ziplist转向双向链表的两个阈值条件

Redis 3.2 版本前后,List 类型的底层编码分别是什么?

ZSet 的 dict 和 zskiplist 如何配合?

ZSet 的 dict 和 zskiplist 如何避免成员内存重复占用?

Set的三种底层编码是什么,什么区别

redis采用的过期删除策略


什么是BIGKEY问题,怎么解决


Redis删除中的熔断机制是什么

Redis如何通过写时复制实现异步持久化机制

哨兵模式下,主节点挂掉,哨兵如何选取新的主节点?选取的策略是什么?

什么是脑裂?在集群模式下和哨兵模式在如何预防?




Mysql


什么是mysql事务的一致性,原子性,隔离性,持久性。分别是使用什么实现的


可重复读如何解决幻读?有没有彻底解决


四种事务隔离级别从低到高排序

脏读、不可重复读、幻读分别被哪些隔离级别解决?

MVCC 实现依赖哪三大核心组件?

ReadView 是什么?包含哪四个核心字段?

RR 与 RC 级别在 ReadView 生成时机上的核心区别?

快照读和当前读的区别是什么?

InnoDB 无索引的查询会加什么锁?

UPDATE 索引字段的底层加锁逻辑是什么?为何会被间隙锁阻塞?

简述 InnoDB 插入记录成功与失败时的加锁流程。


什么是隐式锁?隐式锁何时转为显式锁?


意向锁(IS/IX)的作用是什么?加行锁 / 间隙锁前为何要先加表意向锁?

Buffer Pool 里的数据页和索引页的区别是什么?


简述 MySQL 执行一条增删改并 commit 的完整流程


简单 LRU 算法用于 Buffer Pool 管理会出现哪两个严重问题?请分别说明。

InnoDB优化LRU分哪两个区域,什么作用


InnoDB 优化 LRU 时,为何要为 old 区域添加访问停留时间限制?


bufferpool管理用到的三个链表


索引失效场景有哪些

出现了慢sql,如何排查优化




JVM

JVM内存结构

Java四种引用区别

Java内存泄露的常见情况


JVM创建对象的过程


JVM类加载器有哪几种


JVM类加载到卸载过程


双亲委派模型是什么,有什么作用

可达性分析算法中的GC Roots包含哪些对象?

对象真正被回收前会经历哪两次标记?finalize()方法的作用是什么


CMS 收集器的完整执行流程分为哪四个阶段?哪些阶段会 STW?CMS 的核心缺陷有哪些?


G1 的执行流程包含哪几个核心阶段?Mixed GC的核心逻辑与优势是什么?


CMS用于堆的哪个区域?

G1 为什么采用标记 - 复制算法,而不是标记 - 清除?


Java基础

为什么需要包装类


自动装箱拆箱原理?Integer的缓存机制?


equals和hashcode为什么要配套重写


StringBuffer和StringBuilder的区别

Stream流有什么用

Optional有什么用

Volatile什么作用

什么是代理模式、适配器模式、责任链模式、策略模式


Java集合


CocurrentHashMap如何实现并发安全


hashmap的put过程介绍一下,包括出现冲突,以及扩容和变化桶结构


Hashtable和hashmap区别

Hashmap每次扩容多少,为什么?

HashMap为什么要变化桶结构


java并发编程


讲一下对CAS的理解,以及存在什么问题


ABA问题怎么解决

threadlocal原理?为什么threadlocalmaps的key为弱引用?讲一下threadlocalmaps的内存泄漏原理?怎么解决


cocurrenthashmap如何保证线程安全


java创建线程的方法

java如何停止一个线程的执行


java线程的六种状态


sleep()和wait()方法的区别,包括设计用途,如何唤醒,使用前提,以及是否释放锁

Blocked状态和Waiting状态什么区别


ReentrantLock的高级功能


什么是读写锁

synchronized工作原理


讲一下synchronized的锁升级


CountDownLatch应用场景


死锁四个条件





Spring


@Resource和@Autowired什么区别


什么是构造器注入,字段注入,setter注入?为什么Spring更推荐构造器注入


什么是循环依赖?怎么解决?


为什么使用三级缓存而不是二级缓存

事务什么时候失效


Bean的生命周期


SpringMVC的流程


SpringBoot自动配置类原理

原生的mybatis查询,怎么写




计算机网络


一句话总结linux网络协议栈


HTTP和TCP的keepalive什么区别


http,3,4,5开头的状态码什么含义

为什么说HTTP1.1是无状态的

HTTP和HTTPS哪些区别

HTTPS如何通过数字证书防止服务器冒充


HTTPS如何防止信息泄露(被窃听)


HTTPS如何建立连接的

什么是RPC?RPC和HTTP的主要区别


Websocket如何建立连接


为什么要用websocket,和http相比优势


TCP三个特点


TCP握手为什么是三次,不是两次和四次


为什么每次TCP连接,初始序列号都不一样

为什么TCP挥手需要四次

什么是TCP快速重传

什么是TCP流量控制


什么是TCP拥塞控制


如何理解TCP是面向字节流的协议,UDP是面向什么的?

IP都有分片机制,为什么还要有TCP的MSS

为什么要区分网络号和主机号

为什么要划分网络号和子网号




面试


select * from user inner join address on user.id = address.user_id where user.sex = ‘male’ and user.age >= 18 and address.location=‘henan’ and address.type=2。要优化这个sql,如何建立索引

1.如果驱动表是address,那么查询过程是:先查到location='henan' and type=2user_id,再拿user_id去user表中查记录,并返回id=user_id and sex='male' and age>=18的记录。因此首先有索引(location, type, user_id),注意user_id在最后。由于是select *,因此之后在user表的主键索引查询sex和age相关记录。

2.如果驱动表是user,那查询过程是:首先查user.sex = 'male' and user.age >= 18的对应user记录(包括id),之后在address查对应userid对应的location和type,如果有联合索引(userid,location,type),则查联合索引,找到符合location和type条件的索引节点,之后过滤掉不符合的userid对应记录,剩余的user记录就是结果。所以我们可以建立索引user表:(sex, age)和address表(userid, location, type),或者user表直接用聚簇索引,只建立address表(userid, location, type)


threadlocalmaps为什么设计,key对threadlocal是弱引用,value对存数据是强引用?

1.key如果对threadlocal是强引用,那么如果threadlocal是局部变量,或者用户手动置为null,threadlocalmaps也不会释放该key,threadlocal对象仍然存在堆中。如果是弱引用就会被GC回收
2.value如果对存数据是弱引用,假设我们存了一个new User(),而该对象只被value引用,那么GC会直接回收,导致刚存就丢失了,因此要强引用。


threadlocal在生产中可能出现的问题?

1.由于key是弱引用,因此置threadlocal为null导致对象在堆中被回收,key变为了null,value还在,发生了内存泄露
2.在线程池环境中,如果上一个线程处理用户A请求没清理threadlocal,该线程服务下一个用户B,由于是同一个线程,B会拿到A的数据
3.假设userinfo的threadlocal不是final,而被人修改了,那就会导致同时所有线程的threadlocalmaps的key指向的该userinfo threadlocal(全都是这一个堆对象)不被引用而被GC回收,所有线程的用户信息都同时丢失


rocketmq事务执行流程

1.后端调用template的sendMessageInTransaction方法,并给出topic和消息负载
2.后端rocketmqtemplate发送一个半消息(和全消息负载一样)给broker,来表示我要发送消息了
3.1.broker如果没接收到半消息,那么后端的executeLocalTransaction中的业务逻辑根本不会执行,也不会发送全消息
3.2.broker接收到半消息,并返回给后端该半消息(Message msg),后端执行executeLocalTransaction方法
4.1.方法逻辑执行成功则return commit信号,后端发送commit信号给broker,broker把半消息转成全消息
4.2.方法逻辑执行失败则return rollback信号,后端发送rollback信号给broker,broker销毁半消息
4.3.方法逻辑执行成功return commit信号,但是commit信号没有到达broker,后端本地认为自己发送成功但是broker迟迟接收不到任何信号
4.4.broker收不到信号于是定时去查询后端,后端checkLocalTransaction方法接收,去查询redis或数据库,并重新返回commit和rollback信号。


rocketmq事务是否会有“本地事务执行失败回滚而消息发送了”的情况
@OverridepublicRocketMQLocalTransactionStateexecuteLocalTransaction(Messagemsg,Objectarg){try{// 1. 调用 Service。// 如果 commit() 在此处失败,这一行代码会直接抛出异常!orderService.doBusiness(msg);// 2. 如果上面抛了异常,这一行【永远不会执行】returnRocketMQLocalTransactionState.COMMIT;}catch(Exceptione){// 3. 异常被这里抓住了!log.error("本地事务执行异常(包括commit失败)",e);// 4. 返回 ROLLBACK,告诉 RocketMQ 删掉半消息returnRocketMQLocalTransactionState.ROLLBACK;}}

executeLocalTransaction上不能加@Transactional(防止事务嵌套,保证当doBusiness执行完事务就会提交),doBusiness要加@Transactional。在这种情况下,只要doBusiness事务提交失败,就会发生异常被捕获,发送ROLLBACK消息。因此规范写法下不会出现问题中的情况。


用一个表如user存储所有租户的user信息,会导致表过于庞大,如何解决

1.数据库水平分片。设计多个数据库以及路由算法,比如tenantid%2,利用ShardingSphere等中间件,可以对业务完全无感,代码仍然写select * from user,中间件自动帮你路由
2.数据库原生分区表。CREATE TABLE USER ( id BIGINT, ...) PARTITION BY HASH(tenant_id) PARTITIONS 100;可以将USER表预分成100个物理分区(相当于100个分表),同样业务完全无感,mysql底层自动帮助你进行分区路由


分库分表的算法有哪些
  • 直接取模
    简单但是假设库或表的数量变化,原本的取模逻辑就变化,就需要移动所有数据
  • 范围算法
  • 映射表算法
    专门设置一个key->ShardId的映射表。优点是非常灵活,缺点是每次都要多查询一个表
  • 一致性哈希算法

为什么不先更新mysql再更新redis,而是采用删除redis的策略?

先更新mysql再更新redis有一种意外情况:事务A更新mysql为V1,(过了1s)事务B更新mysqlV2,事务B更新redisV2,事务A更新redisV1。此时数据库为V1但是redis为V2


先更新mysql再删除redis,有没有特殊情况下的风险

事务A更新mysql,事务A删除redis,事务B读取到mysql旧值,事务B把旧值放到redis,事务Acommit。这时mysql是新值,但是redis是旧值。不过这种情况较少,因为删除redis到提交一般都挺快的。

这种情况的解决方案,延迟双删:先删除redis,再更新mysql,提交事务,等待几百毫秒,再删除redis。为什么要等几百毫秒,是因为期间可能有事务读到旧值并放入redis,我们要保证删除redis在其之后。为什么要先删除redis,因为等了几百毫秒才第二次删,期间防止redis有脏数据。


令牌桶限流算法能不能用于限制访问请求量,比如10s只允许10000个请求访问。如何实现

1.采用redis+lua脚本
2.为了防止每个请求都会访问redis造成频繁读写。用服务器批量取令牌并存在本地内存,请求在服务器内存取令牌
3.服务器定期或在令牌即将耗尽时异步取令牌,放入本地内存
4.redis在每次服务器取令牌时根据与前一次取令牌时间差进行补充令牌
5.如果redis没有令牌了则后端直接忽视请求


该算法虽然有一定的限流作用。但是我们知道每天各个时间段请求访问量是动态的。比如晚7点高峰,这会大量请求进入,而其他时间请求量较少。这种情况下该如何设计限流算法?
  • 首先需要明确一点:在流量高峰期应该放宽限流策略还是加紧限流策略?
  • 答案是看系统负载,如果CPU内存还有余力,则应缓慢增加令牌数;如果硬件告急,则应该强制降低令牌数。基于此我们有下面几个基本的限流算法设计:
    1.基于时间段动态限流
    2.监控CPU使用率,内存占用以及平均响应时间等。假如CPU<70%则说明策略偏保守,缓慢增加rate,CPU>80则强制降低rate
    3.核心业务和非核心业务采用不同的令牌桶,且核心业务分配更多令牌

分布式环境下,三级缓存中,如果mysql更新,如何保证caffeine中的本地缓存被清除
  • 由于是分布式环境,简单使用@CacheEvict只会清除本机的缓存,因此不适用
  • 解决方案
    • 使用MQ的广播模式。当某服务器执行更新操作时,发送广播消息到MQ的某topic,此外每个服务器都有消费者监控该topic,监控到则调用caffeine.invalidate(key)

nginx集群环境下,mysql更新要通知nginx删除缓存,这种情况下有一个基础的实现方案:nginx暴露清理缓存接口,后端或MQ消费者去调用接口清除nginx缓存。我们的这种方案有什么问题?如何解决?
问题

nginx集群,该请求所有nginx的接口么?如果nginx集群扩容了呢?

解决方案

最基础的解决方案是,在redis中除了要存具体的数据外,额外存一个键值对,用来注明该键值对的版本号。nginx在每次请求时都去请求一次redis,查询版本号(比查全量数据要快)与自己本地缓存的版本号对比。如果不对则请求放到后端

这个解决方案有几个问题:

  • 每次请求都要查一次redis,造成大量负担,此时nginx缓存就失去初衷了
进阶解决方案

在redis中除了要存具体的数据外,额外存一个键值对,用来注明该键值对的版本号。nginx每隔一秒去请求一次redis,并放到lua_shared_dict共享内存中。查询版本号(比查全量数据要快)与自己本地缓存的版本号对比。如果不对则请求放到后端

该方案不用每次请求都访问redis,保证了1s访问一次redis,以及缓存的实时更新。同时1s的延迟在零售场景下完全可以接受
但是有一个严重问题:

  • 假设100个nginx服务器,每个服务器16个 worker,同时请求redis版本号,造成缓存击穿
最终解决方案1

在redis中除了要存具体的数据外,额外存一个键值对,用来注明该键值对的版本号。nginx每隔一段时间去请求一次redis,并放到lua_shared_dict共享内存中,并使用共享锁,锁定nginx内部worker对redis的请求,确保每个nginx服务器只有一个worker在请求redis。查询版本号(比查全量数据要快)与自己本地缓存的版本号对比。如果不对则请求放到后端

原本的100*16个请求被缩小到了100个

最终解决方案2

采用rocketMQ广播消息,100个nginx服务器作为独立消费者监听并主动更新本地shared_dict


当前项目实现了乐观锁扣减库存防止超卖,redis扣减库存并异步插入mysql订单记录。不过这里没有涉及到用户支付操作,请给出如果要加上用户支付,基本的业务流程该是什么样的
  • 用户点击下单,redis判断库存是否充足并扣减库存,扣减成功则发送MQ延迟消息,并发送MQ订单入mysql库消息,用户点击支付并付款,异步入mysql库付款记录并更新订单状态UNPAID->PAID。
    如果用户没有点击支付,则延迟消息到期,查询付款记录,发现查不到于是认定用户未支付,更新订单状态为CANCELED,清除已下单用户id,并往redis中补充库存

这个业务流程有个很大的漏洞,假设延迟消息过期时间15min,在刚好15min这个节点,用户支付的回调执行,要更新订单状态并生成支付记录,延迟消息过期回调也要执行,更新UNPAID为CANCELED并添加redis中库存。这该怎么办
  • 我的方案是使用乐观锁,UPDATE ORDER SET STATUS = CANCELLED WHERE ID = ORDERID AND STATUS = UNPAID这样可以保证只有一个能执行成功,之后判断如果CANCEL执行成功了就redis.incr。这里如果支付回调支付失败,则需要后续处理,比如退钱,逻辑删除支付记录等

上述业务流程还有一个漏洞:延迟队列到期时,更新订单状态以及更新redis信息,如何保证mysql和redis操作的一致性
  • 我认为还可以用canal+MQ的方案。canal监控订单状态由UNPAID变为CANCELED,于是发送MQ消息,消费者异步更新redis信息。

对于清除redis下单用户id和补充redis中库存这样的非幂等操作(没有主键保证幂等性),假如MQ消费者消费成功但是返回给MQ的信息丢失,可能会重复消费。如何解决
  • 我认为可以使用lua,把判断用户id是否在下单用户set中,清除set中该用户id,补充redis库存,三个步骤放在一起。如果用户id不在set中则不补充库存。

如何防止同时 qps 过高导致瞬间全部访问某个 key(热点key,比如明星发布博客) 的请求到达 redis 造成 redis 炸了,不是缓存击穿。如何解决。
  • 首先理解问题含义,不是缓存击穿,缓存击穿指的是击穿了数据库,这里是击穿了redis
  • 其次,超多请求同时到达redis,此时caffeine还没有被填入数据(还没有一个请求执行完)
  • 解决思路是:锁+双重检查+caffeine只存储热点key
  • 其实思路和缓存击穿类似,只是caffeine不能存所有数据,因为JVM内存很宝贵,因此需要判断热点key并放到caffeine中,非热点key没必要放到caffeine中。

nginx,caffeine,redis分别应该存什么

nginx和caffeine应该存公有的,相对稳定的热点数据。至于订单信息等高频变动的私有数据不能存nginx缓存里。
商品信息就可以存nginx,caffeine中,nginx一般存的是具体商品的json字符串,可以直接返回给前端,caffeine和redis也存,caffeine由于直接运行在JVM,可以直接存对象,redis存序列化后数据或者HASH,caffeine减少对redis的压力。


你的布隆过滤器在初始化时就指定了比特位大小,那万一业务量激增,布隆过滤器比特位太少了呢?

使用redisbloom本来就实现了自动扩容逻辑。底层实现是分层布隆过滤器


布隆过滤器不支持删除操作,假如一个商品此时过期,大量请求通过,还是会有缓存穿透的风险。如何解决
  • 不单单使用布隆过滤器,加上缓存空值
  • 使用布谷鸟过滤器替代布隆过滤器,布谷鸟过滤器支持删除操作

什么是本地事务表

为了保证事务执行与消息发送的一致性,在早期由于MQ没有事务消息,因此需要我们自己实现本地事务。具体的实现方式是:

  • 在该事务中的最后往本地事务表t_local_transaction中插入一个事务记录,包括要发送的消息内容,主题,状态(待发送已发送发送失败),重试次数等。
  • 由于和原事务在一个事务中,因此原事务和事务表插入要么一起成功要么一起失败。
  • 事务提交成功后发送一个消息到MQ中,发送成功则把事务表状态改为已发送
  • 起一个定时任务定时把待发送任务消息发送,防止事务提交成功但是消息发送失败

本地事务表高度定制,但是要自己写重试逻辑,次数等。并且定时扫描消耗大量资源,MQ事务消息是被动回查,对数据库压力小的多。并且本地事务表每次执行事务都要多访问一次mysql来存储事务信息,降低吞吐量。


假如某次EXPLAIN,某sql语句的rows明显偏大,且走了索引,排除了索引失效的问题。你认为还能是什么问题?
索引区分度过低,比如sex
LIMIT

SELECT * FROM user WHERE age > 18 ORDER BY id LIMIT 100000, 10;先找到age>18的记录后,返回其中第100000到100010的记录。那么就需要扫描1-100010记录才能结束。


什么是索引下推ICP?

假设你有一张用户表,建立了联合索引:(name, age)
执行 SQL:SELECT * FROM user WHERE name LIKE '张%' AND age = 20;

没有 ICP 时(5.6 之前):

  1. 存储引擎通过索引找到所有名字叫“张xx”的人的 ID(假设有 1000 个)。
  2. 回表 1000 次,把这 1000 人的完整信息从磁盘读出来。
  3. Server 层最后过滤出age = 20的那 10 个人。
  4. 代价:990 次无效的回表(随机 I/O)。

开启 ICP 时(5.6 之后):

  1. 存储引擎找到名字叫“张xx”的索引记录。
  2. 原地判断:由于索引里刚好也存了age字段,引擎直接看这行索引里的age是不是 20。
  3. 精准回表:只有age真的等于 20 的那 10 个人,才会执行回表。
  4. 代价仅 10 次回表。性能提升了 100 倍。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 3:53:55

需求用例的写法

一、为什么写需求用例 流程图为需求用例提供了关键路径&#xff0c;而需求用例则是对业务场景的全面还原。本文将从以下四个方面阐述用例的信息&#xff1a; 用例的定义用例的粒度用例的例子用例的关键点解释 我写需求文档有几大准则&#xff0c;是需要时刻铭记和实践的&…

作者头像 李华
网站建设 2026/5/2 7:34:15

AI搜索时代,跨境电商获客的核心逻辑变了

在数字营销快速迭代的今天&#xff0c;AI搜索已彻底改变跨境电商的获客逻辑&#xff0c;不再是传统“关键词堆砌”就能抢占流量的时代。据《2025珠三角数字营销行业发展白皮书》数据显示&#xff0c;2025年珠三角跨境电商AI搜索营销市场规模突破120亿元&#xff0c;同比增长47%…

作者头像 李华
网站建设 2026/4/27 1:26:38

OpenClaw+Qwen3.5-9B智能相册:自动归类旅行照片并生成游记

OpenClawQwen3.5-9B智能相册&#xff1a;自动归类旅行照片并生成游记 1. 为什么需要智能相册管理 每次旅行回来&#xff0c;手机里总是堆满几百张照片。手动整理的过程既枯燥又耗时——要按日期创建文件夹、重命名文件、筛选重复照片&#xff0c;最后还得绞尽脑汁写游记。作为…

作者头像 李华
网站建设 2026/4/26 20:36:09

ISDANet:交互式与监督双模式注意力的遥感变化检测

前言 遥感变化检测就是给定同一地区在两个时间点拍摄的遥感图像&#xff0c;判断哪里发生了变化。比如&#xff0c;一块空地几年后变成了建筑群&#xff0c;或者道路扩建了&#xff0c;或者植被发生了明显变化。这类任务在城市规划、灾害评估、生态监测里都很重要。但问题是&a…

作者头像 李华
网站建设 2026/4/27 14:06:35

LM-Studio-0.4.10 安装完,不显示显卡信息

问题设置模型加载参数时&#xff0c;GPU Offload默认是0Settings Hardware不显示显卡信息解决办法在Settings的Runtime里安装CUDA&#xff0c;就好了 CUDA 12 llama.cpp (Windows) Nvidia CUDA 12.8 accelerated llama.cpp engine可以先把Missing libraries的Harmony安装下。解…

作者头像 李华
网站建设 2026/4/30 4:15:48

深入篇第2节:高效排序——在GPU上实现基数排序与合并排序

引言 排序是计算机科学的基石,在GPU上实现高效排序则是并行算法设计的试金石 上一节我们学习了并行扫描,它是许多并行算法的基础。今天,我们将挑战一个更具难度的任务:排序。 在CPU上,排序算法已经非常成熟:快速排序、归并排序、堆排序,平均复杂度 O(n log n)。但在GPU…

作者头像 李华