面试题总结

这篇博客详细剖析了Java面试中常见的重要知识点,涵盖HashMap的底层实现,包括数据结构、扩容策略、红黑树的使用,以及为什么选择特定的设计。此外,还讨论了Spring的核心功能、线程池的submit()和execute()区别,以及MySql的索引原理,如为何使用B+树而非跳表。文章还深入探讨了消息队列如Kafka、RabbitMQ和RocketMQ的区别,并分析了服务调用方式,如Feign的使用。此外,涉及数据库的并发控制、事务特性和Spring的I0C容器实现。最后,涵盖了多种设计模式、并发编程和JVM相关的问题,为Java开发者提供全面的面试准备指南。

目录

1.什么时候使用接口,什么时候使用抽象类?

2.springboot实体类中增加数据库中没有的字段用法

3.Java里 HashMap的底层实现原理

HashMap底层数据结构

为什么在8的时候链表变成树?

HashMap是怎样将数组初始容量的长度转化成2的整数次幂的

为什么要把初始容量转成2的指数次幂呢?不转成2的指数次幂也是可以存储的啊,为什么要转?

为什么加载因子是0.75

为什么用红黑树不用普通的AVL树

为什么在6的时候从树退回链表

HashTable和HashMap(无序不可重复)的区别?

map取值方式

为什么hashtable的扩容是2倍+1

允许null键的map你知道哪些?

null键放在HashMap的哪里

HashMap会自动扩容吗,如何扩容?

4.HashSet如何解决重复

解决哈希冲突的方式

ConcurrentHashMap 底层原理?

CAS是什么?

CAS的问题?

5.==和equals的区别

6.MySQL的索引为什么使用B+树而不使用跳表

①redis的存储json redis的存储引擎是单线程的?

 ②Mysql修改表结构、添加索引会锁表吗?

③B树和B+树的区别?

④红黑树插入的时间复杂度?

7.为什么要用二级缓存,比一级缓存优化在哪里

8.服务之间的调用方式?

②为什么会使用Feign代替Ribbon

③Feign和OpenFeign的区别?

④RPC和HTTP对比

9.kafka、rabbitmq、rocketmq的区别?

①.kafka为什么快

②.kafka删除一个topic

③.如果一个leader挂掉后,所有的follower都有机会成为leader吗

④.follower的作用

⑤.kafka如果保证数据一致性

⑥.kafka如何保证数据全局有序

⑦.kakfa有几个分区,分区的消费策略有哪些

⑧.Kafka为什么同一个消费者组的消费者不能消费相同的分区

⑨.Kafka分区数可以增加或减少吗

⑩kafka不支持减少分区数是有很多原因的?

10.SpringBoot核心功能 

11.线程池中 submit() 和 execute() 方法有什么区别

 12.MySQL索引有哪几种类型 mysql索引可以分为哪些类型?

介绍一下数据库中不同的引擎

什么是索引下推,什么是索引覆盖,什么是回表

13.MySQL的数据定义功能包括哪些?

MySQL的隔离级别?

常用的数据库连接池?

事物的ACID特性?

如何解决幻读问题?

14.谈谈你对Spring的理解?

Spring bean的生命周期

Spring中是如何解决循环依赖的问题的?

缓存的放置时间和删除时间?

BeanFactory和FactoryBean有什么区别?

Spring的核心?

Spring事务的传播机制?

某一个事务嵌套另一个事务的时候怎么办?

REQUIRES_NEW和NESTED的区别?

REQUIRED和NESTED的区别?

Spring框架中的单例bean是线程安全的不?

Spring框架中使用了哪些设计模式及应用场景?

为什么有事务这个概念、事务有什么特性

spring事务的实现方式原理是什么?

Spring事务是如何回滚的?

spring事务什么时候会失效?

spring支持的bean作用域有哪些?

Spring中有两个id相同的bean对象会报错吗?

15.谈一下spring I0C的底层实现

如何实现一个IOC容器?

Spring的AOP的底层实现原理?

16.深克隆和浅克隆

17.redis的数据类型有哪几种?

18.消息队列的作用?

19.Mybatis 中如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

什么是Mybatis及它的优缺点?

Mybatis处理过程:

20.int类型和Integer类型的区别

 为什么要出现封装类型?

21.SpringMVC的理解 

SpringMVC的工作流程:

@Component和@Bean的区别?

22.JDK、JRE、JVM的区别

介绍一下JVM

类加载器

27.线程池7个参数

28.Tomcat为什么要重写类加载器

29.tcp握手挥手过程及其状态转换

30.Cookie和Session的区别

31.说一下反射,反射会影响性能吗

32.log4j的日志级别?

33.java中有几种方法实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?

sleep()和wait()有什么区别?

同步和异步的区别?同步的实现方法?

请对比synchronized与java.util.concurrent.locks.Lock的异同?

34.重载和重写的区别?

35.面向对象的特征?

36.String和StringBuffuer、StringBuilder的区别?

String s =new String (“syz”);创建了几个String Object?

37.作用域public、private、protected 以及不写时的区别?

38.forward和redirect两种跳转方式的区别?


1.什么时候使用接口,什么时候使用抽象类?

 当注重代码的扩展性跟可维护性时,应当优先采用接口:
        ①接口与实现它的类之间可以不存在任何层次关系,接口可以实现毫不相关类的相同行为,比抽象类的使用更加方便灵活;
        ②接口只关心对象之间的交互的方法,而不关心对象所对应的具体类。接口是程序之间的一个协议,比抽象类的使用更安全、清晰。一般使用接口的情况更多。

使用抽象类:

        ①当2个或多个类中有重复部分的时候,我们可以抽象出来一个基类,如果希望这个基类不能被实例化,就可以把这个基类设计成抽象类。

        ②当需要为一些类提供公共的实现代码时,应优先考虑抽象类 。因为抽象类中的非抽象方法可以被子类继承下来,使实现功能的代码更简单。

        ③抽象类只能是单继承的,不能多继承。

总结:使用抽象类是为了代码的复用,而使用接口的动机是为了实现多态性。
抽象类适合用来定义某个领域的固有属性,也就是本质,接口适合用来定义某个领域的扩展功能。

2.springboot实体类中增加数据库中没有的字段用法

①引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

②在实体类字段上加上注解

@Transient
@TableField(exist = false)
private String newPassword;

③使用注解

        @TableField(exist = false)表示该字段在数据库中不存在 ,所以不会插入数据库中

        如果未加该注解,当使用该实体类做查询时,会报错。

3.Java里 HashMap的底层实现原理

①简介:HashMap是一种基于哈希表的Map接口实现。他可以存储键值对,并且支持快速的插入、删除、查找操作

②使用的数据结构:HashMap底层是数组+链表/红黑树

③实现原理:

HashMap内部维护了一个Entry数组,每个Entry包含一个键值对

HashMap根据键的哈希值计算出该哈希值在数组中的位置。如果该位置已经有其它键值对了,则使用链表或红黑树解决冲突

HashMap会根据键的哈希值计算出该键值对在数组中的位置。然后遍历链表或红黑树,找到对应的键值对

Java 8 中,当链表的长度超过一定阈值时,会将链表转换为红黑树,以提高查找效率

补充:

HashMap底层数据结构

JDK1.7及之前:数组+链表

JDK1.8:数组+链表+红黑树

HashMap无序不可重复。

为什么在8的时候链表变成树?

HashMap节点分布遵循泊松分布,按照泊松分布的计算公式计算出了链表中元素个数和概率的对照表,可以看到链表中元素个数为8时的概率已经非常小。

另一方面红黑树平均查找长度是log(n),长度为8的时候,平均查找长度为3,如果继续使用链表,平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果是小于等于6,6/2=3,虽然速度也很快的,但是链表和红黑树之间的转换也很耗时。

当然,虽然在hashmap底层有这种红黑树的结构,但是我们要知道能产生这种结构的概率也不大,所以我们知道在 JDK1.7 到 JDK1.8 这其中HashMap的性能也只提高了7%~8% 左右

HashMap是怎样将数组初始容量的长度转化成2的整数次幂的

HashMap的初始容量是16,默认的加载因子是0.75

在HashMap的源码中有一个HashMap的重载方法,在这个方法里面使用了一个tableSizeFor方法,它的作用是返回一个大于输入参数且最近的2的整数次幂的数,使用的是位运算

为什么要把初始容量转成2的指数次幂呢?不转成2的指数次幂也是可以存储的啊,为什么要转?

添加元素时索引的下标可以通过取模运算获得,但是我们知道计算机的运行效率:加法(减法)>乘法>除法>取模,取模的效率是最低的。所以我们要在HashMap中避免频繁的取模运算,又因为在我们HashMap中他要通过取模去定位我们的索引,并且HashMap是在不停的扩容,数组一旦达到容量的阈值的时候就需要对数组进行扩容。那么扩容就意味着要进行数组的移动,数组一旦移动,每移动一次就要重回记算索引,这个过程中牵扯大量元素的迁移,就会大大影响效率。那么如果说我们直接使用与运算,这个效率是远远高于取模运算的

总结:首先使用位运算来加快计算的效率,而要使用位运算,就需要数组-1然后与hash值保证其在数组范围内,只有当数组长度为2的指数次幂时,其计算得出的值才能和取模算法的值相等,并且保证能取到数组的每一位,减少哈希碰撞,不浪费大量的数组资源

为什么加载因子是0.75

加载因子如果定的太大,比如1,这就意味着数组的每个空位都需要填满,即达到理想状态,不产生链表,但实际是不可能达到这种理想状态,如果一直等数组填满才扩容,虽然达到了最大的数组空间利用率,但会产生大量的哈希碰撞,同时产生更多的链表,显然不符合我们的需求。

但如果设置的过小,比如0.5,这样一来保证了数组空间很充足,减少了哈希碰撞,这种情况下查询效率很高,但消耗了大量空间。

因此,我们就需要在时间和空间上做一个折中,选择最合适的负载因子以保证最优化,取到了0.75

引用:HashMap底层数据结构详解_UKN的博客-优快云博客

为什么用红黑树不用普通的AVL树

自然是AVL还不够好,某些方面还能优化改进,首先大家应该知道,AVL树的平衡是依据平衡因子,就是左右子树高度差不能大于1,这个规则其实过于严格,带来的结果就是几乎每次的插入删除新节点都会破坏平衡!

平衡一旦被破坏就会触发自平衡操作,也就是通过左旋或者右旋来自平衡,所以在插入删除比较多的操作中,AVL会进行频繁的旋转,这就造成了性能下降,可以说,红黑树就是为了解决这个问题!

为什么在6的时候从树退回链表

如果我们设置节点多于8个转红黑树,少于8个就马上转链表,当节点个数在8徘徊时,就会频繁进行红黑树和链表的转换,造成性能的损耗。

HashTable和HashMap(无序不可重复)的区别?

HashTable和hashMap底层实现原理一样,都是哈希表数据结构。两者都是基于k-v键值对的数据结构,k不可以相同,v可以相同

两者都是通过数组+链表 数组是主体,链表是为了解决hash冲突

HashTable的方法都带有synchronized,是线程安全的。

HashTable的key和value都不能为NULL。 HashMap集合的key和value都是可以为null的。

HashTable的初始化容量是11,加载因子是0.75. 容量不要求为2的倍数

HashTable的扩容是:原容量*2+1

HashMap(key 存储的时候会调用底层hashcode(),hashcode是一串数字,然后会进行取余操作

时间复杂度 每一次取数据O(1) , 大多数每一次插入数据O(1) ,理论上增删改查都是O(1)

):初始化容量16,官网推荐为2的倍数,为了散列均匀,提交存取效率,默认加载因子0.75

map取值方式

通过keySet取出map数据[for-each循环]

通过EntrySet取出map数据[for-each循环]

通过EntrySet取出map数据[Iterator遍历]

		//通过keySet取出map数据[for-each循环]
		System.out.println("-------[for-each循环遍历]通过keySet取出map数据-------");
		Set<Integer> keys = map.keySet();   //此行可省略,直接将map.keySet()写在for-each循环的条件中
		for(Integer key:keys){
			System.out.println("key值:"+key+" value值:"+map.get(key));
		}
		
		//通过EntrySet取出map数据[for-each循环]
		System.out.println("-------[for-each循环遍历]通过EntrySet取出map数据-------");
		Set<Entry<Integer, String>> entrys = map.entrySet();  //此行可省略,直接将map.entrySet()写在for-each循环的条件中
		for(Entry<Integer, String> entry:entrys){
			System.out.println("key值:"+entry.getKey()+" value值:"+entry.getValue());
		}
		
		//通过EntrySet取出map数据[Iterator遍历]
		System.out.println("-------[Iterator循环遍历]通过EntrySet取出map数据---------");			
		Iterator<Entry<Integer, String>> iterator = map.entrySet().iterator();  //map.entrySet()得到的是set集合,可以																					使用迭代器遍历
		while(iterator.hasNext()){
			Entry<Integer, String> entry = iterator.next();
			System.out.println("key值:"+entry.getKey()+" value值:"+entry.getValue());
		}


引用:HashMap_hashmap的key和value可以为null吗_鸠叁的博客-优快云博客 

为什么hashtable的扩容是2倍+1

除留余数法,hashtable初始容量方面回答 

允许null键的map你知道哪些?

HashMap,LinkedHashMap,WeakHashMap

null键放在HashMap的哪里

底层数组的0号位置

HashMap会自动扩容吗,如何扩容?

当HashMap的元素数量超过了其容量(初始容量默认为16)和负载因子(默认为0.75)的乘积时,会触发扩容操作。

HashMap的扩容(resizing)是通过创建一个新的、更大的哈希表,并将旧哈希表中的所有元素重新插入到新哈希表中来实现的。这个过程被称为"rehashing"。

以下是HashMap扩容的具体步骤:

  1. 确定新的大小:新的哈希表的大小是旧哈希表大小的两倍。这是因为HashMap的设计者选择了这种方式来保持哈希表的加载因子,从而在空间和时间效率之间达到一个平衡。
  2. 创建新的哈希表:创建一个新的、更大的哈希表。新的哈希表的长度是旧哈希表长度的两倍。
  3. 重新插入元素:遍历旧哈希表中的所有元素,对每个元素重新计算其在新哈希表中的位置,并将其插入到新的位置。这个过程被称为"rehashing"。
  4. 替换旧的哈希表:当所有的元素都被插入到新的哈希表后,旧的哈希表就被新的哈希表替换。

需要注意的是,扩容是一个代价较高的操作,因为它需要创建新的哈希表并重新插入所有的元素。因此,如果可能,最好在创建HashMap时就指定一个足够大的初始大小,以减少扩容的次数。

4.HashSet如何解决重复

HashSet通过哈希表实现,使用哈希算法将元素的值转换为哈希码并存储在哈希表中。当要添加一个新元素时,HashSet会先计算该元素的哈希码,并查找哈希表中已经存在的哈希码,如果存在相同的哈希码,HashSet就会调用该元素的equals方法进行比较,如果equals返回的是true,HashSet就认为该元素已经存在于集合中。如果哈希码没有冲突,或者哈希码冲突equals返回的是false,HashSet就会将元素添加到集合中。因此,HashSet检查重复就是通过哈希码和equals方法来实现的。

补充:

解决哈希冲突的方式

解决哈希冲突的方法一般有:开放寻址法、链地址法(拉链法)、再哈希法、建立公共溢出区等方法。

1、开放寻址法
用开放定址法解决冲突的做法是:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。 沿此序列逐个单元地查找,直到找到给定 的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探查到开放的 地址则表明表中无待查的关键字,即查找失败。

在开放定址法中根据探查序列生成方式的不同,细分有:线性探查法(线行探查法是开放定址法中最简单的冲突处理方法,它从发生冲突的单元起,依次判断下一个单元是否为空,当达到最后一个单元时,再从表首依次判断。直到碰到空闲的单元或者探查完全部单元为止。)、平方探查法(二次探测法)(平方探查法即是发生冲突时,用发生冲突的单元 d[i], 加上 1²、 2² 等。即 d[i] + 1²,d[i] + 2², d[i] + 3²… 直到找到空闲单元。)、双散列函数探查法(两次散列计算)、伪随机探查法等。

开放定址法的缺点在于删除元素的时候不能真的删除,否则会引起查找错误,只能做一个特殊标记。只到有下个元素插入才能真正删除该元素。

2.链地址法

链地址法(Separate Chaining)的思路是将哈希值相同的元素构成一个同义词的单向链表,并将单向链表的头指针存放在哈希表的第 i 个单元中,查找、插入和删除主要在同义词链表中进行。即将相同hash值的对象组织成一个链表放在hash值对应的槽位。 

引用:【精选】Hash冲突解决方法_怎么解决hash冲突-优快云博客

ConcurrentHashMap 底层原理?

ConcurrentHashMap 是线程安全的哈希表,它是 Java 并发包中提供的一种高效的并发 Map 实现。ConcurrentHashMap 底层采用了分段锁的机制,不同的段(Segment)可以被不同的线程同时访问,从而提高了并发性能。

ConcurrentHashMap 采用了数组 + 链表 + 红黑树的数据结构来实现哈希表。数组的每个元素都是一个段(Segment),每个段都是一个独立的哈希表,包含了若干个键值对。每个段都有自己的锁,不同的段可以被不同的线程同时访问,从而提高了并发性能。

ConcurrentHashMap 的 put 操作和 get 操作都是非常高效的,因为它们都可以并发进行,不需要对整个哈希表加锁。在进行 put 操作时,先根据 key 的哈希值找到对应的段,然后对该段加锁,再在该段中进行插入操作;在进行 get 操作时,也是先根据 key 的哈希值找到对应的段,然后对该段加锁,再在该段中进行查找操作。这样就可以实现高并发的插入和查找操作。

ConcurrentHashMap 的扩容过程也是非常高效的。当某个段的元素个数超过了阈值时,就会触发扩容操作。在进行扩容操作时,只需要对该段加锁,不需要对整个哈希表加锁。同时,扩容时只需要将旧的元素重新分配到新的段中即可,不需要像 HashMap 那样重新计算所有元素的哈希值,因此扩容的效率更高。

总之,ConcurrentHashMap 底层采用了分段锁的机制,不同的段可以被不同的线程同时访问,从而提高了并发性能。同时,它采用了数组 + 链表 + 红黑树的数据结构来实现哈希表,具有高效的插入、查找、扩容等操作。

引用:【Java 基础】ConcurrentHashMap 底层原理_concurrenthashmap底层原理-优快云博客

CAS是什么?

CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。

CAS的问题?

①.CAS容易造成ABA问题。一个线程a将数值改成了b,接着又改成了a,此时CAS认为是没有变化,其实是已经变化过了,而这个问题的解决方案可以使用版本号标识,每操作一次version加1。在java5中,已经提供了AtomicStampedReference来解决问题。

②.CAS造成CPU利用率增加。之前说过了CAS里面是一个循环判断的过程,如果线程一直没有获取到状态,cpu资源会一直被占用。

list和map、Set的区别?

List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口

Set下有HashSet,LinkedHashSet,TreeSet

List下有ArrayList,Vector,LinkedList

Map下有Hashtable,LinkedHashMap,HashMap,TreeMap

List(有序可重复)

list里存放的数据都是有序可重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往List集合里插入或删除数据时,会伴随着后面数据的移动,所以插入删除速度慢

set(无序不可重复)

set里面存放的对象是无序,不可重复的,集合中的对象不按特定的方式排序,只是简单的把对象加入到集合中。

map(键值对,键唯一,值不唯一)

map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到对应的值。

List实现类:

vector、LinkedList、ArrayList

①vector线程安全,由于使用了synchronized方法所以性能上要比ArrayList差,ArrayList非线程安全,LinkedList非线程安全,适合在单线程下使用

②ArrayList和Vector是采用数组方式存储数据,都是通过索引访问数据,查询数据块,插入和删除数据慢;LinkedList基于链表的数据结构,对于插入和删除比较占优势,查询的话LinkedList要移动指针,所以不如ArrayList

Set实现类:

HashSet、LinkedHashSet、TreeSet

1)HashSet:Set接口的实现类,底层实现 HashMap,利用了Hashmap的key进行数据存储,add()方法实际调用的 hashmap的 put()方法实现,只不过只有 key,通过 hashCode()和 equals()方法保证元素唯一。

2)LinkedHashSet:底层实现链表,线程不安全,查询慢,增删快;

3)TreeSet:底层实现红黑二叉树(前序-中序-后序),可以对 Set 元素进行排序,每次存储元素会从根元素进行对比,比元素小挂在左边,比元素大,挂载右边,如果一致则不存储;

Map的实现类:

Hashtable、HashMap、LinkedHashMap、TreeMap

TreeMap是有序的,HashMap和HashTable是无序的。

Hashtable的方法是同步的,HashMap的方法不是同步的。这是两者最主要的区别。

那么这就意味着:

Hashtable是线程安全的,HashMap不是线程安全的。

HashMap效率较高,Hashtable效率较低。

引用:List、Set、Map的区别和关系_list和set和map的区别和联系-优快云博客^v38^pc_relevant_default_base3&spm=1001.2101.3001.4242.2&utm_relevant_index=4

5.==和equals的区别

== 是运算符  可以用于比较基本数据类型和引用数据类型 两端如果是基本数据类型,就是判断值是否相同

equals来自于Object类定义的一个方法,只能用于比较引用类型 equals在重写之后,判断两个对象的属性值是否相同,如果不重写,其实就是==

为什么重写equals要重写hashcode?

先调用hashCode,唯一则存储,不唯一则再调用equals,结果相同则不再存储,结果不同则散列到其他位置。因为hashCode效率更高(仅为一个int值),比较起来更快。

6.MySQL的索引为什么使用B+树而不使用跳表

B+树是多叉平衡搜索树,扇出高,只需要3层左右就能存放2kw左右的数据,同样情况下跳表则需要24层左右,假设层高对应磁盘IO,那么B+树的读性能会比跳表要好,因此mysql选了B+树做索引。
redis的读写全在内存里进行操作,不涉及磁盘IO,同时跳表实现简单,相比B+树、AVL树、少了旋转树结构的开销,因此redis使用跳表来实现ZSET,而不是树结构。
存储引擎RocksDB内部使用了跳表,对比使用B+树的innodb,虽然写性能更好,但读性能属实差了些。在读多写少的场景下,B+树依旧YYDS。

引用:MySQL的索引为什么使用B+树而不使用跳表?_mysql为什么不使用跳表_stevsun的博客-优快云博客

补充:

①redis的存储json redis的存储引擎是单线程的?

因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。

redis 核心就是 如果我的数据全都在内存里,我单线程的去操作 就是效率最高的,为什么呢,因为多线程的本质就是 CPU 模拟出来多个线程的情况,这种模拟出来的情况就有一个代价,就是上下文的切换,对于一个内存的系统来说,它没有上下文的切换就是效率最高的。

redis 用 单个CPU 绑定一块内存的数据,然后针对这块内存的数据进行多次读写的时候,都是在一个CPU上完成的,所以它是单线程处理这个事。在内存的情况下,这个方案就是最佳方案。
引用:redis的存储json redis的存储引擎是单线程的_footballboy的技术博客_51CTO博客

 ②Mysql修改表结构、添加索引会锁表吗?

在MySQL5.7和MySQL8.0中,表结构修改和索引添加通常不会锁定整个表。但是,在某些情况下,MySQL可能需要锁定整个表。如果使用ALTER TABLE语句添加索引,则将锁定表。相反,如果使用CREATE INDEX语句添加索引,则不会锁定表。在MySQL8.0中,还引入了“Invisible Indexes”、“Instant DDL”和“In-Place Alter”升级等新功能,可以进一步提高MySQL的性能和可维护性。在进行表结构修改和索引添加时,需要根据具体情况选择合适的方法,以避免对MySQL的性能产生负面影响。

引用:百度安全验证

③B树和B+树的区别?

1.关键字的数量不同;B+树中分支结点有m个关键字,其叶子结点也有m个,其关键字只是起到了一个索引的作用,但是B树虽然也有m个子结点,但是其只拥有m-1个关键字。
2.存储的位置不同;B+树中的数据都存储在叶子结点上,也就是其所有叶子结点的数据组合起来就是完整的数据,但是B树的数据存储在每一个结点中,并不仅仅存储在叶子结点上。
3.分支结点的构造不同;B+树的分支结点仅仅存储着关键字信息和儿子的指针(这里的指针指的是磁盘块的偏移量),也就是说内部结点仅仅包含着索引信息。
4.查询不同;B树在找到具体的数值以后,则结束,而B+树则需要通过索引找到叶子结点中的数据才结束,也就是说B+树的搜索过程中走了一条从根结点到叶子结点的路径。

④红黑树插入的时间复杂度?

O(logn)

7.为什么要用二级缓存,比一级缓存优化在哪里

解决网络延迟

8.服务之间的调用方式?

①RPC:Remote Produce Call 远程过程调用

②.http (包含RestTemplate)

RestTemplate 结合 eureka(注入Eureka客户端,通过服务名拿到服务实例) 通过地址,动态调用其他微服务 (入参出参)

Feign 会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理

Feign是以接口方式进行调用,而不是通过RestTemplate来调用地址

通过feign直接注入生产者(其他微服务)实例进行调用

 补充:

①Feign是一个声明式的客户端负载均衡器;采用的是基于接口的注解;整合了ribbon,具有负载均衡的能力;整合了Hystrix,具有熔断的能力;

②为什么会使用Feign代替Ribbon


原先使用RestTemplate + ribbon的方式来进行服务间的调用,会导致我们每次去调用其他服务的一个接口,都要单独写一些代码。

而Feign是声明式调用,可以让我们不用写代码,直接用一些接口和注解就可以完成对其他服务的调用。

③Feign和OpenFeign的区别?


依赖不同:一个是spring-cloud-starter-feign,一个是spring-cloud-starter-openfeign
支持的注解:OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。
即:OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

④RPC和HTTP对比


两种方式都是基于TCP通信,一种是RPC调用,一种是HTTP调用。

RPC有几个特点
(1)数据的格式可以自定义
(2)速度快,效率高
(3)早期的wedservice和现在比较热门的dubbo都是RPC的典型代表

HTTP其实就是一种网络传输协议
(1)规定了数据格式
(2)对服务没有任何技术限定
(3)现在rest风格,就可通过Http协议来实现。

9.kafka、rabbitmq、rocketmq的区别?

引用:kafka、rabbitmq 、rocketmq的区别-优快云博客

补充:

①.kafka为什么快

1.数据压缩,减少网络IO,压缩格式包括Gzip、Snappy

2.批量传输,先将消息缓存在内存中,然后达到某个条件(比如到多少条数据,或者到几秒钟)就flush一次,flush到磁盘上

3.顺序读写,避免随机寻址,写入时是单个partition末尾添加

4.利用操作系统的page cache优化读写

5.零拷贝技术。在producer和consumer两个方面都使用了零拷贝技术。

网络数据持久化到磁盘 (Producer 到 Broker)。(使用了mmap)

磁盘文件通过网络发送(Broker 到 Consumer)。(使用了DMA)

零拷贝(Zero-copy)技术指在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间。

②.kafka删除一个topic

Kafka删除一个topic会经历几个步骤:

首先判断要删除的topic是否存在
然后判断是否开启了删除功能,也就是说删除的那个配置是否为true,如果不是true,设置为false,则不会删除,只会将其进行标记
再判断是否有正在进行重分配的topic,topic重分配会导致partition中的数据在broker中转移
如果上述三个都可以,执行删除命令
删除某个topic,其实删除的就是topic下面的partition的数据,此外每个Partition还有主从备份的机制,主备都要删,然后再删除zookeeper里面的数据,因为zookeeper里面管理着kafka的元数据


③.如果一个leader挂掉后,所有的follower都有机会成为leader吗

不是,kafka有一个ISR OSR副本同步队列机制。ISR表示只有跟的上leader的follower才可以进入ISR队列,否则只能呆在OSR队列。因为ISR的机制就保证了,处于ISR内部的follower都是可以和leader进行同步的,一旦出现故障或延迟,就会被踢出ISR。如果Leader挂掉之后,只有ISR中的follower才有机会成为新的leader。

④.follower的作用

读写都是由leader处理,follower只是作备份功能,不对外提供服务。

⑤.kafka如果保证数据一致性

通过ISR + High WaterMark

假设有一主(replica 0)两备(replica 1 replica 2),两备都是在ISR队列中

其中leader数据已经写到了Message4,replica1写到了Message3,replica2写到了Message2。但是consumer只能读取到Message2的数据。

这个比较类似木桶原理,就是consumer读取到的数据永远是ISR队列中所有数据的最小值。在上述情况中,最小值为Message2,因此High WaterMark就是Message2。

原因是因为如果leader挂掉了,但是consumer读取到了原来leader中的Message4的数据,当选出

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值