Redis+Spring+MyBatis + 微服务 + 消息队列核心知识点(面试高频题目合集)

1. Redis

1. Redis的使用场景?

(1) 结合简历的业务回答(通常情况下是缓存和分布式锁)。其中下一个问题:

复制代码
1. 缓存:穿透、击穿、雪崩、双写一致、持久化、数据过期、淘汰策略(2-8)
2. 分布式锁:setnx、redisson(9开始)

(2) 例如什么是缓存穿透?怎么解决?

复制代码
1. **正常流程**:查询数据时,先查Redis缓存→缓存命中,直接返回,不用访问MySQL数据库。或者redis未命中查DB,DB查询到结果返回,并且将数据存储到redis。

2. **缓存穿透**指客户端持续请求数据库和缓存中都不存在的数据,这类请求永远无法命中缓存,会直接穿透到MySQL数据库;又因为查询结果为空、不会回写缓存,导致相同的无效请求每次都会直达数据库,持续占用数据库连接、消耗数据库性能,严重时会造成数据库过载甚至宕机。

3. **方案一:缓存空数据。**缓存空数据是最简易的修复方案,当数据库查询不到目标数据时,依然将空值、空占位符写入缓存,并配置一个较短的过期时间;后续相同的无效请求可以直接命中缓存拦截,不再访问数据库,快速缓解穿透压力,仅需少量改造即可快速落地,适合大多数常规业务场景。

4. **方案二:布隆过滤器**。布隆过滤器是一种空间效率极高的概率型容器,提前将系统所有合法有效的数据Key预先存入过滤器中;请求到来时先进行前置校验,若过滤器判定数据一定不存在,则直接拦截请求、禁止访问缓存与数据库,仅判定大概率存在的请求才放行至后续链路,能够高效抵御大规模恶意穿透攻击,高并发防护能力极强。

(3) 例如什么是缓存击穿?怎么解决?

复制代码
1. **缓存击穿**指某个热点高频访问的Key设置了过期时间,当该热点Key刚好过期失效的瞬间,恰好有海量高并发请求同时涌入;此时缓存临时失效,所有并发流量会全部直接击穿缓存、直达底层数据库,瞬间激增的数据库访问压力极易导致数据库卡顿、连接占满甚至直接被压垮。

2. **方案一:互斥锁。**当缓存热点Key过期后,只允许抢到分布式锁的唯一一个线程去查询数据库、更新缓存,其余大量并发线程等待重试或者直接读取旧缓存数据;该方案可以严格保证数据的强一致性,精准控制同一时间仅一个请求重建缓存,不会打崩数据库,但多线程排队等待会产生阻塞,整体系统吞吐量下降、并发性能较差,同时还需要额外处理锁的死锁、释放和重试逻辑。

3. **方案二:逻辑过期**。逻辑过期方案不会真的让缓存物理过期,缓存中同时存储业务数据与逻辑过期时间标记,数据永久缓存、不主动淘汰;当请求发现数据逻辑过期后,仅单独开启一个异步后台线程去数据库更新缓存、刷新过期标记,前端所有用户请求依旧可以直接返回当前缓存中的旧数据,全程无阻塞、系统高可用且并发性能极强;缺点是异步刷新的机制无法保证数据绝对实时一致,会短暂存在缓存数据和数据库数据不一致的窗口。

(4) 例如什么是缓存雪崩?怎么解决?

复制代码
1. **缓存雪崩**是缓存高并发问题中影响范围最大的故障场景,指同一时间段内大批量缓存Key集体过期失效,或是Redis整体集群宕机、缓存服务完全不可用,导致系统中几乎所有流量都无法命中缓存,海量请求瞬间全部直接涌入底层数据库,瞬间超出数据库承载上限,给数据库带来毁灭性的巨大压力,极易引发整个业务系统全线崩溃。

2. **方案一:给不同Key的TTL添加随机值。**绝大多数缓存雪崩的诱因,是大量业务缓存设置了相同的过期时间,导致集体到期批量失效;通过在原本统一的TTL基础上,为每个Key叠加一个随机的偏移时间,打散所有缓存的过期时刻,就可以避免大批量缓存集中在同一秒同时失效,从根源上错开缓存重建高峰,分散数据库瞬时压力,实现成本最低、改动最小的前置预防效果。

3. **方案二:利用Redis集群提高可用性**。单节点Redis存在单点故障风险,一旦宕机就会造成全量缓存不可用,直接触发雪崩;搭建Redis主从、哨兵或是集群分片架构后,缓存服务具备了故障自动转移、数据多副本容灾、负载均衡的能力,即使个别节点故障,整体缓存服务依然可以正常对外提供读写能力,大幅提升缓存整体的高可用,杜绝因 Redis 服务整体宕机引发的雪崩问题。

4. **方案三:给缓存业务添加降级限流策略**。当缓存大面积失效、流量异常激增时,通过网关、服务层提前配置限流规则,限制单位时间内访问数据库的最大请求数量,同时配套业务降级预案,对于超出流量阈值的请求直接返回预设兜底结果、友好提示,既可以保护数据库不会被瞬时大流量压垮,又能保证核心基础业务可用,故障期间不会完全瘫痪,是雪崩发生时的最后一道防护屏障。

5. **方案四:给业务添加多级缓存**。多级缓存一般包含浏览器本地缓存、Nginx本地缓存、应用进程内本地缓存、Redis分布式缓存多层结构,即使分布式Redis缓存整体失效、大面积雪崩,上层的本地缓存依然可以拦截绝大多数用户请求,大幅直达数据库的流量体量;同时分层架构也拉长了故障传导链路,大幅降低极端场景下数据库被击穿、系统雪崩的风险,整体抗灾能力大幅提升。

(5) Redis作为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)

复制代码
1. **双写一致性**当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致。

2. **读操作:**缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间。

3. **写操作:**延迟双删。(删除缓存-->修改数据库-->删除缓存(延时))

4. **先删除缓存还是先修改数据库?都会出现问题**

    1) **先修改数据库,再删除缓存**。**正常情况**:无并发读写时,先更新数据库数据,再立即清空缓存,后续查询会从数据库拉取新数据写入缓存,数据可以保持一致。**异常情况**:高并发下,写操作刚改完库、还没删缓存时,读请求刚好命中旧缓存,直接返回旧数据;且旧缓存还没被删除,会长期存在脏数据,直到缓存过期才能恢复一致。

    2) **先删除缓存,再修改数据库**:**正常情况**:无并发读写时,先清空缓存,再更新数据库,后续查询缓存缺失,自动从数据库加载新数据入缓存,数据保持一致。**异常情况**:高并发下,写请求删完缓存、还未更新数据库的间隙,读请求进来查询数据库旧数据,并把旧数据重新写入缓存;后续写请求更新完数据库,缓存却被回填成旧数据,造成永久数据不一致。

5. **为什么要删除了两次缓存:**避免数据库和缓存不一致的情况(也就是在修改完数据库后再更新一次缓存)。

6. 为什么要延时双删:一般情况下数据库是主存模式,是读写分离的,延时一会来保证主节点把数据同步到从节点。但是也有可能出现问题,在延时过程中由于时间不可控还是可能出现脏读。要想保证强一致性可以采用**互斥锁(读数据采用共享锁,支持其他线程读但是不允许写;写数据时加排他锁,不允许其他线程读和写)**。允许延时一致的业务采用异步通知。

(6) Redis作为缓存,数据的持久化是怎么做的?(持久化)

复制代码
1. **持久化:**Redis提供了两种数据持久化的方式:RDB和AOF。

2. **RDB:**把内存中的数据都记录到磁盘中,当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。执行原理:Redis执行RDB持久化时会通过fork创建子进程,利用写时复制COW机制让父子进程共享内存数据,子进程遍历内存将某一时刻的全量数据以二进制快照形式写入临时RDB文件,写入完成后替换旧的RDB文件;主进程无需阻塞,可正常处理客户端请求,重启服务时直接加载RDB文件就能快速恢复数据。
3. **AOF:记录写入命令的日志文件。
  1. RDB和AOF各有优点,如果对数据安全性较高,在实际开发中往往结合两者来使用。

(7) Redis的key过期之后,会立即删除吗?(数据过期策略)

复制代码
1. **数据过期策略:**Redis对数据设置了数据的有效时间,数据过期以后,就需要将数据从内存中删除掉。可以按照不同的规则进行删除,这种删除规则就被称为数据的删除策略(数据过期策略)。包含两种过期策略:惰性删除、定期删除。

2. **惰性删除:**key过期后不立即删除,等到下次客户端访问该key时,Redis才检查过期时间,若已过期就直接删除并返回空。

    1) **优点**:对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。

    2) **缺点**:对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放。

3. **定期删除:**Redis每隔一段时间主动随机抽取一批key进行检查,删除过期数据,平衡内存占用和CPU开销。

    1) **SLOW模式**是定时任务,执行频率默认为10hz,每次不超过25ms,可以通过修改配置文件redis.conf的hz选项来调整这个次数

    2) **FAST模式**执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms。

    3) **优点**:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。

    4) **缺点**:难以确定删除操作执行的时长和频率。

(8) 缓存过多,内存是有限的,内存被占满了怎么办?(数据淘汰策略)

复制代码
1. **数据淘汰策略:**当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。

2. Redis支持8种不同策略来选择要删除的key:

    1) **noeviction:**不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略。
    2) **volatile-ttl:**对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰。
    3) **allkeys-random:**对全体key,随机进行淘汰。
    4) **volatile-random:**对设置了TTL的key,随机进行淘汰。
    5) **allkeys-lru:**对全体key,基于LRU算法进行淘汰。
    6) **volatile-lru:**对设置了TTL的key,基于LRU算法进行淘汰。
    7) **allkeys-lfu:**对全体key,基于LFU算法进行淘汰。
    8) **volatile-lfu:**对设置了TTL的key,基于LFU算法进行淘汰。
    **9) LRU (Least Recently Used) 最近最少使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
    10) LFU (Least Frequently Used) 最少频率使用。会统计每个 key 的访问频率,值越小淘汰优先级越高。**
3. **使用策略:**
    1) 优先使用allkeys-lru策略。充分利用LRU算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。
    2) 如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用 allkeys-random,随机选择淘汰。
    3) 如果业务中有置顶的需求,可以使用volatile-lru策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。
    4) 如果业务中有短时高频访问的数据,可以使用allkeys-lfu或volatile-lfu策略。

(9) Redis分布式锁,是如何实现的?

复制代码
1. **分布式锁:**主要利用Redis的setnx命令。setnx是SET if not exists(如果不存在, 则 SET)的简写。

    1) **获取锁:**执行SET lock:key唯一标识NX EX超时时间命令。
    2) **释放锁:**业务执行完成后,执行DEL lock:key命令删除锁。

2. **锁超时控制方案:**

    1) **基于业务执行时间预估**:根据业务平均耗时,设置合理的锁超时时间,避免锁长期占用。
    2) **锁续期机制(看门狗)**:通过后台线程定期延长锁的有效期,防止业务未完成时锁提前过期,适用于执行时间较长的业务场景。

3. **执行流程说明:**

    1) 客户端尝试获取锁,若成功则执行业务逻辑;
    2) 若获取失败则等待重试;
    3) 业务执行完成后主动释放锁;
    4) 若业务超时或服务宕机,锁会因设置的过期时间自动释放,避免死锁。

4. **红锁:**

    1) 红锁(RedLock)是Redis官方提出的高可用分布式锁算法,用来解决单节点/主从Redis锁丢失、双客户端同时加锁的问题,核心是:多独立节点+多数派共识(N/2+1)才算加锁成功。
    2) 为什么需要红锁(痛点):
        a. 普通Redis主从锁有致命问题:
        b. 客户端A在主节点加锁成功;
        c. 主节点没同步到从节点就宕机;
        d. 从节点变新主,锁数据丢失;
        e. 客户端B也加锁成功→两个客户端同时持有锁,导致超卖、数据错乱。

(10) Redis集群有哪些方案?

复制代码
**三种集群方案**:主从复制、哨兵模式、分片集群。

1. **主从复制(基础数据备份):**一个主节点负责写,多个从节点同步主节点数据、负责读。

    1) **作用:**数据备份+读写分离,提升读性能,但没有自动故障转移能力,主节点挂了服务就没法写了。
    2) **主从数据同步的流程:**Redis主从复制分为全量同步和增量同步两个阶段。
    3) **全量同步(初次/断连恢复时触发):**从节点向主节点发送PSYNC命令,主节点生成RDB快照发送给从节点;同时将快照期间的写命令存入缓冲区,待RDB传输完成后补发,让从节点一次性完成数据初始化。
复制代码
    4) **增量同步(主从正常运行时触发):**主节点将后续所有写命令,通过复制流实时发送给从节点;从节点逐条执行命令,保持与主节点数据一致;若网络短暂中断,恢复后可通过复制积压缓冲区补发缺失命令,实现断点续传。
复制代码
2. **哨兵模式(高可用版主从):**

    1) **哨兵模式**:在主从复制的基础上,加上哨兵节点监控主节点状态,主节点挂了自动从从节点里选出新主节点。
        a. **作用**:实现主从自动故障转移,保证服务持续可用,但本质还是一套主从架构,数据不拆分,内存上限受限于单节点。
复制代码
    2) **脑裂**:脑裂是Redis集群/哨兵模式中一种严重的网络分区故障:集群节点因网络中断被分成两个独立的部分,各自选举出主节点,导致系统出现两个主节点同时接收写入请求,最终造成数据不一致。
    3) **场景**:原主节点因网络波动与哨兵断开,哨兵判定其宕机并选出新主节点;网络恢复后,原主节点仍认为自己是主节点,继续接收写入请求,造成两个主节点同时写入。
    4) 核心解决思路:修改redis配置,设置最少的从节点数量和缩短主从数据同步的延迟时间,达不到要求就拒绝请求;


3. **分片集群(高可用版主从):**

    主从和哨兵可以解决高可用、高并发读的问题,但还是存在海量数据存储问题,高并发写的问题。使用分片集群可以解决上述问题,分片集群作用:

    集群中有多个master,每个master保存不同数据;
    每个master都可以有多个slave节点
    master之间通过ping监测彼此健康状态
    客户端请求可以访问集群任意节点,最终都会被转发到正确节点

    1) **分片集群**:Redis分片集群引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。

2. Redis是单线程的,但是为什么还那么快?

(1) Redis是纯内存操作,执行速度非常快

(2) 采用单线程,避免不必要的上下文切换和竞争条件,多线程还要考虑线程安全问题

(3) 使用 I/O 多路复用模型,非阻塞 IO:

引申出其他问题:

(1) 能解释一下I/O多路复用模型

复制代码
Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型主要就是实现了高效的网络请求。I/O多路复用是一种网络I/O模型,是**利用单个线程来同时监听多个Socket(客户端连接)**,并在某个Socket刻度、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
复制代码
1. I/O多路复用的**实现方式**:

(2) Redis网络模型

复制代码
1. 多个客户端连接通过**IO多路复用+事件派发机制**,由单线程分别处理连接建立、请求读取和回复发送,而命令执行部分采用串行单线程处理,保证数据一致性的同时,也能高效应对高并发连接。
2. 读请求解析和写回复发送部分**可采用多线程处理**,进一步提升网络 IO 效率,但核心命令执行仍保持单线程,避免并发问题。

2. 框架篇

1. Spring的bean是单例的吗?单例bean是线程安全的吗?

Spring 中的 Bean 默认是单例的(@Scope("singleton"))。单例 Bean 不一定是线程安全的,是否安全取决于该 Bean 是否包含可变的成员变量。

如果 Bean 是无状态的(例如只提供方法,没有可修改的字段),则是线程安全的;如果 Bean 有可变的实例变量,并且会被多个线程并发修改,则存在线程安全问题,可以通过改用原型作用域、加锁、使用 ThreadLocal 或无状态设计来解决。

2. 什么是AOP?你的项目中有没有使用到AOP?Spring中的事务是如何实现的?

AOP 称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块(抽取为公共模块进行复用),这个模块被命名为切面(Aspect),减少重复代码,降低模块间的耦合度,提高系统的可维护性。

常见的AOP使用场景:

  • 记录操作日志
  • 缓存处理
  • Spring中内置的事务处理

(1) 记录操作日志 :通过AOP切面拦截指定方法(如Controller或Service层),在方法执行前后获取请求参数、返回值、执行时间等信息,并写入日志系统。

(2) 缓存处理 :使用AOP在方法执行前检查缓存是否存在(@Around),若存在直接返回缓存值;否则执行目标方法,将结果存入缓存。

(3) Spring内置的事务处理:@Transactional注解通过AOP实现:代理对象拦截被注解的方法,在方法执行前开启事务,执行后根据异常情况提交或回滚事务。

(4) Spring中的事务:Spring支持编程式事务管理声明式事务管理两种方式。

  • 编程式事务控制:需使用TransactionTemplate来进行实现,对业务代码有侵入性,项目中很少使用。
  • 声明式事务管理 :建立在AOP之上。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

3. Spring中事务失效的场景有哪些?(对spring框架的深入理解、复杂业务的编码经验)

事务失效场景:

  • 异常捕获处理
  • 抛出检查异常
  • 非public方法

分别解释这三种是如何导致场景失效的

(1) 异常捕获处理

  • 原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉。
  • 解决 :在catch块添加throw new RuntimeException(e)抛出。

(2) 抛出检查异常

  • 原因:Spring默认只会回滚非检查异常。
  • 解决 :配置rollbackFor属性@Transactional(rollbackFor=Exception.class)。这种只要有异常都会回滚

(3) 非public方法

  • 原因:Spring为方法创建代理、添加事务通知、前提条件都是该方法是public的。
  • 解决:改为public方法。

4. Spring的bean的生命周期?

Spring Bean 的生命周期大致分为:构造→注入→初始化前→初始化→初始化后→使用→销毁。核心流程如下:

(1) 通过BeanDefinition获取bean的定义信息

(2) 调用构造函数实例化bean对象

(3) Bean的依赖注入,通常情况下是set方法注入

(4) 处理Aware接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware),以Aware结尾的一些接口,实现这些接口之后,有些方法需要重写再去执行对应方法。

(5) Bean的后置处理器BeanPostProcessor-前置:调用BeanPostProcessor的postProcessBeforeInitialization方法。

(6) 初始化方法

  • 若Bean实现了InitializingBean,调用afterPropertiesSet()。

  • 或执行自定义的init-method。

(7) Bean的后置处理器BeanPostProcessor-后置:调用BeanPostProcessor的postProcessAfterInitialization(此处产生AOP代理)

(8) 销毁

  • 若Bean实现了DisposableBean,调用destroy()。

  • 或执行自定义的destroy-method。

5. Spring的循环引用?

(1) 循环引用:在创建A对象的同时需要使用B对象,在创建B对象的同时需要使用A对象(两个对象互相引用);也可以是A对象依赖B对象,B对象依赖C对象,C对象依赖A对象;A依赖于自己。

(2) 问题:可能造成死循环(循环依赖/引用):当Spring创建依赖B的A对象时,会先实例化A并得到半成品对象,初始化A时发现容器中没有B对象,便转而创建依赖A的B对象,B初始化时又发现容器中没有已完成的A对象,于是再次触发A的创建流程,如此往复循环,两个对象的创建过程始终卡在互相等待对方完成的节点,无法推进到赋值并返回的阶段,最终形成死循环。如下图所示:

(3) 解决方法:三级缓存,可以解决大部分循环依赖(可以解决初始化过程中的循环依赖,不能解决构造函数产生的循环依赖)。

复制代码
创建Bean时先把对象工厂放入三级缓存,其他Bean依赖它时,通过三级缓存的工厂获取早期引用并放入二级缓存,最终初始化完成后再存入一级缓存,以此打破循环依赖的死循环。具体流程:

1. 创建 A:实例化→将A的对象工厂放入三级缓存→开始属性填充,发现需要B。
2. 尝试获取B:在获取Bean的过程中,先从一级缓存找;没有则从二级缓存找;还没有则从三级缓存找(执行对象工厂的获取对象方法来得到早期引用,可能是原始对象或 AOP 代理)→将该早期引用放入 二级缓存,并移除三级缓存。
3. 创建B:实例化→将B的对象工厂放入三级缓存→属性填充时发现需要A。
4. 获取A:此时A已存在于 二级缓存(或通过三级缓存中的对象工厂生成后移入二级缓存),B直接拿到A的早期引用,完成注入。
5. B继续初始化→初始化完成后,将B移入一级缓存(成品)。
6. 回到A:A收到B的成品注入,继续完成自己的初始化→初始化完成后将A移入一级缓存。

(4) 但对于构造方法注入导致的循环依赖,三级缓存无法解决。解决方案是加上@Lazy注解,实现延迟加载:只有在真正需要使用该Bean时才会创建对象,而不是在实例化阶段就立即注入依赖。

6. SpringMVC的执行流程?

SpringMVC的执行流程是这个框架最核心的内容。包含以下两个内容:

  • 视图阶段(老旧JSP等)
  • 前后端分离阶段(接口开发、异步)

(1) 视图阶段(JSP)执行流程:

复制代码
1. 用户发送出请求到前端控制器DispatcherServlet
2. DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
3. HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4. DispatcherServlet调用HandlerAdapter(处理器适配器)
5. HandlerAdapter经过适配调用具体的处理器(Handler/Controller),也就是具体的Controller中的方法
    1) Controller执行完成返回ModelAndView对象
    2) HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
6. DispatcherServlet将ModelAndView传给ViewResolver(视图解析器)
7. ViewResolver解析后返回具体View(视图)
8. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
9. DispatcherServlet响应用户。

(2) 前后端分离阶段(接口开发、异步请求):

复制代码
1. **前端发起异步请求**:浏览器通过AJAX等方式请求 http://localhost:8080/user/getById/1。
2. **前端控制器(DispatcherServlet)接收请求**:作为统一入口,它根据URL查找处理器。
3. **处理器映射器(HandlerMapping)返回执行链**:找到匹配的处理器(Controller方法),返回 HandlerExecutionChain。
4. **处理器适配器(HandlerAdapter)执行处理器(Handler)**:适配器调用具体的处理器方法,处理器中不进行页面跳转,只执行业务逻辑(如查询用户数据)。
    1) **处理参数与返回值**:处理器方法上添加了@ResponseBody注解(或使用@RestController),表示处理器返回的对象需要直接写入HTTP响应体。
    2) **使用HttpMessageConverter转换数据**:Spring MVC根据请求头Accept或注解配置,选择合适的HttpMessageConverter(如MappingJackson2HttpMessageConverter)将处理器返回的对象转换为JSON格式。
    3) **返回JSON响应**:转换后的JSON数据作为响应体返回给前端,前端回调函数接收该数据进行局部页面更新。
    4) 2和3可以合并为HttpMessageConverter来返回结果转换为JSON并响应。

7. SpringBoot自动配置原理?

在Spring Boot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

(1) 其中@EnableAutoConfiguration是实现自动化配置的核心注解。

(2) 该注解底层通过@Import导入对应的配置选择器,该选择器会扫描当前项目和该项目引用的jar包中classpath下META-INF/spring.factories文件,读取里面预定义的自动配置类全类名,再配合条件注解根据当前项目环境、依赖是否存在等条件进行判断,满足条件的配置类就会生效,自动向Spring容器中注入所需Bean。

(3) @EnableAutoConfiguration通过@Import(AutoConfigurationImportSelector.class) 导入选择器,该选择器读取文件并按条件过滤,满足条件的配置类就会生效,自动向Spring容器中注入所需Bean。(简易版)

  • Spring Boot 2.7之前使用META-INF/spring.factories;2.7及之后版本改用META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(逐行列出自自动配置类),但spring.factories仍保留用于其他扩展。

8. Spring框架(Spring、SpringMVC、Springboot)常见注解有哪些?

(1) Spring是一个轻量级的企业级应用开发框架,核心提供IoC(控制反转)容器和AOP(面向切面编程),用于管理对象依赖和横切逻辑。

常见注解:

(2) SpringMVC是Spring框架中的一个Web模块,基于MVC模式设计,通过DispatcherServlet等组件处理HTTP请求与响应,它依赖Spring核心容器运行。

常见注解:

(3) Spring Boot是一个快速开发脚手架,底层依然使用Spring+Spring MVC,通过自动配置、起步依赖和嵌入式服务器简化配置和部署流程,让开发者能更快捷地构建Spring应用。

常见注解:

(4) Spring是基础容器,Spring MVC是基于Spring的Web模块,Spring Boot是快速开发与自动配置的封装工具。没有Spring就没有Spring MVC和Spring Boot;Spring Boot让Spring和Spring MVC用起来更便捷。

3. MyBatis

1. MyBatis的执行流程?

(1) 读取MyBatis配置文件:mybatis-config.xml加载运行环境和映射文件

(2) 构造会话工厂SqlSessionFactory

(3) 会话工厂创建SqlSession(会话)对象(包含了执行SQL语句的所有方法)

(4) 操作数据库的接口,也就是Executor执行器,同时也负责查询缓存的维护

(5) Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息

(6) 输入参数映射(java类型转化为数据库支持的类型)

(7) 输出结果映射(从数据库类型转换为java类型)

2. MyBatis是否支持延迟加载?

Mybatis支持延迟加载,但默认没有开启

(1) 什么叫延迟加载?

(2) 延迟加载原理:

  1. 使用CGLIB创建目标对象的代理对象

  2. 当调用目标方法user.getOrderList()时,进入拦截器invoke方法,发现user.getOrderList()是null值,执行sql查询order列表

  3. 把order查询上来,然后调用user.setOrderList(List orderList),接着完成user.getOrderList()方法的调用

3. MyBatis的一级和二级缓存?

(1) 一级缓存 :MyBatis的一级缓存是SqlSession级别的,默认自动开启。在同一个SqlSession中执行相同SQL和参数的多次查询时,第一次查询后会缓存结果,后续查询直接从缓存返回,不会重复访问数据库;当执行增删改操作或调用commit()、rollback()、close()时,一级缓存会被清空,且不同SqlSession之间的缓存互不影响。

(2) 二级缓存 :MyBatis的二级缓存是Mapper(namespace)级别的,需要手动配置开启(全局设置加标签)。它可以让缓存数据在不同SqlSession之间共享:当某个SqlSession关闭或提交时,其一级缓存结果会存入二级缓存,之后其他SqlSession执行同一namespace下的相同查询时可直接从二级缓存获取;但执行该 namespace下的增删改操作会清空二级缓存,且被缓存的对象必须实现Serializable 接口(为了让对象能在二级缓存中正常存储、传递、复制,MyBatis要求这些对象可被序列化,Serializable作用是允许一个类的对象被序列化。)

(3) 一级缓存、二级缓存都是基于PerpetualCache的HashMap本地缓存。

4. 微服务-Sping Cloud

1. Sping Cloud 5大组件有哪些?

(1) Eureka :服务注册中心,负责管理所有微服务的地址信息,实现服务的自动注册与发现。

(2) Ribbon :客户端负载均衡器,在服务调用方实现负载均衡,将请求按策略分发到多个服务提供者。

(3) Feign :声明式HTTP客户端,让开发者通过接口和注解即可调用远程服务,简化了服务间的通信代码。

(4) Hystrix :服务熔断器,提供容错机制,通过熔断和降级防止因个别服务故障引发"雪崩效应"。

(5) Zuul / Gateway :API网关,系统的统一入口,负责请求路由、过滤、鉴权等。

2. 服务注册和发现是什么意思?Sping Cloud如何实现服务注册发现?

常见的注册中心:eureka、nacos、zookeeper

(1) Eureka :Eureka作为Spring Cloud中的注册中心,采用AP模式实现服务注册与发现:服务提供者启动时将自己的信息(服务名、IP、端口等)注册到 Eureka Server;服务消费者从Eureka Server拉取服务列表,若提供者有集群,则配合Ribbon等负载均衡策略发起调用;同时,服务提供者每隔30秒发送心跳续约,如果Eureka Server连续90秒未收到心跳,则将该实例从注册表中剔除。

(2) Nacos :服务提供者注册到Nacos Server,消费者拉取服务列表;Nacos支持服务端主动检测提供者状态,其中临时实例采用心跳模式,非临时实例则采用主动检测模式(非临时实例心跳异常不会被剔除);同时Nacos支持服务列表变更时的消息推送模式,使服务列表更新更及时;Nacos集群默认采用AP模式,但当存在非临时实例时会切换为CP模式;此外Nacos还内置配置中心功能,可同时作为注册中心和配置中心使用。

(3)ZooKeeper :基于CP模式(优先保证一致性),通过临时节点和会话心跳维护服务实例的健康状态:服务提供者启动时在 ZK 中创建临时节点,若与 ZK 的心跳会话超时则该节点自动删除;服务消费者通过 watch 机制实时监听服务列表的变化,一旦有节点变动会立刻收到通知。

(4) 共同点 :都支持服务注册、服务拉取,以及通过心跳方式做健康检测。

(5) 区别

CAP 模式 :Eureka 仅支持 AP(可用性优先,允许数据不一致);ZooKeeper 仅支持 CP(一致性优先,网络分区时可能短暂不可用);Nacos 默认 AP,且当存在非临时实例时可切换为 CP 模式。

健康检测 :Eureka依赖客户端定时心跳,服务端连续90s未收到心跳则剔除实例;Nacos 支持心跳模式(临时实例)和服务端主动探测模式(非临时实例);ZooKeeper基于临时节点与会话心跳,会话超时则节点自动删除,无需额外剔除逻辑。

服务列表更新方式 :Eureka 需要消费者每30s主动拉取服务列表;Nacos 支持服务端变更后主动推送给消费者,同时消费者也可拉取;ZooKeeper 采用 watch 机制,服务节点变化时实时通知所有订阅的客户端。

额外功能 :Nacos 除注册中心外还内置配置中心,可同时管理配置;Eureka 仅作为注册中心;ZooKeeper 虽可做分布式协调(如选主、配置管理),但在 Spring Cloud 体系中通常只作为注册中心使用,且需单独搭建配置中心。。

(6) 知识点 :(第七个问题)

  1. AP模式:可用性(Availability)+分区容错性(Partition tolerance):在分布式系统中,当出现网络分区(部分节点无法通信)时,系统优先保证可用性(即每个请求都能收到响应,可能数据不一致),暂时牺牲一致性。

  2. CP模式:一致性(Consistency)+分区容错性(Partition tolerance):当出现网络分区时,系统优先保证数据一致性(所有节点数据相同),可能短暂牺牲可用性(某些节点不可写或返回错误)。

  3. 临时实例:默认注册方式。实例通过心跳向 Nacos 汇报健康状态(如每几秒发送一次心跳)。如果 Nacos 长时间收不到心跳,会主动将该实例从服务列表中剔除(下线)。适合大多数微服务场景(如订单服务、商品服务、用户服务等。

  4. 非临时实例:不需要心跳。Nacos 会主动往实例发请求检测是否存活。即使检测失败,也不会自动剔除,而是标记为不健康,由人工或策略决定后续处理。这种模式常用于需要长期稳定的服务实例(如数据库、配置中心等)。适合需要强一致性、人工管理或长期稳定的核心服务。

3. 负载均衡?

(1) 你们项目的负载均衡怎么实现的:微服务的负载均衡主要使用了一个组件Ribbon,比如,我们在使用feign远程调用的过程中,底层的负载均衡就是使用了ribbon。

复制代码
1. **发起请求**:order-service向服务名http://userservice/user/1发起远程调用。
2. **拉取服务列表**:order-service从注册中心拉取名为userservice的服务实例列表。
3. **返回服务列表**:注册中心返回可用实例地址,例如 localhost:8081、localhost:8082。
4. **负载均衡选择**:Ribbon使用默认轮询策略,从列表中选出一个实例(如轮询到 8081),再将请求转发至该实例对应的user-service。

(2) Ribbon负载均衡策略

1. RoundRobinRule: 简单轮询服务列表来选择服务器
2. WeightedResponseTimeRule: 按照权重来选择服务器,响应时间越长,权重越小
3. RandomRule: 随机选择一个可用的服务器

  1. BestAvailableRule: 忽略那些短路的服务器,并选择并发数较低的服务器

  2. RetryRule: 重试机制的选择逻辑

  3. AvailabilityFilteringRule: 可用性敏感策略,先过滤非健康的,再选择连接数较小的实例

7. ZoneAvoidanceRule: 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询

(3) 如果想自定义负载均衡策略如何实现 :可以自己创建类实现IRule接口,然后再通过配置类或者配置文件配置即可,通过定义IRule实现可以修改负载均衡规则,有两种方式:

4. 什么是服务雪崩?怎么解决

(1) 服务雪崩 :一个服务失败,导致整条链路的服务都失败的情形。

(2) 解决方法 :熔断降级。

  1. 服务降级 :服务自我保护的一种方式,或者保护下游服务的一种方式,用于确保服务不会受请求突增影响变得不可用,确保服务不会崩溃,一般在实际开发中与feign接口整合,编写降级逻辑。

  2. 服务熔断 :默认关闭,需要手动打开,如果检测到10秒内请求的失败率超过50%,就触发熔断机制。之后每隔5秒重新尝试请求微服务,如果微服务不能响应,继续走熔断机制。如果微服务可达,则关闭熔断机制,恢复正常请求。

(3) 预防策略 :限流。

  1. 限流:限制单位时间内进入系统的请求数量,超过阈值的请求被直接拒绝、排队或降级处理。目的是防止突发流量把系统冲垮。

5. 微服务是怎么监控的?

我们项目中采用的skywalking进行监控的

(1) skywalking主要可以监控接口、服务、物理实例的一些状态。特别是在压测的时候可以看到众多服务中哪些服务和接口比较慢,我们可以针对性的分析和优化。

(2) 我们还在skywalking设置了告警规则,特别是在项目上线以后,如果报错,我们分别设置了可以给相关负责人发短信和发邮件,第一时间知道项目的bug情况,第一时间修复。

6. 限流?

限流常见的算法有哪些?

(1) 漏桶算法 :请求先进入一个桶,桶底部以恒定速率 漏出请求进行处理,桶满则新请求被丢弃或排队。漏桶可以强制平滑突发流量,使输出速率完全固定,适合保护下游系统不被瞬时流量冲垮,典型应用是Nginx的限流模块。

(2) 令牌桶算法 :系统以固定速率向桶中添加令牌,桶有最大容量;每个请求需要从桶中获取一个令牌才能通过,否则等待或拒绝。令牌桶允许一定程度的突发(桶内积累的令牌可一次性处理突发请求),比漏桶更灵活,Spring Cloud Gateway默认使用该算法。

你们的项目是否涉及限流,怎么做的?

7. 解释一下CAP和BASE?

CAP定理?

(1) **Consistency(一致性):**用户访问分布式系统中的任意节点,得到的数据必须完全一致。

(2) Availability(可用性) :用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝。

(3) **Partition(分区):**因为网络故障或其他原因导致分布式系统中的部分节点与其他节点失去连接,形成独立分区。

(4) Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务。

BASE理论?BASE理论是对CAP的一种解决思路,包含三个思想:

(1) Basically Available(基本可用) :分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。

(2) Soft State(软状态) :在一定时间内,允许出现中间状态,比如临时的不一致状态。

(3) Eventually Consistent(最终一致性) :虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。

8. 分布式服务的接口幂等性如何设计?

幂等:多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致性。

(1) 如果是新增数据,可以使用数据库的唯一索引

(2) 如果是新增或修改数据

  1. 分布式锁,性能较低

  2. 使用token+redis来实现,性能较好

  1. 第一次请求,生成一个唯一token存入redis,返回给前端

  2. 第二次请求,业务处理,携带之前的token,到redis进行验证,如果存在,可以执行业务,删除token;如果不存在,则直接返回,不处理业务。

9. 你们项目中使用什么分布式任务调度?

(1) 定义 :xxl-job是一个开源的分布式任务调度平台,其核心设计为:通过一个中心化的调度中心(Admin)管理任务,执行器(Handler)以集群方式部署,从而解决分布式环境下的任务重复执行、高可用、失败重试、分片处理等问题,并支持灵活的 cron 表达式和图形化运维。

(2) 主要解决的问题 :集群环境下定时任务的重复执行、通过灵活的cron表达式定义任务调度、支持任务失败后的重试与统计、以及应对任务量大时的分片执行能力。

(3) xxl-job路由策略有哪些

(4) xxl-job任务执行失败怎么解决

  1. 路由策略选择故障转移,使用健康的实例来执行任务

  2. 设置重试次数

  3. 查看日志+邮件告警来通知相关负责人解决

(5) 如果有大数据量的任务同时都需要执行,怎么解决?

  1. 让多个实例一块去执行(部署集群),路由策略为分片广播

  2. 在任务执行的代码中可以获取分片总数和当前分片,按照取模的方式分摊到各个实例执行。

5. 消息中间件-RabbitMQ、Kafka

1. RabbitMQ如何保证消息不丢失?

RabbitMQ 保证消息不丢失需要从生产者、MQ自身、消费者三个环节分别处理:

(1) **生产者端:**开启生产者确认机制,消息成功到达交换机后Broker(服务端)会回传确认;若未收到确认,生产者可重发。另外,如果交换机和队列的绑定失败,可结合备用交换机或mandatory参数+ReturnCallback处理路由失败的消息。

(2) MQ端:

  1. 交换机、队列、消息全部持久化:创建时设置durable=true,发送消息时设置 deliveryMode=2。

  2. 使用镜像队列(集群模式):主节点宕机时从节点自动接管,防止队列数据丢失。

(3) 消费者端:

  1. 关闭自动ACK,改为手动确认(basicAck):消费者处理完业务逻辑后再ACK,如果处理失败且不ACK,消息会重新入队。

  2. 开启消费者失败重试机制:配置重试次数和间隔,重试耗尽后,将消息投递到异常交换机,最终由人工介入处理。

2. RabbitMQ消息的重复消费怎么解决?

(1) 什么情况导致消息重复消费:

  1. 网络抖动导致确认(ACK)未及时发送

  2. 消费者挂掉(未ACK后重启),消息重新入队,从而被再次消费了

(2) 解决方法:

  1. 每条消息设置一个唯一的标识id。

  2. 消费者端实现幂等:利用数据库唯一键、Redis分布式锁、状态表等方式,根据messageId判断是否已处理过。

3. RabbitMQ中的死信交换机?(RabbitMQ延迟队列有了解吗)

死信交换机(DLX)是一个普通的交换机,但它接收的是普通队列中的死信消息(即无法被正常消费的消息)。当队列设置了dead-letter-exchange属性后,该队列中的消息若因被拒绝且不重新入队、超时(TTL到期)、队列长度溢出而变为死信时,就会被自动转发到这个交换机。

延迟队列通常借助死信交换机+TTL实现:

  • 把消息发到一个不设置消费者的普通队列,同时给消息或队列设置TTL。
  • 消息超时后成为死信,被投递到绑定了目标队列的死信交换机,最终进入真正的业务消费队列,从而实现消息发送后延迟一段时间才被消费。

(1) **延迟队列:**进入队列的消息会被延迟消费的队列

  1. **场景:**超时订单、限时优惠、定时发布

(2) 死信交换机:

当一个队列中的消息满足下列情况之一时,可以成为死信(dead letter):

  1. 消费者使用basic.reject或basic.nack声明消费失败,并且消息的requeue参数设置为false

  2. 消息是一个过期消息,超时无人消费

  3. 要投递的队列消息堆积满了,最早的消息可能成为死信

如果该队列配置了dead-letter-exchange属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机称为死信交换机(Dead Letter Exchange,简称DLX)。

(3) TTL:

TTL,也就是Time-To-Live。如果一个队列中的消息TTL结束仍未消费,则会变成死信,TTL超时分为两种情况:

  1. 消息所在的队列设置了存活时间

  2. 消息本身设置了存活时间

4. RabbitMQ如果有100万消息堆积在MQ 怎么解决?(消息堆积怎么解决)

(1) **消息堆积:**当生产者发送消息的速度超过了消费者处理消息的速度,就会导致队列中的消息堆积,直到队列存储消息达到上限。之后发送的消息就会成为死信,可能会被丢弃,这就是消息堆积问题

(2) 解决方法:

  1. 增加消费者数量:水平扩容,启动更多消费者实例,共享消费队列

  2. 生产者限流:如果消费者无法快速扩容,可对生产者做限流(如使用令牌桶),从源头减少消息发送速率费

  3. 在消费者内开启线程池加快消费处理速度

  4. 使用惰性队列:RabbitMQ 3.6+支持惰性队列,消息直接存入磁盘而非内存,消费者消费消息时才会从磁盘中读取并加载到内存。可以避免内存溢出并支持更长的堆积,提高堆积上限。

5. RabbitMQ的高可用机制?(集群)

在生产环境下使用集群来保证高可用性。集群模式:普通集群、镜像集群、仲裁队列集群

(1) **普通集群:**也叫标准集群,具备下列特征:

  1. 会在集群的各个节点间共享部分数据,包括:交换机、队列元信息。不包含队列中的消息。

  2. 当访问集群某节点时,如果队列不在该节点,会从数据所在节点传递到当前节点并返回

  3. 队列所在节点宕机,队列中的消息就会丢失

(2) **镜像集群:**本质上主从模式,具备下列特征:

  1. 交换机、队列、队列中的消息会在各个mq的镜像节点之间同步备份。

  2. 创建队列的节点被称为该队列的主节点,备份到的其它节点叫做该队列的镜像节点。

  3. 一个队列的主节点可能是另一个队列的镜像节点

  4. 所有操作都是主节点完成,然后同步给镜像节点

  5. 主宕机后,镜像节点会替代成新的主节点。

(3) **仲裁队列:**RabbitMQ 3.8以后才有的新功能,用来替代镜像队列,具备以下特征:

  1. 与镜像队列一样,都是主从模式,支持主从数据同步

  2. 使用非常简单,没有复杂的配置

  3. 主从同步基于Raft协议,强一致。

6. Kafka如何保证消息不丢失?

(1) 生产者发送消息到Broker丢失:

  1. 设置异步发送,发送失败使用回调进行记录或重发

  2. 失败重试,参数配置,可以设置重试次数

(2) 消息在Broker中存储丢失:

  1. 发送确认acks,选择all,让所有的副本都参与保存数据后确认。

(3) 消费者从Broker接收消息丢失:

  1. 关闭自动提交偏移量,开启手动提交偏移量

  2. 提交方式,最好是同步+异步提交。

7. Kafka如何保证消息顺序性?

消息顺序性应用场景:

  • 即时消息中的单对单聊天和群聊,保证发送方消息发送顺序与接收方的顺序一致
  • 充值转账两个渠道在同一个时间进行余额变更,短信通知必须要有顺序

(1) 默认情况不能保证消费顺序性,原因:

  1. 一个topic的数据可能存储在不同的分区中,每个分区都有一个按照顺序的存储的偏移量,如果消费者关联了多个分区不能保证顺序性

  2. 失败重试,参数配置,可以设置重试次数

(2) 解决方法:

  1. 发送消息时指定分区号

  2. 发送消息时按照相同的业务设置相同的key。

8. Kafka高可用机制?

从两方面回答:

  • 集群模式
  • 分区备份机制

(1) 集群 :一个kafka集群由多个broker实例组成,即使某一台宕机,也不耽误其他broker继续对外提供服务。

(2) 分区备份机制:

  1. 一个topic有多个分区,每个分区有多个副本,有一个leader,其余都是follower,副本存储在不同的broker中

  2. 所有分区副本的内容都是相同的,如果leader发生故障,会自动将其中一个follower提升为leader,保证系统的容错性、高可用性。

9. Kafka数据清理机制?

先说Kafka是怎么存储的,再说清理:

  • Kafka存储结构
  • 日志的清理策略

(1) Kafka存储结构:

  1. Kafka中topic的数据存储在分区上,分区如果文件过大会分段存储segment

  2. 每个分段都在磁盘上以索引(xxxx.index)和日志文件(xxxx.log)的形式存储

  3. 分段的好处是,第一能够减少单个文件内容的大小,查找数据方便,第二方便kafka进行日志清理

(2) 日志的清理策略:

  1. 根据消息的保留时间,当消息保存的时间超过了指定的时间,就会触发清理,默认是168小时(7天)

  2. 根据topic存储的数据大小,当topic所占的日志文件大小大于一定的阈值,则开始删除最久的消息(默认关闭)。

10. Kafka中实现高性能的设计?

  • 消息分区:不受单台服务器的限制,可以不受限的处理更多的数据
  • 顺序读写:磁盘顺序读写,提升读写效率
  • 页缓存:把磁盘中的数据缓存在到内存中,把对磁盘的访问变为对内存的访问
  • 零拷贝:减少上下文切换及数据拷贝
  • 消息压缩:减少磁盘I/O和网络I/O
  • 分批发送:将消息打包批量发送,减少网络开销

(1) 传统:Linux的I/O模型:四次拷贝

  1. Producer(应用程序)发起读请求,从用户空间调用内核函数。

  2. 内核从磁盘文件读取数据到内核页缓存(DMA拷贝)。

  3. 内核将页缓存中的数据拷贝到用户空间缓冲区(CPU拷贝)。

  4. 应用程序再从用户空间缓冲区拷贝到内核Socket缓冲区(CPU拷贝)。

  5. 内核将 Socket 缓冲区的数据拷贝到网卡(DMA拷贝),发送给消费者。

(2) 零拷贝 :是一种避免CPU在用户态和内核态之间多次拷贝数据的技术。一次拷贝

  1. Producer(应用程序)发起sendfile等系统调用,直接指定内核页缓存中的文件描述符和Socket描述符。

  2. 内核将磁盘文件数据读入内核页缓存(DMA拷贝)。

  3. 内核直接将页缓存中的数据拷贝到Socket缓冲区/网卡,直接发送给消费者。

相关推荐
Devin~Y2 小时前
大厂Java面试实录:Spring Boot/Cloud、Kafka、Redis、K8s 可观测性 + RAG/Agent(小Y翻车版)
java·spring boot·redis·spring cloud·kafka·kubernetes·mybatis
rising start2 小时前
Redis 哨兵模式(Sentinel)
数据库·redis·sentinel
仙俊红2 小时前
spring有多个对象时如何注入
java·后端·spring
小碗羊肉2 小时前
【Redis | 第五篇】分布式锁
数据库·redis·分布式
我有满天星辰2 小时前
Mac 安装 Redis + Spring Boot 整合 Redis(完整实战指南)
spring boot·redis·macos
Java爱好狂.2 小时前
Redis高级笔记:深入浅出Java面试高频考点!
java·数据库·redis·后端·java面试·java程序员·java八股文
rising start2 小时前
深度解析 Redis 主从复制
数据库·redis·主从复制
架构源启2 小时前
Spring AI进阶系列(13)- 安全最佳实践(进阶版):Prompt注入防护、数据泄露预防与合规审计实战
人工智能·安全·spring
REDcker2 小时前
Linux文件IO底层原理详解
linux·运维·spring