面试整理
GC触发
线程通信
CAS如何解决ABA问题
大文件上传,如何分片、续传、快传
oom如何排查
内存溢出如何排查
lock锁的实现原理
lock锁如何实现公平和非公平
注册中心原理
有哪些负载均衡策略
微服务如何拆分
生产实例事故如何处理?
场景
接口幂等如何设计?
幂等分为前端幂等和后端幂等,前端幂等主要防止表单重复提交,可以采用token验证的方式,后端幂等主要是后端微服务之间重复调用比如消息重复消费等,可以使用状态机或消息唯一id
1.使用token:前台在表单提交时,向服务器获取一个唯一token,发起提交时将表单数据和token一起传递,后端使用redis进行判断(setnx命令),是否重复
2.业务状态机:定义清晰的业务状态流转(如"未领取一已领取一已使用一已过期"),每次操作前校验状态(如"已领取"状态不可重复领取)
库存超买超卖问题?
1.数据库层面:使用悲观锁(使用数据库的行级锁机制,例如 SELECT ... FOR UPDATE,确保在同一时间只有一个事务可以修改库存)或乐观锁(使用版本号或库存数据来实现。每次更新库存时,检查版本号是否一致,如果不一致则重试)
2.分布式锁:使用Redis、Zookeeper等分布式锁服务来保证同一时间只有一个请求能够修改库存
3.使用消息队列:将用户的购买请求放入消息队列(如RabbitMQ、Kafka),然后由专门的消费者进程按顺序处理这些请求,确保库存操作的一致性
4.缓存库存数据:使用缓存(如Redis)来存储库存数据,并在缓存中进行扣减操作。如果缓存中的库存不足,再回写到数据库中
扫码登录实现?
JVM
https://blog.csdn.net/weixin_45081813/article/details/115312135
JVM运行时数据区域
堆、方法区(元空间)、虚拟机栈、本地方法栈、程序计数器。
- Heap(堆):对象的实例以及数组的内存都是要在堆上进行分配的,堆是线程共享的一块区域,用来存放对象实例,也是垃圾回收(GC)的主要区域;开启逃逸分析后,某些未逃逸的对象可以通过标量替换的方式在栈中分配。堆细分:新生代、老年代,对于新生代又分为:Eden区和Surviver1和Surviver2区。
- 方法区:对于JVM的方法区也可以称之为永久区,它储存的是已经被java虚拟机加载的类信息、常量、静态变量;Jdk1.8以后取消了方法区这个概念,称之为元空间(MetaSpace);当应用中的 Java 类过多时,比如 Spring 等一些使用动态代理的框架生成了很多类,如果占用空间超出了我们的设定值,就会发生元空间溢出。
- 虚拟机栈:虚拟机栈是线程私有的,他的生命周期和线程的生命周期是一致的。里面装的是一个一个的栈帧,每一个方法在执行的时候都会创建一个栈帧,栈帧中用来存放(局部变量表、操作数栈 、动态链接 、返回地址);在Java虚拟机规范中,对此区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将会抛出StackOverflowError异常;如果虚拟机栈动态扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
- 本地方法栈:本地方法栈和虚拟机栈类似,不同的是虚拟机栈服务的是Java方法,而本地方法栈服务的是Native方法。在HotSpot虚拟机实现中是把本地方法栈和虚拟机栈合二为一的,同理它也会抛出StackOverflowError和OOM异常。
- PC程序计数器:PC,指的是存放下一条指令的位置的一个指针。它是一块较小的内存空间,且是线程私有的。由于线程的切换,CPU在执行的过程中,需要记住原线程的下一条指令的位置,所以每一个线程都需要有自己的PC。
什么是Java内存模型
JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本,本地内存是 JMM 的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化

什么是双亲委派模型


类装载过程有哪些?

Java对象组成
Java对象由对象头、实例数据和对齐填充组成。对象头存储了一些元数据,如哈希码、锁状态和指向类元数据的指针。实例数据包含对象的成员变量值。对齐填充用于确保对象在内存中的对齐。
Java引用类型有哪些?
强引用(Strong Reference):
强引用是最常见的引用类型。当我们通过 new 关键字创建一个对象时,就会使用强引用来引用该对象。只要强引用存在,垃圾回收器就不会回收被引用的对象。
软引用(Soft Reference):
软引用用于描述一些还有用但非必需的对象。在内存不足时,垃圾回收器会根据软引用的情况决定是否回收这些对象。可以通过 SoftReference 类来创建软引用。
弱引用(Weak Reference):
弱引用也用于描述非必需对象,但是弱引用比软引用更脆弱。只要垃圾回收器运行,就有可能回收被弱引用关联的对象。可以通过 WeakReference 类来创建弱引用。
虚引用(Phantom Reference):
虚引用是最弱的引用类型,几乎没有实际作用。它主要用于跟踪对象被垃圾回收的状态。虚引用和引用队列(ReferenceQueue)一起使用,通过 PhantomReference 类来创建。
内存溢出排查思路?

CPU飙高的解决思路?

计算机网络
三次握手
四次挥手
浏览器输入url发生什么

消息队列
有没有遇到过消息堆积,如何解决?
现象:有次使用低版本HttpClent有bug,设置了超时时间没生效,导致消费者线程卡死,从而引发mq消费偏移量不前进,最终引发消息堆积
排查:先找到哪个topic有问题,看消费者的线程和日志状态,如果 线程状态在变化,日志有响应,那就是消费慢;如果消费者不动了,那就是消费者卡住了
解决:1.增加消费者实例;2.增加临时消费者,临时消费者只做插入操作将消息保存到数据库,保证消息不丢失 3.调整队列大小来允许更多消息存储 4.对业务进行降级,最低限度让系统正常运行,比如减少发送方发送的数据量,减轻消费者业务逻辑
如何防止重复消费?
1.每条消息都会带有唯一ID,那么我们可以通过这个id来进行判断是否处理过,使用数据库唯一约束保存(也可以利用Redis 的 SETNX 命令来替代关系型数据库,如果存在,直接返回,不存在再执行业务逻辑) 2.使用乐观锁(发送消息的时候带上版本号,判断版本号是否一致,更新完数据后版本号+1)3.使用数据库存储消费记录和状态
如何保证消息有序消费?
Java基础
Java中对象的存储位置
1.java中对象的存储位置
String aa = new String();
new创建的对象存储在堆内存中;
aa这个局部变量存储在栈内存中;
2.Java中常量的存储位置
常量存放在常量池中,而常量池在堆内存中
3.Java中局部变量的存储位置
局部变量存放在栈内存中
4.Java中全局变量和Static常量的存储位置
存放在全局数据区内存中
5.java中static修饰的成员变量及参数存放位置
存放在方法区
arraylist扩容
ArrayList是基于数组的集合,数组的容量是在定义的时候确定的,如果数组满了,再插入,就会数组溢出。所以在插入时候,会先检查是否需要扩容,如果当前容量+1超过数组长度,就会进行扩容。ArrayList的扩容是创建一个1.5倍的新数组,然后把原数组的值拷贝过去。
有哪几种实现ArrayList线程安全的方法?
- 使用 Vector 代替 ArrayList。(不推荐,Vector是一个历史遗留类)
- 使用 Collections.synchronizedList 包装 ArrayList,然后操作包装后的 list。
- 使用 CopyOnWriteArrayList 代替 ArrayList。
- 用 ArrayList 时,应用程序通过同步机制去控制 ArrayList 的读写。
CopyOnWriteArrayList了解多少?
CopyOnWriteArrayList就是线程安全版本的ArrayList。CopyOnWriteArrayList采用了一种读写分离的并发策略。CopyOnWriteArrayList容器允许并发读,读操作是无锁的,性能较高。至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。

arraylist、linkedarraylist区别
集合遍历时可以修改嘛?
遍历集合时进行增加或删除操作可能会导致ConcurrentModificationException异常。如果确实需要这样操作,应使用迭代器的相关方法来进行修改hashmap原理?
数据结构是数组+链表+红黑树
数据元素通过映射关系,也就是散列函数,映射到桶数组对应索引的位置如果发生冲突,从冲突的位置拉一个链表,插入冲突的元素如果链表长度>8 & 数组大小>=64,链表转为红黑树如果红黑树节点个数<6 ,转为链表
hashmap put操作过程
1.首先,计算要插入的键的哈希值,并找到它在哈希表中的桶位置。如果该桶为空,则直接将键值对放入桶中,完成插入操作。
2.如果该桶不为空,则需要遍历该桶的链表以查找是否已经存在相同的键。如果存在相同的键,则更新其对应的值,完成插入操作。
3.如果不存在相同的键,则将键值对添加到链表的尾部(或红黑树中,如果链表长度超过阈值)。如果此时链表长度超过了阈值,则将链表转换为红黑树。
4.如果添加操作导致哈希表中键值对数量超过了阈值(即负载因子乘以哈希表大小),则进行扩容操作。

hashmap 什么时候需要扩容
在首次调用put方法的时候,初始化数组table
当HashMap中的元素个数超过数组大小(数组长度)*loadFactor(负载因子)时,就会进行数组扩容,loadFactor的默认值(DEFAULT_LOAD_FACTOR)是0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中的元素个数超过16×0.75=12(这个值就是阈值或者边界值threshold值)的时候,就把数组的大小扩展为2×16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预知元素的个数能够有效的提高HashMap的性能。
当HashMap中的其中一个链表的对象个数如果达到了8个,此时如果数组长度没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链表会变成红黑树,节点类型由Node变成TreeNode类型。当然,如果映射关系被移除后,下次执行resize方法时判断树的节点个数低于6,也会再把树转换为链表。
hashmap 扩容原理
①:先生成新数组,新的容量是当前容量的两倍
②:遍历老数组中的每个位置上的链表或红黑树;
③:如果是链表,则直接将链表中的每个元素重新计算下标,并添加到新数组中去;
④:如果是红黑树,则先遍历红黑树,先计算出红黑树中每个元素对应在新数组中的下标位置;
a:统计每个下标位置的元素个数;
b:如果该位置下的元素个数超过了8,则生成一个新的红黑树,并将根节点添加到新数组的对应位置;
c:如果该位置下的元素个数没有超过8,那么则生成一个链表,并将链表的头节点添加到新数组的对应位置;
⑤:所有元素转移完了之后,将新数组赋值给HashMap对象的table属性。
hashmap和hashtable区别?和hashset区别?
线程安全性:Hashtable 是线程安全的,而 HashMap 不是。Hashtable 的操作方法都使用 synchronized 关键字进行同步,可以保证在多线程环境下的线程安全性。而 HashMap 在多线程环境中需要外部同步措施来保证线程安全。
允许键值对为 null:HashMap 允许键和值都为 null,而 Hashtable 不允许,如果尝试存储 null 键或 null 值,将会抛出 NullPointerException。
迭代器:HashMap 的迭代器是快速失败的(fail-fast),即在迭代过程中,如果其他线程对 HashMap 进行修改,会抛出 ConcurrentModificationException 异常。而 Hashtable 的迭代器则不是快速失败的,不会抛出异常。
初始容量和增长方式:HashMap 的初始容量和增长方式是可调的,而 Hashtable 的初始容量是固定的(11),增长方式是当前容量的两倍加一。
继承关系:HashMap 继承自 AbstractMap 类,而 Hashtable 继承自 Dictionary 类。
HashSet 是基于 HashMap 实现的,它是一种集合(Set)的实现,与 HashMap 不同的是,HashSet 只存储元素,不存储键值对。HashSet 中的元素是唯一的,不允许重复。与 HashMap 相比,HashSet 的操作方式和特性类似,只是不需要存储键值对而已。
ConcurrentHashmap的实现
其实可以看出JDK1.8版本的ConcurrentHashMap的数据结构已经接近HashMap,相对而言,ConcurrentHashMap只是增加了同步的操作来控制并发,从JDK1.7版本的ReentrantLock+Segment+HashEntry,到JDK1.8版本中synchronized+CAS+HashEntry+红黑树。
1.数据结构:取消了Segment分段锁的数据结构,取而代之的是数组+链表+红黑树的结构。
2.保证线程安全机制:JDK1.7采用segment的分段锁机制实现线程安全,其中segment继承自ReentrantLock。JDK1.8采用CAS+Synchronized保证线程安全。
3.锁的粒度:原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)。
4.链表转化为红黑树:定位结点的hash算法简化会带来弊端,Hash冲突加剧,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。
5.查询时间复杂度:从原来的遍历链表O(n),变成遍历红黑树O(logN)。

多线程
start和run的区别
都是Thread类的方法,start表示启动一个线程,run方法表示线程启动以后要执行的代码
runable和callable区别

java线程状态有哪些
6种状态
1.new创建态:当用new关键字创建一个线程时,还没调用start时
2.runnable可运行态:程序无法判新某个时间到底是就绪还是运行态,所以这2个状态对程序就没有意义
3.waiting等待态:处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态
4.timed_waiting超时等待态:处于这种状态的线程不会被分配CPU执行时间,不过无需无期限等待被其他线程显式地唤醒,在达到一定时间后它们会自动唤醒
5.bolcked阻塞态:线程在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态
6.terminated销毁终止态:
当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生
在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常
Sleep、Wait区别
1、 sleep 来自 Thread 类,和 wait 来自 Object 类。
2、最主要是sleep方法没有释放锁,而wait方法释放了
锁,使得其他线程可以使用同步控制块或者方法。
3、wait,notify和 notifyAll 只能在同步控制方法或者同步控制块里面使用,而 sleep 可以在任何地方使用(使
用范围)
4、 sleep 必须捕获异常,而 wait , notify 和 notifyAll 不需要捕获异常
wait¬ify和Park&Unpark区别
wait和notify相比必须配合Object Monitor一起使用,而park,unpark则不用
park&unpark是以线程为单位来阻塞和唤醒线程的,而notify只能随机唤醒一个,notifyAll只能唤醒所有,不怎么精确
park&unpark是可以先unpark的类似于先打个预防针,而wait¬ify则不能
synchronized底层原理

synchronized锁升级过程

wait() 方法会导致当前线程释放锁并进入等待状态,其他线程可以获取该锁。当等待的线程被唤醒后,它需要重新获取锁才能继续执行。轻量级锁不支持这种复杂的锁释放和重新获取操作,因为它们依赖于简单的 CAS 操作。为了确保线程安全和正确的锁状态管理,JVM 直接将偏向锁升级为重量级锁,以利用重量级锁提供的更强大的同步机制。
synchronized和volatile区别
synchronized和Lock有什么区别?

什么是AQS
Java的AQS(AbstractQueuedSynchronizer)是一个抽象类,位于java.util.concurrent.locks包中。AQS是构建锁和其他同步组件的基础框架,它提供了一个FIFO队列,用于管理线程的等待和唤醒。许多并发工具如ReentrantLock、Semaphore、CountDownLatch等都是基于AQS实现的。
FIFO队列:AQS使用一个FIFO队列来管理等待线程。当线程尝试获取锁但失败时,会被放入队列中等待。
状态管理:AQS通过一个int类型的state变量来表示同步状态。子类可以通过继承AQS并定义自己的逻辑来管理这个状态。
死锁如何产生,如何排查?

并行并发区别
并行:在同一时间段,多个请求访问不同资源,同时执行,互不影响,并行强调的是同一时间执行。
并发:在同一时间,多个请求同时访问同一个资源,并发强调是访问同一资源。
线程池参数

线程池常见阻塞队列
LinkedBlockingQueue:这是一个基于链表实现的无界阻塞队列。当线程池的任务队列满时,新的任务将会被阻塞,直到队列有空闲位置。
ArrayBlockingQueue:这是一个基于数组实现的有界阻塞队列。在创建时需要指定队列的容量,当队列满时,新的任务将会被阻塞,直到队列有空闲位置。
SynchronousQueue:这是一个没有容量的阻塞队列,每个插入操作必须等待一个相应的删除操作,反之亦然。常用于线程池中的任务直接交付给消费者线程处理的场景。
PriorityBlockingQueue:这是一个支持优先级排序的无界阻塞队列。元素按照优先级进行排序,优先级高的元素先被取出。

线程池拒绝策略有哪些
ThreadPoolExecutor.AbortPolicy(默认):当线程池无法处理新的任务时,会抛出RejectedExecutionException异常,表示拒绝执行该任务。
ThreadPoolExecutor.CallerRunsPolicy:当线程池无法处理新的任务时,新的任务会由调用线程来执行。也就是说,调用线程本身会参与到任务的执行中。
ThreadPoolExecutor.DiscardPolicy:当线程池无法处理新的任务时,会静默地丢弃该任务,不会抛出任何异常或警告。
ThreadPoolExecutor.DiscardOldestPolicy:当线程池无法处理新的任务时,会丢弃最早被添加到队列中但尚未被执行的任务,然后尝试重新提交当前任务。
此外,我们还可以根据具体业务需求自定义拒绝策略,通过实现RejectedExecutionHandler接口来定义自己的拒绝策略
为什么不建议使用Executors

ThreadLocal如何做到线程隔离?内存泄漏?

CAS是什么,实现原理
CAS(Compare and Swap)是一种并发算法,用于实现多线程环境下的原子操作。在Java中,CAS主要通过java.util.concurrent.atomic包中的原子类来实现,如AtomicInteger、AtomicLong等。
CAS操作涉及到三个操作数:内存地址(或称为变量的引用)、期望值和新值。它的基本思想是,先比较内存地址处的值与期望值是否相等,如果相等,则将内存地址处的值更新为新值;否则,不做任何操作。整个操作是原子性的。
总结来说,CAS通过使用底层的硬件支持和原子指令,实现了多线程环境下的原子操作。它可以避免传统的锁机制所带来的性能开销,并提供了一种高效的并发编程方式。在Java中,CAS主要通过Atomic类来提供原子操作的支持,使得开发者可以方便地进行线程安全的操作。
CAS导致的问题
[cas-算法存在哪些问题](https://javaguide.cn/java/concurrent/cas.html#cas-%E7%AE%97%E6%B3%95%E5%AD%98%E5%9C%A8%E5%93%AA%E4%BA%9B%E9%97%AE%E9%A2%98 "cas-算法存在哪些问题")
谈谈对volatile的理解?

分布式
什么是服务熔断和服务降级?
服务熔断:在分布式系统中,当某个服务出现故障或不可用时,为了避免故障扩散,可以通过服务熔断来防止对该服务的继续调用,并提供一个备用的响应或错误信息。
服务降级:在分布式系统中,当系统出现高峰期或故障时,为了保证核心功能的可用性,可以通过服务降级来减少对不重要或可选功能的调用,从而释放资源以应对高负载或故障情况。
Spring Cloud 中的服务调用方式有哪些?
Spring Cloud 提供了多种服务调用的方式,包括使用 RestTemplate、Feign、Ribbon、OpenFeign 等。其中,RestTemplate 是 Spring 提供的 HTTP 客户端工具,用于发送 HTTP 请求;Feign 和 OpenFeign 是声明式的 HTTP 客户端,可以通过注解方式进行声明,简化服务调用的过程;Ribbon 是负责客户端负载均衡的组件,可以让服务消费者轮流请求多个服务提供者。
组成部分有哪些?

分布式事务中seata提供哪些解决方案?
AT(Auto-Commit)模式:AT 模式是 Seata 默认的事务模式。在 AT 模式下,Seata 通过对业务代码的透明封装,将分布式事务的提交和回滚操作自动化。当业务代码执行完毕后,Seata 会根据事务上下文中收集到的分支事务状态信息,自动决定是否提交或回滚整个分布式事务。
TCC(Try-Confirm-Cancel)模式:TCC 模式是一种更为细粒度的补偿机制,适用于对数据一致性要求较高的场景。在 TCC 模式下,开发者需要显式地定义 Try、Confirm 和 Cancel 三个阶段的业务逻辑。Seata 通过调用各个服务的 Try、Confirm 和 Cancel 接口来协调分布式事务的提交和回滚。
Saga 模式:Saga 模式是一种长流程补偿模式,适用于分布式事务涉及多个阶段且每个阶段有自己的补偿逻辑的场景。Saga 模式将分布式事务拆分为多个阶段,并为每个阶段定义补偿逻辑。每个阶段都是一个本地事务,Seata 通过协调每个阶段的执行和补偿,最终达到整体事务的一致性。
XA 模式:XA 模式基于全局事务管理协议 X/Open XA 规范,支持使用分布式事务协调器(DTC)来实现分布式事务。Seata 提供了对 XA 模式的支持,可以与支持 XA 协议的数据库、消息中间件等进行集成,实现分布式事务的提交和回滚。
什么是服务熔断、降级?

微服务监控用的是什么?

Sentinel和Hystix的线程隔离区别

Sentinel 的线程隔离就是基于信号量隔离实现的,而 Hystix 两种都支持,但默认是基于线程池隔离
GC
有哪些存活算法
引用计数法:给对象添加一个引用计数器,每当由一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。优点:实现简单,判定效率也很高缺点:他很难解决对象之间相互循环引用的问题,基本上被抛弃
可达性分析法:通过一系列的成为“GC Roots”(活动线程相关的各种引用,虚拟机栈帧引用,静态变量引用,JNI引用)的对象作为起始点,从这些节点ReferenceChains开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC ROOTS没有任何引用链相连时,则证明此对象时不可用的;
两次标记过程:对象被回收之前,该对象的finalize()方法会被调用;两次标记,即第一次标记不在“关系网”中的对象。第二次的话就要先判断该对象有没有实现finalize()方法了,如果没有实现就直接判断该对象可回收;如果实现了就会先放在一个队列中,并由虚拟机建立的一个低优先级的线程去执行它,随后就会进行第二次的小规模标记,在这次被标记的对象就会真正的被回收了。
Java中的垃圾回收算法有哪些?
1.标记-清除算法(Mark and Sweep):
首先通过根节点(如活动线程的栈、静态变量等)标记出所有存活的对象。
然后对堆中未被标记的对象进行清除,将其空间回收。
该算法容易产生碎片,并且清除和标记操作的效率相对较低。
2.复制算法(Copying):
将堆内存划分为两个相等大小的区域,每次只使用其中一个区域。
当当前区域的内存用尽时,将存活的对象复制到另一个区域,然后清空当前区域。
该算法避免了碎片问题,但需要额外的内存空间。
3.标记-整理算法(Mark and Compact):
类似于标记-清除算法,首先标记所有存活的对象。
接着将存活的对象往一端移动,然后清理边界之外的内存。
该算法解决了标记-清除算法的碎片问题,但仍然存在标记和整理操作的开销。
4.分代算法(Generational):
基于分代假设,大部分对象的生命周期较短。
将堆内存划分为不同的代,如新生代(Young Generation)和老年代(Old Generation)。
使用不同的GC算法对不同代应用适当的回收策略,例如新生代采用复制算法,老年代采用标记-整理算法。
通过不同的分代策略和回收算法,可以提高垃圾回收的效率。
MinorGC、MajorGC、FullGC


Java中的垃圾收集器有哪些?



如何手动触发垃圾回收?
在Java中,可以使用System.gc()方法手动触发垃圾回收。但是,调用该方法并不一定会立即执行垃圾回收Redis
Redis的数据类型

Redis常用命令?
SET:设置指定键的值。
GET:获取指定键的值。
DEL:删除指定键。
EXISTS:检查键是否存在。
INCR:将指定键的值加1。
DECR:将指定键的值减1。
LPUSH:在列表的左侧添加一个或多个元素。
RPUSH:在列表的右侧添加一个或多个元素。
SADD:向集合中添加一个或多个元素。
ZADD:向有序集合中添加一个或多个元素。
以上命令只是Redis的一小部分常用命令,Redis还有很多其他强大的命令可以用于不同的操作和场景。
Redis的持久化机制
Redis的持久化机制有两种:RDB(Redis Database)和AOF(Append-Only File)。
RDB持久化:将Redis在某个时间点上的数据保存到磁盘上的一个二进制文件。它的优点是文件紧凑、恢复速度快,适用于大规模数据的备份和恢复。但是缺点是如果Redis意外宕机,会丢失最后一次持久化之后的数据。
AOF持久化:将Redis的每个写操作追加到一个文件中,通过重新执行这些写操作来恢复数据。它的优点是数据更可靠,即使Redis宕机也能保证较小粒度的数据恢复。但是缺点是AOF文件可能会变得很大,恢复速度相对较慢。
可以根据需求选择使用RDB持久化、AOF持久化或两者结合来保证数据的持久化和可靠性。
Redis 集群有哪些方式?
缓存穿透、击穿、雪崩?



Redis数据不一致问题

大Key如何存储

Redis 淘汰策略
> 1.noeviction(默认策略):内存满时拒绝写入新数据,返回错误,但允许读取操作。 淘汰易失数据(仅含过期时间的键)。 2.volatile-lru:淘汰最近最少使用的键。 3.volatile-lfu:淘汰访问频率最低的键(Redis 4.0+)。 4.volatile-ttl:淘汰剩余存活时间最短的键。 5.volatile-random:随机淘汰键。 全局淘汰(包含所有键)。 6.allkeys-lru:全局LRU淘汰。 7.allkeys-lfu:全局LFU淘汰(Redis 4.0+)。 8.allkeys-random:全局随机淘汰过期策略
- 定时删除
在设置键值过期时间时,创建一个定时事件,当过期时间到达时,由事件处理器自动执行键的删除操作 - 惰性删除
不主动删除过期键,每次从数据库获取键值时判断是否过期,如果过期则删除键值,并返回 null - 定期删除
每隔一段时间检查一次数据库,随机删除一些过期键,配置hz 10• 优点:通过限制删除操作的时长和频率,来减少操作对Redis主业务的影响,同时也能删除一部分过期的数据• 缺点:内存清理方面没有定时删除效果好,同时没有惰性删除使用的系统资源少
总结:Redis 使用的是惰性删除加定期删除的过期策略
Spring
https://mp.weixin.qq.com/s/21YeQ5h85gXoa2kJg3nSgw
Bean生命周期?

@Component 和 @Bean 的区别是什么?

@Configuration 和 @Component 的区别?

Spring 的循环依赖
AOP 的了解?
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理
Spring AOP 通知类型

多个切面的执行顺序如何控制
1、通常使用@Order 注解直接定义切面顺序
2.实现Ordered 接口重写 getOrder 方法
Spring事务是如何实现的?

Spring事务失效的场景?

Spring事务传播机制
Spring事务失效场景

Spring mvc流程


@Autowired注解@Resource区别
@Autowired注解:
1.来自于Spring框架,是Spring提供的注入方式之一。
2.默认按类型(byType)进行注入,在容器中查找匹配的Bean,如果找到多个匹配的Bean,则根据变量名进行匹配。
3.可以用在构造方法、字段、Setter方法或者任何方法上。
4.不强制要求依赖项必须存在,如果不存在对应的Bean,会抛出异常。可以通过设置@Autowired注解的required属性为false来避免抛出异常。
@Resource注解:
1.是JavaEE规范的一部分,提供了依赖注入的功能。
2.默认按名称(byName)进行注入,先按照名称在容器中查找匹配的Bean,如果找不到,则按类型进行匹配。
3.可以用在字段、Setter方法或者任何方法上,但不支持构造方法注入。
4.强制要求依赖项必须存在,如果找不到对应的Bean,会抛出异常。
@SpringBootApplication 作用?
把bean注册到spring ioc容器。
@SpringBootApplication就只干了一件事通过3种方式来实现:
- @SpringBootConfiguration 通过@Configuration 与@Bean结合,注册到Spring ioc 容器。
- @ComponentScan 通过范围扫描的方式,扫描特定注解类,将其注册到Spring ioc 容器。
- @EnableAutoConfiguration 通过spring.factories的配置,来实现bean的注册到Spring ioc 容器。
自动装配原理

SpringBootApplication原理
@SpringBootApplication是一个组合注解,它包含了以下三个注解:
@Configuration:表明该类是一个配置类,定义了一些Bean的创建和注册。
@EnableAutoConfiguration:开启自动配置功能,根据类路径中的依赖和配置,自动配置Spring Boot的特性。
@ComponentScan:指定要扫描的包路径,让Spring能够发现并注册Bean。
当启动Spring Boot应用程序时,会扫描主类所在的包及其子包,找到所有使用@Component及其衍生注解(如@Controller、@Service等)的类,并将其实例化为Bean。
使用@EnableAutoConfiguration注解时,Spring Boot会根据约定和条件自动配置应用程序的各项特性,如数据库连接、Web MVC、消息队列等。它会根据类路径中的依赖情况,自动装配相应的Bean。
@Configuration注解表示这是一个配置类,可以使用@Bean注解来定义和注册Bean。在Spring Boot应用程序中,通常会创建一些配置类来定义必要的Bean,以及进行一些自定义配置。
@ComponentScan注解会扫描指定的包路径,将使用了@Component及其衍生注解的类作为Bean注册到Spring容器中。这样就可以在应用程序中使用@Autowired等注解将Bean注入到其他组件中。
Mybatis
mybatis执行流程
配置文件加载:MyBatis 首先加载配置文件(通常是 XML 格式),包括数据源配置、映射文件配置、全局设置等。SqlSessionFactory 构建:基于加载的配置文件,创建 SqlSessionFactory 对象,它是 MyBatis 中最重要的对象之一,负责创建 SqlSession 对象。
SqlSession 创建:通过 SqlSessionFactory 创建 SqlSession 对象,SqlSession 提供了对数据库进行操作的方法。开发人员在需要与数据库交互时,通过 SqlSession 执行 SQL 语句。
Mapper 接口绑定:将 Mapper 接口与对应的映射文件进行绑定,使得调用 Mapper 接口方法时可以执行对应的 SQL 语句。
SQL 执行:通过 Mapper 接口方法调用执行 SQL 语句。MyBatis 会根据方法名和参数类型,找到对应的映射文件,并执行其中定义的 SQL 语句。
参数处理:将传入的参数与 SQL 语句中的占位符进行映射,生成完整的 SQL 语句。
SQL 执行:将生成的完整 SQL 语句发送给数据库,并执行。数据库返回结果。
结果映射:将数据库返回的结果映射为 Java 对象,可以是单个对象、对象列表或者嵌套对象等。
延时加载如何开启,原理是什么?

一级二级缓存作用域?如何开启?何时清理

MyBatis 的工作原理是什么?
MyBatis 的核心组件是 SqlSessionFactory,它负责创建 SqlSession 对象。SqlSession 提供了操作数据库的方法,并且通过 Mapper 接口与对应的映射文件进行绑定。在执行 SQL 时,MyBatis 会将 SQL 语句和参数进行处理,并将结果映射为 Java 对象。MyBatis 的参数映射是如何工作的?
MyBatis 支持多种参数映射方式,包括基本类型、Map、Java 对象等。在 SQL 语句中,可以使用占位符(如 #{param})来引用参数。MyBatis 会根据参数类型和占位符名称,将参数映射到 SQL 语句中。什么是 MyBatis 的一级缓存和二级缓存?它们有什么区别?
一级缓存:一级缓存是当前 SqlSession 内部的缓存。当同一个 SqlSession 多次查询同一条记录时,第一次查询会将结果放入缓存中,后续查询直接从缓存中获取结果。一级缓存的作用域是 SqlSession,它的生命周期较短。
二级缓存:二级缓存是跨 SqlSession 的缓存。当多个 SqlSession 查询同一条记录时,第一次查询会将结果放入缓存中,后续 SqlSession 查询直接从缓存中获取结果。二级缓存的作用域是 Mapper 接口,它的生命周期较长。
动态 SQL 在 MyBatis 中如何使用?
动态 SQL 是 MyBatis 中强大的特性之一,可以根据不同的条件来动态生成 SQL 语句。MyBatis 提供了MyBatis 中的 resultMap 是什么?如何定义和使用它?
resultMap 是 MyBatis 中用于结果映射的配置元素。它定义了数据库列和 Java 对象属性之间的映射关系。可以使用MyBatis 如何开启日志?
配置日志级别。在application.properties或application.yml中设置日志级别。例如,可以将MyBatis的日志级别设置为DEBUG,以打印更详细的日志信息:
在application.properties中的配置:
logging.level.org.mybatis=DEBUG
MyBatis 如何配置多数据源?
首先 在application.properties或application.yml文件中配置多个数据源的连接信息和属性,包括URL、用户名、密码和驱动程序等。
接下来,在配置类中创建多个DataSource实例,并将其注入到DataSource类型的Bean中。可以使用@Configuration、@Bean和@Primary注解来完成。
最后,配置MyBatis的SqlSessionFactory和MapperScannerConfigurer,确保每个数据源都有对应的实例。
Docker
如何创建一个Docker容器?
可以使用Docker镜像来创建容器。首先,需要编写一个Dockerfile来定义容器的配置。然后,使用docker build命令基于Dockerfile构建镜像。最后,使用docker run命令基于镜像创建并运行容器。Docker镜像和容器的区别是什么?
Docker镜像是一个只读的模板,用于创建Docker容器。镜像包含了应用程序的代码、运行时环境、依赖项和配置等。而Docker容器则是运行中的实例,可以从镜像创建并启动。可以将容器看作是镜像的可执行版本。如何共享数据和文件夹给Docker容器?
可以使用Docker的数据卷(Volume)来共享数据和文件夹给容器。通过将主机目录挂载到容器的指定目录,可以实现主机与容器之间的数据共享。可以使用-v或--mount参数来指定挂载配置。如何将容器与容器连接和通信?
可以使用Docker的网络功能来连接和通信容器。可以创建自定义网络,并使用--network参数将多个容器连接到同一个网络中。这样,容器之间就可以通过容器名称或IP地址进行通信。什么是Docker Compose?它有什么作用?
Docker Compose是一个用于定义和运行多容器应用程序的工具。通过使用简单的YAML文件定义应用程序的服务、网络和卷等配置,可以一键启动、停止和管理多个相关的容器。K8s
https://blog.csdn.net/u013226533/article/details/124386859
https://mp.weixin.qq.com/s/-KRetiHriG-7zPx8O8v_3w
Kubernetes的三种外部访问方式
NodePort、LoadBalancer 和 Ingress
K8S的基本组成部分?
Master节点主要有五个组件,分别是kubectl、api-server、controller-manager、kube-scheduler 和 etcd;node节点主要有三个组件,分别是 kubelet、kube-proxy 和 容器运行时 docker 或者 rkt;
kubectl:客户端命令行工具,作为整个系统的操作入口。
apiserver:以REST API服务形式提供接口,作为整个系统的控制入口。
controller-manager:执行整个系统的后台任务,包括节点状态状况、Pod个数、Pods和Service的关联等。
kube-scheduler:负责节点资源管理,接收来自kube-apiserver创建Pods任务,并分配到某个节点。
etcd:负责节点间的服务发现和配置共享。
kube-proxy:运行在每个计算节点上,负责Pod网络代理。定时从etcd获取到service信息来做相应的策略。
kubelet:运行在每个计算节点上,作为agent,接收分配该节点的Pods任务及管理容器,周期性获取容器状态,反馈给kube-apiserver。
DNS:一个可选的DNS服务,用于为每个Service对象创建DNS记录,这样所有的Pod就可以通过DNS访问服务了。
Pod的状态?
1)Pending:已经创建了Pod,但是其内部还有容器没有创建;
2)Running:Pod内部的所有容器都已经创建,只有由一个容器还处于运行状态或者重启状态;
3)Succeeed:Pod内所有容器均已经成功执行并且退出,不会再重启;
4)Failed:Pod内所有容器都退出,但至少有一个为退出失败状态;
5)Unknown:由于某种原因不能获取该Pod的状态,可能是网络问题;
Kubelet的作用是什么
Kubelet是运行在每个Node上的代理,负责与Master节点通信,管理Pod的生命周期,并报告Node的状态Kube Proxy的作用是什么
Kube Proxy运行在每个Node上,负责维护网络规则,实现Service的负载均衡和网络路由什么是StatefulSet
StatefulSet是一种控制器,用于管理有状态的应用程序,如数据库。它为每个Pod提供稳定的网络标识和持久存储什么是Deployment
Deployment是一种控制器,用于管理Pod的生命周期,确保一定数量的Pod副本在运行,并支持滚动更新和回滚。Mysql
https://mp.weixin.qq.com/s/wXpAvt6gV4Di9E6oWV28FA
并发事务问题、和隔离级别

undolog、redolog、binlog
这些日志在MySQL中起到了不同的作用:
Undolog保证了事务的原子性和一致性。
Redolog保证了数据库的持久性和故障恢复能力。
Binlog则提供了对数据库操作的详细记录和追踪,以实现数据备份、恢复和数据复制等功能。


主从同步原理

关键字顺序
from
on
join
where
group by
having
select
distinct
union (all)
order by
limit
SQL语句在MySQL中的执行过程
https://javaguide.cn/database/mysql/how-sql-executed-in-mysql.html
慢sql如何排查
索引分类
创建索引原则有哪些?

聚簇、非聚簇、回表?


















浙公网安备 33010602011771号