- 博客(51)
- 收藏
- 关注
原创 Redis面试题
当客户端访问一个key时检查key是否过期,过期就会删除。如果有大量过期key没有被再次访问,就会浪费大量空间。创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作。首先,对实时性要求非常高的场景不建议使用缓存,使用了缓存就会牺牲一定的实时性。以下方式都是缓解问题。1、2可以加一个更新的分布式锁,防止大量线程在缓存失效时导致数据库压力大。周期性随机抽部分设置了过期时间的key检查是否过期,并删除已过期的key。一般单个key超过10kb被认为是大key,可能会导致。
2025-07-13 23:41:52
360
原创 SSM与SpringBoot面试题
Spring是一个Java生态,SpringFramework是Spring的基础框架包括ioc,aop等。ioc是控制反转,注入方式有构造器注入、setter方法注入、注解注入AOP面向切面编程。核心是动态代理,JDK的动态代理和CGLIB的动态代理。
2025-07-11 19:19:14
301
原创 Java并发编程面试题
如果线程长时间不调用任何 ThreadLocal 相关方法,废弃的 Entry 会一直残留在 ThreadLocalMap 里,value 一直被强引用着,导致内存无法释放。当然使用时可以多new几个ThreadLocal,每次ThreadLocal.set()都是往ThreadLocalMap存值,key是ThreadLocal,value是形参。非公平锁:主要体现在加锁阶段。当线程需要对资源加锁的时候,不会直接加入队列,而是先尝试获取一次锁。公平锁:就是底层维护一个队列,当加锁失败就加入队列排队。
2025-07-05 17:54:05
693
原创 JVM面试题
类加载器在加载类时会先将请求委派给父加载器,一层层向上直到 Bootstrap,如果父加载器无法加载,再由当前加载器尝试。加载 → 连接(验证 → 准备 → 解析) → 初始化 → 使用 → 卸载。有五个阶段(连接也可以算三个阶段。所以可以是七个阶段)初始化(Initialization)验证(Verification)准备(Preparation)解析(Resolution)卸载(Unloading)加载(Loading)
2025-07-03 17:20:40
237
原创 java基础面试题
equals是Object的一个方法,可以重写实现比较属性值判断对象,最好配合重写 hashCode() 方法(用于集合如 HashMap、HashSet)。元素查询:第一次Hash定位到Segment,第二次定位到元素所在链表的head(HashEntry数组存的就是head),接着就遍历链表执行equals。每次循环取出元素,在新的数组重新计算下标。用于引用类型时,比较的是两个对象在内存中的地址是否相同,即是否是同一个对象。数组+链表,通过hash定位到数组下标,再遍历数组对应下标存的链表head。
2025-06-30 18:41:25
823
原创 JDK17的GC调优
也就是在全局标记结束后能够统计出可被回收的垃圾占整堆的比例值,如果超过5%,那么就会触发之后的多轮Mixed GC,如果不超过,那么会在之后的某次Young GC中重新执行全局并发标记。与永久代不同的是,MetaSpace并不使用JVM的内存,而是直接使用本地内存,这意味着Metaspace的大小不再受限于JVM内存大小的限制,而是受操作系统可用内存的限制。这个参数的作用主要是优化代码缓存空间的内存使用。如果没有打开这个选项,那么所有的代码缓存是一个大的内存片段,这不利于内存空间的灵活使用。
2025-06-28 17:49:05
1004
原创 jdk17新特性
在BuildinClassLoader中实现了新的模块化架构下类如何从模块中加载的逻辑,以及模块中资源可访问性的处理。隐藏类不再依赖于类加载器,而是通过读取目标类字节码的方式,创建一个对其他类字节码隐藏的class对象,然后通过反射的方式创建对象,调用方法。而使用模块化机制后,虽然 JDK 也依然在尽力兼容传统的类加载体系,但是,为了兼容模块化机制,JDK 还是对类加载体系做了几个非常明显的调整。GraalVM的这种AOT编译模式,能够极大提升Java程序的执行速度,更贴合现在的微服务,云原生的技术环境。
2025-06-27 04:06:33
908
原创 JVM调优实战及常量池详解
步骤大致如下:因为有"zhuge"这个字面量,所以会先检查字符串常量池中是否存在字符串"zhuge"不存在,先在字符串常量池里创建一个字符串对象;再去内存中创建一个字符串对象"zhuge";存在的话,就直接去堆内存中创建一个字符串对象"zhuge";最后,将内存中的引用返回。
2025-06-26 16:38:05
826
原创 JVM调优工具详解及调优实战
假设是每5分钟一次,那么可以执行命令 jstat -gc pid 300000 10 ,观察每次结果eden,survivor和老年代使用的变化情况,在每次gc后eden区使用一般会大幅减少,survivor和老年代都有可能增长,这些增长的对象就是每次Young GC后存活的对象,同时还可以看出每次Young GC后进去老年代大概多少对象,从而可以推算出老年代对象增长速率。知道了老年代对象的增长速率就可以推算出Full GC的触发频率了,Full GC的每次耗时可以用公式 FGCT/FGC 计算得出。
2025-06-25 14:51:16
669
原创 垃圾收集器G1&ZGC详解
XX:G1HeapWastePercent(默认5%): gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立即停止混合回收,意味着本次混合回收就结束了。不同的是对大对象的处理,G1有专门分配大对象的Region叫Humongous区。
2025-06-24 14:38:05
1002
原创 垃圾收集器ParNew&CMS与底层三色标记算法详解
SATB相对增量更新效率会高(当然SATB可能造成更多的浮动垃圾),因为不需要在重新标记阶段再次深度扫描被删除引用对象,而CMS对增量引用的根对象会做深度扫描,G1因为很多对象都位于不同的region,CMS就一块老年代区域,重新深度扫描对象的话G1的代价会比CMS高,所以G1选择SATB不深度扫描对象,只是简单标记,等到下一轮GC再深度扫描。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
2025-06-21 20:54:20
754
原创 深入理解JVM执行引擎
JVM 执行引擎(Execution Engine)是 Java 虚拟机中最核心的组件之一,它的作用是负责将字节码解释或编译为本地机器指令并执行。
2025-06-20 23:37:01
598
原创 JVM字节码文件结构深度剖析
基本参数类型和void类型都是用一个大写的字符来表示,对象类型是通过一个大写L加全类名表示,这么做的好处就是在保证jvm能读懂class文件的情况下尽量的压缩class文件体积.我们的常量池可以看作我们的java class类的一个资源仓库(比如Java类定的方法和变量信息),我们后面的方法类的信息的描述信息都是通过索引去常量池中获取。常量池入口,占用二个字节,常量池中的第0个位置被我们的jvm占用了表示为null 所以我们通过编译出来的常量池索引是从1开始的。同样占用二个字节,也是表示索引值。
2025-06-20 09:00:00
1980
原创 JVM对象创建与内存分配机制深度剖析
大多数对象生命周期短暂,及时GC回收效率高,Eden区空间一般较大,分配速度快。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC,GC期间虚拟机又发现无法存入Survior空间,所以只好把新生代的对象提前转移到老年代中去,老年代上的空间足够存放,所以不会出现Full GC。当然,如果minor gc之后剩余存活的需要挪动到老年代的对象大小还是大于老年代可用空间,那么也会触发full gc,full gc完之后如果还是没有空间放minor gc之后的存活对象,则也会发生“OOM”
2025-06-19 10:00:00
833
原创 JVM内存模型深度剖析与优化
由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大,对于8G物理内存的机器来说,一般我会将这两个值都设置为256M。如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。
2025-06-18 14:18:25
764
原创 JAVA类加载机制(jdk8)
对class进行MD5、对称加密、非对称加密,类加载器再进行解密。同时把解密类加载器通过另一个加载器内网远程加载,或者u盘加载来实现Class代码混淆加密。
2025-06-17 13:08:18
404
原创 RocketMQ总结
各种防止MQ消息丢失的方案,本质上都是以牺牲系统性能和吞吐量为代价的。这种资源消耗必然会导致集群整体效率的下降。在实际业务场景中,我们需要根据具体需求对这些安全方案进行权衡取舍。生产者发送消息如何保证不丢失同步发送+多次尝试(降低吞吐)异步发送(增加生产者客户端负担)事务消息机制(多次网络请求)Broker写入数据如何保证不丢失同步刷盘(I/O负担)Dledger集群(网络负担)消费者消费消息如何不丢失同步处理消息,再提交offset(无法通过异步提高吞吐)
2025-06-13 22:39:07
1051
原创 MQ常见问题梳理
各种防止MQ消息丢失的方案,本质上都是以牺牲系统性能和吞吐量为代价的。这种资源消耗必然会导致集群整体效率的下降。在实际业务场景中,我们需要根据具体需求对这些安全方案进行权衡取舍。生产者发送消息如何保证不丢失同步发送+多次尝试(降低吞吐)异步发送(增加生产者客户端负担)事务消息机制(多次网络请求)Broker写入数据如何保证不丢失同步刷盘(I/O负担)Dledger集群(网络负担)消费者消费消息如何不丢失同步处理消息,再提交offset(无法通过异步提高吞吐)
2025-06-13 11:00:00
959
原创 RocketMQ集群高级特性
RocketMQ提供的Dledger虽然确实增加了集群的高可用,但是他是把集群选举和同步日志都一起完成的。当有多数节点共同保存了 Entry 后,就可以执行 Entry 中的客户端执行,提交到State Machine 状态机中。在一个Server集群中,数据写入到Server集群中的一个节点,然后希望从集群中任何一个节点都能读到写入的数据,这种问题就称为分布式数据一致性问题。注意,是保证Entry顺序一致,而不是保证Entry不丢失(但是主节点有的从节点也会有,丢失是发生在主节点上的),这是两个概念。
2025-06-12 01:19:54
647
原创 基于RocketMQ源码理解顺序写、刷盘机制与零拷贝
引入DMA拷贝之后,在读写请求的过程中,CPU不再需要参与具体的工作,DMA可以独立完成数据在系统内部的复制。在后期的不断改进过程中,sendfile优化了实现机制,在拷贝过程中,并不直接拷贝文件的内容,而是只拷贝一个带有文件位置和长度等信息的文件描述符FD,这样就大大减少了需要传递的数据。这个过程中有大量的寻址操作,会严重影响写数据的性能。而mmap文件映射的方式,就是在用户态不再保存文件的内容,进程可以像访问普通内存一样,通过指针访问文件内容,只保存文件的映射,包括文件的内存起始地址,文件大小等。
2025-06-11 18:24:28
788
原创 RocketMQ长轮询机制
长轮询机制简单来说,就是当Broker接收到Consumer的Pull请求时,判断如果没有对应的消息,不用直接给Consumer响应(给响应也是个空的,没意义),而是就将这个Pull请求给缓存起来。当Producer发送消息过来时,增加一个步骤去检查是否有对应的已缓存的Pull请求,如果有,就及时将请求从缓存中拉取出来,并将消息通知给Consumer。DefaultPullMessageResultHandler 关键代码。PullRequestHoldService关键代码。
2025-06-11 02:22:10
281
原创 RocketMQ延迟消息机制
对于指定时间点的延迟消息也会转移到系统内置的Topic中。转移到TIMER_TOPIC(rmq_sys_wheel_timer) Topic中,队列ID固定为0。对于固定延迟级别的延迟消息会转移到系统内置的Topic中。转移到SCHEDULE_TOPIC_XXXX Topic中,对列对应延迟级别。Broker在处理消息之前,会注册一系列的钩子,类似于过滤器,对消息做一些预处理。其中就会对延迟消息做处理。HookUtils中有一个方法,就会在Broker处理消息之前对延迟消息做一些特殊处理。
2025-06-10 23:09:56
776
原创 Rocket消息持久化设计
ConsumeQueue文件主要是加速消费者进行消息索引。他的每个文件夹对应RocketMQ中的⼀个MessageQueue,文件夹下的文件记录了每个MessageQueue中的消息在CommitLog文件当中的偏移量。文件结构:每个ConsumeQueue文件固定由30万个固定大小20byte的数据块组成,数据块的内容包括:msgPhyOffset(8byte,消息在文件中的起始位置)+msgSize(4byte,消息在文件中占⽤的长度)+msgTagCode(8byte,消息的tag的Hash值)。
2025-06-10 02:41:04
738
原创 Rocket源码解读(二)
Producer发送消息时,默认会轮询⽬标Topic下的所有MessageQueue,并采⽤递增取模的⽅式往不同MessageQueue上发送消息,以达到让消息平均落在不同的queue上的⽬的。⽽由于MessageQueue是分布在不同的Broker上的,所以消息也会发送到不同的broker上。如果发现往某⼀个Broker上发送消息失败了,那么下⼀次会尽量避免再往同⼀个Broker上发送消息。如果你的。
2025-06-09 09:00:00
1732
原创 RocketMQ核心源码解读(一)
RocketMQ的通信是长连接双向的,使用REQUEST__COMMAND 和 RESPONSE_COMMAND来判断是什么类型的连接,客户端向服务端请求就是REQUEST__COMMAND,服务端接着会发送RESPONSE_COMMAND类型到客户端,客户端做回调(异步操作)可以在生产者示例中,start后打⼀个断点,然后把NameServer停掉,这时,Producer还是可以发送消息的。事务消息的Producer也需要响应Broker的事务状态回查,他也是需要NettyServer的。
2025-06-08 02:14:02
1308
原创 SpringBoot整合RocketMQ与客户端注意事项
默认创建出来的死信队列,他⾥⾯的消息是⽆法读取的,在控制台和消费者中都⽆法读取。这是因为这些默认的死信队列,他们的权限perm被设置成了2:禁读(这个权限有三种 2:禁读,4:禁写,6:可读可写)。但是此时,RocketMQ不会立刻将这个有问题的消息丢弃,而会将其发送到这个消费者组对应的⼀种特殊队列:死信队列。而要处理这个问题,RocketMQ的每条消息都有⼀个唯⼀的MessageId,这个参数在多次投递的过程中是不会改变的,所以业务上可以⽤这个MessageId来作为判断幂等的关键依据。
2025-06-06 22:08:47
1046
原创 Rocket消息机制和ACL权限控制
由于RocketMQ与消费者端有失败重试机制,所以,只要消息成功发送到RocketMQ了,那么可以认为Branch2.1,Branch2.2,Branch2.3这⼏个分⽀步骤,是可以保证最终的数据⼀致性的。对于指定固定延迟级别的延迟消息,RocketMQ的实现⽅式是预设⼀个系统Topic,名字叫做SCHEDULE_TOPIC_XXXXX。通常来说,RocketMQ作为⼀个内部服务,是不需要进⾏权限控制的,但是,如果要通过RocketMQ进⾏跨部⻔甚⾄跨公司的合作,权限控制的重要性就显现出来了。
2025-06-06 01:39:43
678
原创 Rocket客户端消息确认机制
这时,就可以创建另外⼀个新的消费者组,并通过ConsumerFromWhere属性指定这个消费者组的消费起点,从⽽让这个新的消费者组去消费之前发送过的历史消息。⽽这个ConsumerFromWhere属性并不是直接指定Offset的数值,因为客户端也不知道Broker端记录的Offset数值是多少。但是如果消费者没有处理成功,返回的是RECONSUME_LATER,Broker就会过⼀段时间再发起消息重试。⽽消息安全有两⽅⾯的要求,⼀⽅⾯是⽣产者要能确保将消息发送到。这个返回值是⼀个枚举值,有两个选项。
2025-06-05 00:04:43
846
原创 RocketMQ运行架构和消息模型
⽣产者和消费者都可以指定⼀个Topic发送消息或者拉取消息。Topic中的消息会分布在后⾯多个MessageQueue当中。这些MessageQueue会分布到⼀个或者多个broker中。RocketMQ会把主题的消息分为数个MessageQueue(队列先进先出FIFO)RocketMQ的消息模型。
2025-06-04 03:12:09
266
原创 RocketMQ介绍与部署
MQ:MessageQueue,消息队列。异步能提⾼系统的响应速度、吞吐量。解耦:服务之间进⾏解耦,才可以减少服务之间的影响。解耦后可以实现数据分发,⽣产者发送⼀个消息后,可以由⼀个或者多个消费者进⾏消费,并且消费者的增加或者减少对⽣产者没有影响。削峰:以稳定的系统资源应对突发的流量冲击。
2025-06-02 22:38:23
356
原创 Redis7底层数据结构解析
这意味着如果使⽤APPEND之类的指令尝试修改⼀个key的值,那么就算value的⻓度没有超过44,Redis也会使⽤⼀个新创建的raw类型,⽽不再使⽤原来的SDS。
2025-05-31 22:18:09
957
1
原创 京东热点缓存探测系统JDhotkey架构剖析
收集各应用实例上报的信息,使用滑动窗口算法计算 Key 的热度。当 Key 的热度达到设定值时,推送热 Key 信息至所有应用实例。所以计划采用2个HashMap加一个atomicLong,如奇数时写入map0,为1写入map1,上传后会清空该map。为了防止阻塞IKeyCollector使用两个ConcurrentHashMap切换,实现轮流提供读写、暂存key的操作。上报时譬如采用定时器,,监听有新key推送事件,收到来自于worker的新增key。,key的访问次数积攒起来,等待每半秒发送一次。
2025-05-30 22:47:38
797
原创 Redis Stack常见拓展
在 RedisJSON 中,Search and Query(搜索与查询) 是通过 RediSearch 模块 实现的,这也是 Redis Stack 中最强大的功能之一。Cuckoo Filter(布谷鸟过滤器) 是一种替代布隆过滤器(Bloom Filter)的数据结构,它同样用于判断元素是否存在于集合中,但它在一些方面有显著优势。Bloom Filter 是一种高效、概率型的数据结构,用于判断某个元素是否存在于集合中。⼀句话解释:⼀种快速检索⼀个元素是否在⼀个海量集合中的算法。
2025-05-29 23:24:04
520
原创 Redis缓存设计与性能优化
pipeline可以打包不同的命令,原生命令做不到。好处是全局一把锁并且一把锁只锁一个对应需要重建的数据。大量缓存同时失效(或者缓存层支撑不住或者宕机),请求直接打到数据库,造成数据库瞬间崩溃或响应变慢。读多写少的情况加入缓存提高性能,写多读多的情况又不能容忍缓存数据不一致就不要加缓存了。LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降。优化:确定得出数据的时间可以使用trylock避免大量的锁逻辑。数据过期的时候一下子涌入大量的请求,在缓存中不存在。高并发下添加熔断功能。
2025-05-28 23:21:19
968
原创 redis高并发问题
CAP 冲突,指的是分布式系统中无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)这三个特性,最多只能同时满足其中两个。好处是全局一把锁并且一把锁只锁一个对应需要重建的数据。更新缓存的线程在查到数据以后卡顿或者cpu调度被其他线程先更新了数据库,这时候更新缓存的线程把之前读到的旧值写入缓存。假设我们有 5 个 Redis 实例:R1、R2、R3、R4、R5。优化:确定得出数据的时间可以使用trylock避免大量的锁逻辑。
2025-05-27 23:38:16
336
原创 Redisson分布式锁原理
lua返回锁的ttl,想获取锁的线程会先尝试一次(非公平锁),然后在while循环中阻塞ttl秒的时间,继续获取锁。默认锁过期时间是 30 秒(如未指定),如果业务还未完成,Redisson 内部有个后台线程定期(锁过期 / 3)刷新 TTL,只有加锁线程还持有锁,续期才会生效。如果是同一线程重复加锁,Redisson 使用hash结构记录每个线程(uuid+线程线程id)加锁次数,实现可重入锁。调用 tryLock(),底层执行的是一段lua脚本,相当于setnx但是使用hash结构存储数据和线程信息。
2025-05-26 23:37:43
431
1
原创 Redis数据安全分析
由于Redis集群的gossip协议在同步元数据时不保证强⼀致性,这意味着在特定的条件下Redis集群可能会丢掉⼀些被系统收到的写⼊请求命令 ,这些特定条件通常都⽐较苛刻,概率⽐较⼩。集群会检查每个槽位是否有对应的节点负责如果负责⼀部分槽位的⼀组复制节点(主节点和从节点都挂)都挂了,默认情况下Redis集群就会停⽌服务。虽然禁⽌了对数据的写操作,但是并没有禁⽌CONFIG、DEBUG等管理指令,这些指令如果和主节点不⼀致,还是容易造成数据不⼀致。默认情况下,从库是只读的,不允许写⼊数据。
2025-05-25 21:47:18
709
原创 深入理解Redis线程模型
以上介绍的各种机制,其实都是Redis改变指令执⾏顺序的⽅式。在这⼏种⼯具中,Lua脚本通常会是项⽬中⽤得最多的⽅式。在很多追求极致性能的⾼并发场景,Lua脚本都会担任很重要的⻆⾊。Redis的线程模型整体还是多线程的,只是后台执⾏指令的核⼼线程是单线程的。整个线程模型可以理解为还是以单线程为主。基于这种单线程为主的线程模型,不同客户端的各种指令都需要依次排队执⾏。
2025-05-24 21:58:07
1513
原创 redis数据类型
最基本的数据类型,二进制安全,可以存储任何类型的值,如数字、字符串、JSON 等。一种概率性数据结构,用于统计基数(不重复元素数量)。应用场景:UV(独立访客)统计,误差率 0.81%类似消息队列的结构,适用于日志收集、实时数据处理。基于位操作的数据结构,可高效地进行统计和标记。应用场景:排行榜、延时队列、按权重排序的数据。应用场景:活跃用户统计、签到功能、状态标记。应用场景:消息系统、实时数据分析、事件追踪。应用场景:缓存、计数器、共享 token。应用场景:存储对象,如用户信息。数值操作(自增/自减)
2025-05-23 22:51:49
780
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人