目录
HashMap是怎样将数组初始容量的长度转化成2的整数次幂的
为什么要把初始容量转成2的指数次幂呢?不转成2的指数次幂也是可以存储的啊,为什么要转?
①redis的存储json redis的存储引擎是单线程的?
③.如果一个leader挂掉后,所有的follower都有机会成为leader吗
⑧.Kafka为什么同一个消费者组的消费者不能消费相同的分区
11.线程池中 submit() 和 execute() 方法有什么区别
12.MySQL索引有哪几种类型 mysql索引可以分为哪些类型?
19.Mybatis 中如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
33.java中有几种方法实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?
请对比synchronized与java.util.concurrent.locks.Lock的异同?
36.String和StringBuffuer、StringBuilder的区别?
String s =new String (“syz”);创建了几个String Object?
37.作用域public、private、protected 以及不写时的区别?
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扩容的具体步骤:
- 确定新的大小:新的哈希表的大小是旧哈希表大小的两倍。这是因为HashMap的设计者选择了这种方式来保持哈希表的加载因子,从而在空间和时间效率之间达到一个平衡。
- 创建新的哈希表:创建一个新的、更大的哈希表。新的哈希表的长度是旧哈希表长度的两倍。
- 重新插入元素:遍历旧哈希表中的所有元素,对每个元素重新计算其在新哈希表中的位置,并将其插入到新的位置。这个过程被称为"rehashing"。
- 替换旧的哈希表:当所有的元素都被插入到新的哈希表后,旧的哈希表就被新的哈希表替换。
需要注意的是,扩容是一个代价较高的操作,因为它需要创建新的哈希表并重新插入所有的元素。因此,如果可能,最好在创建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的数据,当选出

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

被折叠的 条评论
为什么被折叠?



