【Java中高级高频面试题】

Java中高级高频面试题

集合

arraylist和linkedlist的区别?

1、数据结构不同
ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的数据结构

2、效率不同
当随机查询访问ArrayList时,ArrayList比LinkedList的效率更高,因为LinkedList是线性的链表数据存储方式,
所以需要移动指针(遍历链表)从前往后依次查找。

当对数据进行增加和删除的操作时,LinkedList比ArrayList的效率更高,因为ArrayList是数组,
所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动
(
**重要:**
2.1、尾插法插入数据效率并不比LinkedList低,因为只需要往数组后面添加元素即可,不涉及到数据移动,
但是如果尾插达到数组的最大长度(默认是10),ArrayList将会进行扩容,这时候就会涉及到新数组的拷贝,
效率会低。
2.2、头插法和中间随机插入时ArrayList需要进行数据的移动,所以效率会低
)

arraylist扩容原理

arraylist不给与定义数组长度的情况下,默认长度是10,当数据大于数组长度时会进行扩容。
1、扩容时创建一个新的数组,这个数组新容量等于旧容量加上旧容量右移一位
(
**重要:**不要说1.5倍了,只是第一次计算出来正好是1.5倍而已,重点记住计算公式!
新容量等于旧容量加上旧容量右移一位,例如:旧容量10>>右移一位1=5,
新容量 = 10+5;
第二次扩容15>>1=7,这里的新容量数组长度=15+7=22,以此计算)

2、然后使用Arrays.copyOf方法把老数组里面的数据拷贝到新的数组里面。
**!!!注意扩容是先创建新的数组,再把老数组的数据拷贝移动到新数组中。**

Hashmap结构和底层原理

1、结构:
	jdk1.8之前是数组加链表,1.8加入了红黑树
2、原理
	**2.1、什么时候链表会转换成红黑树?**
	答:数组长度达到64并且链表超过8时转换成红黑树
	(**重要:**必须两个条件都达到才会转换成红黑树,如果数组长度没有达到64,
	hashmap会采用扩容的方式让链表数据重新分配从而减低链表长度,
	所以链表是有可能长度超过8也没有转换成红黑树的情况的,
	**网上大多只说链表长度超过8转换成红黑树是不准确的!还是看源码靠谱)**
	
	2.2、扩容机制
		当我们new一个hashmap时并没有初始化数组的长度,而是在我们第一次调用put方法的时候去初始化数组长度,
		默认长度是16。
		1、key的hash:
			static final int hash(Object key) {
    			int h;
    			return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    		}
		(这里有个二次hash计算,也就是>>>16)
	
		**注:会问到为什么要进行二次hash?**
				为了让数据分布得更加均匀,减少hash冲突!

		2、判断数组是否初始化,没有则先初始化数组
		3、扩容
			3.1、触发扩容是当数组数据超过数组长度*负载因子0.75时就会扩容,
			如果数组长度没超过64则扩容数组,如果达到64并且链表超过8则会转换成红黑树。
			扩容后链表或者说数组数据会重新hash计算桶下标值,会重新分布在扩容后的数组桶下标下。
			例如:数组长度16*0.75=12,那么当插入第13个数据的时候就会进行扩容,扩容两倍,16*2=32,
			第二次扩容就是32*0.75=24,大于24就会进行第二次扩容,以此类推。。。
			
			**注:为什么负载因子是0.75?**
				答:因为设置大于0.75时,如果数组数据过多导致链表过长,影响查询效率
					小于0.75时会导致数组频繁扩容,主要也是影响效率和数组利用率
					所以0.75是比较合适的一个取值范围
	
	**注:面试官会问你hashmap1.7和1.8的底层原理区别有哪些?**
		1、以上是1.8的扩容机制,
			1.7扩容并不是数组数据超过数组长度*负载因子0.75时就进行扩容,
			1.7扩容是达到这个条件后还需要产生hash冲突才会扩容,
			例如:我数组数据已经超过12了,但是我第13个元素的桶下标下面并没有值,
			那就会直接放入对应的桶下标中,直到数组满了或者我插入的数据计算出来的桶下标下已经有元素了,
			那这样才会扩容!
		2、1.7是头插法,1.8是尾插法,意思就是1.7版本如果我插入了1和2两个数据,计算出是相同的桶下标,
		那么先插入的值在下面,后插入的值在链表头部。
		而1.8是后插入的值放在链表尾部
		(**记住:7上8下**)

ConcurrentHashMap

	1、ConcurrentHashMap是线程安全的,hashmap非线程安全
	2、1.7主要采用分段锁(16段)+synchronized,1.8主要采用CAS(自旋)+synchronized
	3、ConcurrentHashMap如果key或者value为null会抛出空指针异常,hashmap允许key或者value为null。
	ConcurrentHashMap底层原理整体流程跟HashMap基本类似
	
	**ConcurrentHashMap迭代器是强一致性还是弱一致性?HashMap呢?**
		弱一致性,HashMap强一直性。
		ConcurrentHashMap可以支持在迭代过程中,向map添加新元素,而HashMap则抛出了
		ConcurrentModificationException,因为HashMap包含一个修改计数器,
		当你调用他的next()方法来获取下一个元素时,迭代器将会用到这个计数器。

多线程

为什么要用线程池?

	1、降低资源的消耗,通过重复利用已经创建的线程降低线程创建和销毁造成的消耗。
	2、提高相应速度,当任务到达的时候,任务可以不需要等到线程创建就能立刻执行。
	3、提高线程的可管理性,线程是稀缺资源,使用线程池可以统一的分配、调优和监控

创建多线程的几种方式?

	1、继承于Thread类,重写Thread类的run()方法
	2、实现Runnable接口,实现Runnable中的抽象方法:run( ) 
	3、实现Callable接口,实现call方法
	4、创建线程池的方式
	(注:Thread、Runnable、Callable三种的区别和不同这里不做阐述,也需要去看一下)

怎么创建一个线程池?

	线程池默认提供四种创建方式:
		1.1、使用newCachedThreadPool
			ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
			newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,
			可灵活回收空闲线程,若无可回收,则新建线程
		1.2、使用newFixedThreadPool
			ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
			创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,
			如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中
		1.3、使用newSingleThreadExecutor
			ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
			创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,来一个处理一个,
			它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行
		1.4、使用newScheduledThreadPool
			ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
			创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行

线程池的主要参数有哪些?

ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 
线程池底层有七大参数:
	corePoolSize:核心线程数
	maximumPoolSize:最大线程数
	keepAliveTime:过期时间
	unit:过期时间的单位
	workQueue:阻塞队列
	threadFactory:工厂
	handler:拒绝策略

线程池的工作原理?

	一个线程进来会先由核心线程数进行处理,如果线程数大于核心线程数,那么其它线程首先会放到
	堵塞队列等待,堵塞队列满了后才会进行扩容,创建非核心线程数,当非核心线程数达到配置的最大线程数时,
	就会开启拒绝策略,当超过设置的过期时间非核心线程依然无线程可处理时,就会进行缩容,
	销毁非核心线程数。
	
	注:面试会问到的几个问题
	**1、缩容的实现方式是什么机制?**
		通过自旋的方式,非核心线程不断得去线程池里面拿数据处理,如果超过设置的keepAliveTime时间
		还没拿到数据处理,则会销毁非核心线程,进行缩容
		
	**2、拒绝策略有哪几种方式?**
		第一种拒绝策略是 AbortPolicy(默认),这种拒绝策略在拒绝任务时,会直接抛出异常
			RejectedExecutionException 	(属于RuntimeException)
		第二种拒绝策略是 DiscardPolicy,这种拒绝策略正如它的名字所描述的一样,
			当新任务被提交后直接被丢弃掉,也不会给你任何的通知。
		第三种拒绝策略是 DiscardOldestPolicy,丢弃等待最久的那个线程。
		第四种拒绝策略是 CallerRunsPolicy,当有新任务提交后,如果线程池没被关闭且没有能力执行,
			则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务。

实际工作中你用那种方式的线程池?

	这里一定要说自定义线程池,因为默认提供的四种线程池的堵塞队列底层容量是Integer.MAX_VALUE,
	这样会导致大量线程堵塞在队列里面,有很大的风险。
	所以一般去自定义线程池,重写ThreadPoolExecutor()方法!

	**注:还会问你生产上核心线程数设置多少?**
		核心线程数=CPU核心数+1
		例如你的cpu是8核的,就设置9个核心线程数
		这个值不建议说太大,这个值一定要大于cpu数,小于等于cpu的两倍,
		因为小于cup数就会造成有些的cup没有利用到,
		大于cpu的两倍也一样,会导致cup上下文频繁切换。

JVM

内存模型

内存模型主要包含五部分的内容:堆、栈、本地方法栈、方法区(元空间)、程序计数器。

	**堆**:JVM管理的最大一块内存空间,它是所有线程所共享的一块区域。在虚拟机启动的时候创建,
		该区域的唯一目的就是为了存放对象实例,几乎所有通过new创建的实例对象都会被分配在该区域。

	**栈(虚拟机栈)**:也可以称为虚拟机线程栈,它是JVM中每个线程所私有的一块空间,每个线程都会有这么
		一块空间。它的生命周期是与线程的生命周期是绑定的。用于存放局部变量表、操作数栈、动态连接和
		方法出口等信息,每个方法从调用到完成的过程,就对应着一个栈帧在线程栈中从入栈到出栈的过程。

	**本地方法栈**:本地方法栈与虚拟机栈的作用是相似,不同的是虚拟机栈为JVM执行的Java方法服务,而
		本地方法栈为JVM调用的本地方法服务。

	**程序计数器**:只需要占用一小块的内存空间,每个线程都会有自己独立的程序计数器,
		主要功能就是记录当前线程执行到哪一行指令了,可以看作是当前线程所执行的字节码行号指示器。

	**方法区(元空间)**:在JDK 8之前,方法区也称之为永久代,这部分区域与堆一样,是所有线程所共享的,
		它主要用于存放被虚拟机加载的类型信息、常量、静态变量以及即时编译器编译后的代码缓存等数据。
		对于一个Class文件,除了版本、字段、方法、接口等描述信息外,还有常量池表,但需要注意的是,
		在JDK 7以后的版本中,字符串常量池和静态变量等被移至到了Java堆区,而到了JDK 8,抛弃了之前
		永久代的概念,通过在本地内存中实现了元空间(Meta-space)来代替永久代,并把JDK 7中永久代
		剩余内容(主要是类型信息)全部移至到了元空间。

	所以,方法区是使用直接内存来实现,这与堆是不一样的,也就是堆和方法区用的并不是同一块物理内存

垃圾回收过程及算法

**1、常用的垃圾回收算法:**
		**标记清除算法**:标记存活的对象,把未标记的回收。回收后内存不是连续的,会产生大量的不连续的碎片,
			这样会导致后续创建大对象时无法分配连续的内存空间,标记对象的时候效率低。
		**复制算法**:会把内存分为相同的2个部分,每次回收,会把存活的对象移动到另一边,回收当前使用的空间。
			分配的内存被分成2份,实际使用空间变成正常的一半。虽然效率高而且不会出现垃圾碎片,但是需要
			两块内存空间,比较耗费内存。
		**标记整理算法**:在标记清除的基础上做了优化,清除完垃圾对象后会把存活的对象移动到一起,这样就
		不会存在不连续的碎片,但是需要移动对象,效率低。

**2、垃圾回收过程?**
	首先要了解什么样的才能算垃圾对象会被回收
		1、引用计算法:每个对象要维护一个引用计数器,当这个对象被引用后该引用计数器+1,减少一个引用
			后会-1,如果这个对象的引用是0,则可以认为可回收,但是很难处理循环引用的问题。
		2、可达性分析:可达性分析算法是从GC Roots的对象作为起始点,从这个被称为GC Roots的对象开始
			向下搜索,如果一个对象到GCRoots没有任何引用链相连时,则说明此对象不可用。也即给定一个
			集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可到达的)对象就被判定为存活
			,没有被遍历到的就自然被判定为死亡
			
			**重要:那些可以作为GC ROOTS对象?**
				1. java虚拟机栈中的引用的对象。
				2.方法区中的类静态属性引用的对象。 (一般指被static修饰的对象,加载类的时候就加载到内存中。)
				3.方法区中的常量引用的对象。
				4.本地方法栈中的JNI(native方法)引用的对象

垃圾回收主要是年轻代和老年代
	**年轻代:**采用的是复制算法,因为年轻代存活率低,垃圾回收频率最高,复制算法效率更快,
		年轻代又划分三块内存区域,分别是Eden区、Form区(s0)、To区(s1),默认比例为8:1:1,
		新创建的对象都会被分配到Eden区(大对象会直接进入老年代),GC时Eden区中所有存活的对象都会
		被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值
		(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置年龄,默认是15)的对象会被移动到年老代中,
		没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,
		“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。
		**不管怎样,名为To的区域是GC后永远都是空的**。

	**注:这里会问到对象的年龄是存放在那里的?**
			对象的回收分代年龄存放在对象头!
			
	**扩展:Java对象的组成部分,每一个对象都由对象头、对象的实例数据区和对齐填充字节这三部分组成**。
		1、对象头:
			对象头由三部分组成:
				Mark Word:记录对象和锁的有关信息。当一个对象被 synchronized 关键字加锁之后,
					围绕锁的操作就都会和MarkWord有关联。MarkWord通常都是 32 bit位大小。
					会保存一些分代年龄、无锁状态下对象的HashCode、偏向锁的线程ID、轻量级锁指向
					栈中锁记录的指针、指向重量级锁的指针、锁的标志位等内容。
				指向类的指针:大小也通常为32bit,它主要指向类的数据,也就是指向方法区中的位置。
				数组长度:只有数组对象才有,在32位或者64位JVM中,长度都是32bit。
		2、实例数据区:
			该区域主要就存放着实例对象的一些字段属性内容。
		3、对齐填充字节
			由于JVM要求Java对象所占的内存大小应该是8bit的倍数,所以这部分主要就是将
			对象大小补充为8bit的倍数,没有别的功能。

	**老年代:**区域较大,对像存活率高,一般用标记整理算法

**3、jvm调优和问题排查?**
	这里一般都是调整堆、栈、年轻代、老年代、垃圾回收器等。。
	这里没有固定答案,要根据项目实际情况去分析,你这里可以举例遇到的jvm性能上面的错误或者瓶颈,
	然后怎么去解决的。
	例如:-xx:Xms和-xx:Xmx值配置一样
	1、频繁垃圾回收问题:你这边可以说看日志发现有代码频繁创建大对象并且没有释放,
		或者说查看jvm配置堆或者年轻代的配置太小了,然后根据项目的大小和
		实际的服务器机器内存去进行了调整,网上很多说比例物理内存内存的1/8,或者说1/64的,
		其实也只是一个参考和建议,主要看实际项目情况,如果我项目小对象少,我1/100也够用呢,
		所以这个值自己去把握并解释一下就行。
	2、OOM(内存溢出)问题:这里首先说配置了-XX:+HeapDumpOnOutOfMemoryError 和 
		-XX:HeapDumpPath=/Users/jarye/Downloads/heapdump.dump,这里就是配置发生内存溢出时
		会在指定的目录下生成.dump的文件,然后下载这个文件用Mat工具进行查看分析原因。
	3、CPU飙升居高不下问题:排查思路是先找到进程id,然后根据进程id查看线程id并进行排序,看那个线程占cpu最高,
		查询到的线程id是十进制的,还需要通过printf 命令转换成十六进制,然后用jstack 命令查看线程日志。
		
	具体操作细节大家可以网上搜一下,这里就不做明细讲解了。

jvm常见参数:
	-XX:Xms 最小堆内存
	-XX:Xmx 最大堆内存
	-XX:Xss 栈内存
	-XX:Xmn 年轻代内存
	-XX:SurvivorRatio=8  设置年轻代中Eden区与一个Survivor区的比例为8:1,默认为8
	-XX:NewRatio=2 设置老年代和年轻代比例大小2:1,默认为2
	。。。。。。。

**4、1.8默认垃圾回收器是哪一种?**
	默认是-XX:+UseParallelGC,使用的是Parallel Scavenge(年轻代)和Parallel Old(老年代)收集器组合

Spring

spring的AOP和IOC?

	AOP :面向切面编程,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等,
	公用操作处理的过程就是面向切面编程的思想。AOP 底层是动态代理,动态代理又是基于反射实现的,如果是接口采用 JDK 动态代理,
	如果是类采用CGLIB 方式实现动态代理。
	
	IOC:控制反转,就是由 spring 来负责控制对象的生命周期和对象间的关系。简单来说,控制指的是当前对象对内部成员的控制权;
	控制反转指的是,这种控制权不需要程序员手动去创建对象了,由其他(类,第三方容器)来创建和管理。主要是基于DI(依赖注入)
	实现,方式有三种,分别是构造方法注入,setter注入,基于注解的注入(@Autowired、@Resource)

Spring boot自动装配原理和加载过程?

	https://cloud.tencent.com/developer/article/2068889

Spring的对象循环依赖问题怎么解决?

	https://baijiahao.baidu.com/s?id=1694034649427742142&wfr=spider&for=pc

说一下Spring的双亲委派机制?

	https://blog.csdn.net/codeyanbao/article/details/82875064

Spring事务是如何实现的?那些情况会导致事务失效?

	1、https://blog.csdn.net/dayuiicghaid/article/details/125262298
	**注:分布式事务如何实现?**
		http://www.toobug.cn/post/8390.html
		这里要去了解一下mysql的binlog、redo log、undo log三个日志特性
	
	2、https://blog.csdn.net/weixin_43564627/article/details/121354260

说一下Spring bean的作用域和生命周期?

	https://blog.csdn.net/tomonkey/article/details/104801929/

以上这些都去看一下实际的讲解教学视频,跟着老师一步一步去看源码,这样才能更好理解,很多问题光背是没用的。

Java

java类加载机制?

![在这里插入图片描述](https://img-blog.csdnimg.cn/3f5e9eef714b4394bab8627c5bea97f7.png#pic_center)
类的加载过程主要分这几个阶段:加载、验证、准备、解析、初始化
	1、加载:将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,
		然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构
	2、验证:验证的目的是为了确保Class文件中的字节流包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。
		包括文件格式的验证、元数据验证、字节码验证、符号引用验证。
	3、准备:准备阶段正式为类变量分配内存并设置类变量初始值(null或者0),这些内存都将在方法区中进行分配,
		注意final修饰的static的值在这个阶段就已经完成初始化赋值了。
	4、解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
		直接引用可以是直接指向目标的指针、相对偏移量或者能间接定位到目标
	5、初始化:为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。
		JVM初始化步骤
			1)、假如这个类还没有被加载和连接,则程序先加载并连接该类
			2)、假如该类的直接父类还没有被初始化,则先初始化其直接父类
			3)、假如类中有初始化语句,则系统依次执行这些初始化语句

反射原理?

	https://blog.csdn.net/qq_35958391/article/details/124840988

MyBatis

MyBatis的一、二级缓存和底层实现运用到了什么技术?

https://dandelioncloud.cn/article/details/1481993814998056961/
重点JDBC和动态代理

**注:在分布式下开启二级缓存会有什么问题吗?**
	https://www.cnblogs.com/goloving/p/14855362.html
	mybatis自带的二级缓存,但是这个缓存是单服务器工作,无法实现分布式缓存

# 和 $的区别?

#是一个占位符,$是拼接符
使用# 方式引用参数的时候,Mybatis会把传入的参数当成是一个字符串,自动添加双引号。
使用$ 引用参数时,不做任何处理,直接将值拼接在sql语句中。
# 的方式引用参数,mybatis会先对sql语句进行预编译,然后再从对象里面去获取值,能够有效防止sql注入,提高安全性。
$ 的方式引用参数,sql语句不进行预编译。

MyBatis有哪些动态sql标签?

MyBatis提供了9种动态SQL标签:trim、where、set、foreach、if、choose、when、otherwise、bind;

MyBatis和MyBatis Plus的区别?

https://blog.csdn.net/m0_67391401/article/details/123778699

RabbitMQ

怎么避免消息丢失和重复消费?

避免消息丢失:https://www.cnblogs.com/frankcui/p/15374268.html
解决重复消费:https://blog.csdn.net/weixin_45393094/article/details/123150714

两条消息同时发送到MQ,消费端怎么保证按顺序消费?

https://blog.csdn.net/m0_49496327/article/details/123690279

Redis

Redis 有哪几种数据类型?分别用于什么场景?

1、String:它是二进制安全的,可以存储图片或者序列化的对象,值最大存储为512M
		应用场景:共享session、分布式锁,计数器等。
2、Hash:哈希类型是指v(值)本身又是一个键值对(k-v)结构
		应用场景:缓存用户信息等。
3、List:列表(list)类型是用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素。
		应用场景: 消息队列,文章列表。
4、Set:集合(set)类型也是用来保存多个的字符串元素,但是不允许重复元素。
		应用场景: 用户标签,生成随机数抽奖、社交需求。
5、Zset:已排序的字符串集合,同时元素不能重复.
		应用场景:排行榜,社交需求(如用户点赞)。

Redis的持久化方式?

redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。
	RDB:就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;
	AOF:是将redis执行过的所有写指令通过日志方式记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,
		就可以实现数据恢复了。
	对比:RDB快照的方式在恢复数据的时候更快,但是因为是快照式的,数据完整性没有AOF高。
		 AOF已日志的信息记录,数据完整性高,但是日志文件过大会导致恢复数据比较慢。
		 
	RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,
	这是因为AOF方式的数据恢复完整度更高。

Redis的分布式锁怎么实现?

主要是Redisson
https://www.cnblogs.com/wangyingshuo/p/14510524.html

缓存雪崩、穿透、击穿怎么处理?

https://blog.csdn.net/qq_41071876/article/details/120076924

怎么保证数据一致性?

这里可以先说可以采用先删缓存再更新数据库 或者先更新数据库再删缓存的方式,然后说明一下这两种分别产生什么问题。
1、 先删除了redis缓存,但是因为其他什么原因还没来得及写入数据库,另外一个线程就来读取,发现缓存为空,
	则去数据库读取到之前的数据并写入缓存,此时缓存中为脏数据。
2、 如果先写入数据库再删缓存,如果在缓存被删除前,写入数据库后因为其他原因被中断了,没有删除掉缓存,
	就也会出现数据不一致的情况。
然后再说更好的方式的话可以采用双删机制。

具体详细讲解:https://www.jb51.net/article/224160.htm

Redis的高可用有哪几种方式?

https://blog.csdn.net/weixin_44183721/article/details/126195582

**注:缓存数据倾斜问题怎么解决?**
数据倾斜就是分布不均匀的问题,这里要去了解一下redis采用的一致性hash算法
https://developer.aliyun.com/article/938465

说一下Redis的缓存淘汰策略?

https://blog.51cto.com/u_11720620/5198456

Redis内存满了怎么处理?

大key的查找没用则删除、扩容、淘汰策略这几个方面。
https://blog.csdn.net/zhizhengguan/article/details/120813836

Nginx

怎么配置反向代理和负载均衡?

https://blog.csdn.net/projectno/article/details/118303004

负载均衡有哪几种方式?

https://blog.csdn.net/Kevinnsm/article/details/114671552

Mysql

索引类型有哪几种?

https://blog.csdn.net/Ghost_hell/article/details/119822128

索引为什么快?

去了解一下B+树的数据结构再看这篇文章就很好理解了
https://blog.csdn.net/weixin_42492543/article/details/113216509

B树和B+树的区别?

https://blog.csdn.net/weixin_42112028/article/details/125483550

常见的数据库优化有哪些?

sql、索引(避免回表)、表设计、分表等方面去优化
https://blog.csdn.net/wuxianbing2012/article/details/122960591

有几种隔离级别?分别解决了什么问题?默认隔离级别是什么?

默认隔离级别:Repeatable Read可重复读(oracle是读已提交)
https://blog.csdn.net/easylife206/article/details/102814254

update一条数据是行锁还是表锁?

update时,where中的过滤条件列,如果走了索引,锁行,无法用索引,锁表。
https://blog.csdn.net/Fire_Sky_Ho/article/details/120239996

说一下Mysql的binlog、redo log、undo log分别是什么?

https://blog.csdn.net/weixin_44688973/article/details/125460075

为什么要遵循索引的最左匹配原则?

https://blog.csdn.net/qq_40277163/article/details/124131756

说一下Mysql的回表?

https://www.cnblogs.com/taojietaoge/p/16167188.html

说一下explain执行计划的一些字段意义?

https://blog.csdn.net/User_jing/article/details/119882903

Mysql和oracle的区别有哪些?

https://baijiahao.baidu.com/s?id=1706137861467720857&wfr=spider&for=pc

SpringCloud

有哪些组件?分别用处是什么?

https://zhuanlan.zhihu.com/p/150782140

这是我面试了很多公司的一个总结,在这里做个记录,大概记得这些,
后续有其他的会持续更新,有哪里写的什么不对的麻烦大家指出,我好更正,以免误人子弟

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值