腾讯 Java 面试
腾讯 Java 面试
腾讯终于也 陆陆 续续 开奖了,今年腾讯的薪资结构相比去年是有变化 的。
往年腾讯校招生每个月都有 4k 的房补,然后是 16 薪资(12 薪资+4 个月年终),今年开始房补是加入到了月薪 base ,并且把第 13 薪资平摊到了月薪 base ,年终奖变为 3 个月。
这样变化 之后,月薪 base 相比之前就会高不少,每个月到手的变多了,但是总包其实没变化 。先看看 去年 24 届腾讯的校招薪资:

根据今年已 开奖同学的薪资情况,我也整理了一个 25 届腾讯后端开发岗位的校招薪资:

ssp offer :
33k * 15 + 10w (股票分 2 年发)+ 签字费6w ,同学 bg 硕士海⻳,base 深圳
32k * 15 + 6w (股票分 2 年发)+ 签字费3w ,同学 bg 硕士 985 ,base 深圳
sp offer :
30k * 15 + 6w (股票分 2 年发)+ 签字费3w ,同学 bg 硕士 985 ,base 深圳
29k * 15 + 6w (股票分 2 年发)+ 签字费3w ,同学 bg 硕士 985 ,base 深圳
28k * 15 + 6w (股票分 2 年发) + 签字费3w ,同学 bg 硕士 985 ,base 北京
27k * 15 + 签字费3w ,同学 bg 本科,base 深圳
普通 offer :
26k * 15 ,同学 bg 未知,base 深圳
24k * 15 ,同学 bg 未知,base 深圳
今天给大家分享一位 Java 后端同学的腾讯校招面经,问的问题还是比较多的,接近 30 个问题,
再加上写算法,一场面试下来,时长有 1 小时+。
面试的强度还是很大,很多同学跟我反馈,每次 面完腾讯都一把汗,甚至有同学被腾讯面了 2 小
时+。

考察的知识还是比较多的,我这里简单给在大家列了一下:
操作系统:进程&线程、进程隔离性
数据结构:排序算法、排序稳定性、归并排序、快速排序
MySQL :存储引擎、聚簇索引、B+ 树、索引失效、事务隔离级别、脏读、幻读
Redis :数据类型、String 底层实现、热 key
Java :ArrayList 、Vector 、HashMap
MQ :消息队列选型、消息可靠性、消息确认机制、Kafka 、RocketMQ
总体考察的范围,就是编程语言+计算机基础+后端组件。

操作系统
进程和线程的区别
本质区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
在开销方面:每个进程都有独立的代码和数据空间(程序上下 文),程序之间的切换会有较大的
开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立
的运行栈和程序计数器( PC ),线程之间切换的开销小
稳定性方面:进程中某个线程如果崩溃了,可能会导致整个进程都崩溃。而进程中的子进程崩
溃,并不会影响其他进程。
内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了
CPU 外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只
能共享资源
包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不
是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权
进程或者轻量级进程
为什么 进程崩溃不会对其他进程产生很大影响
主要是因为:
进程隔离性:每个进程都有自己独立的内存空间,当一个进程崩溃时,其内 存空间会被操作系
统回收,不会影响其他进程的内存空间。这种进程间的隔离性保证了一个进程崩溃不会直接影
响其他进程的执行。
进程独立性:每个进程都是独立运行的,它们之间不会共享资源,如文件、网络连接等。因
此,一个进程的崩溃通常不会对其他进程的资源产生影响。
数据结构
知道哪些排序算法,时间复杂度

冒泡排序:通过相邻元素的比较和交换,每次 将最大(或最小)的元素逐步“冒泡”到最后(或
最前)。时间复杂度:最好情况下O(n) ,最坏情况下O(n^2) ,平均情况下O(n^2) 。,空间复杂
度:O(1) 。插入排序:将待排序元素逐个插入到已排序序 列的合适位置,形成有序序 列。时间复杂度:最
好情况下O(n) ,最坏情况下O(n^2) ,平均情况下O(n^2) ,空间复杂度:O(1) 。选择排序:通过不断选择未排序部分的最小(或最大)元素,并将其放置在已排序部分的末尾
(或开头)。时间复杂度:最好情况下O(n^2) ,最坏情况下O(n^2) ,平均情况下O(n^2) 。空间
复杂度:O(1) 。快速排序:通过选择一个基准元 素,将数组划分 为两个 子数组,使得左子数组的元素都小于
(或等于)基准元 素,右子数组的元素都大于(或等于)基准元 素,然后对子数组进行递归排
序。时间复杂度:最好情况下O(nlogn) ,最坏情况下O(n^2) ,平均情况下O(nlogn) 。空间复杂
度:最好情况下O(logn) ,最坏情况下O(n) 。归并排序:将数组不断分割 为更小的子数组,然后将子数组进行合并,合并过程中进行排序。
时间复杂度:最好情况下O(nlogn) ,最坏情况下O(nlogn) ,平均情况下O(nlogn) 。空间复杂度:
O(n) 。堆排序:通过将待排序元素构建成一个最大堆(或最小堆),然后将堆顶元素与末尾元素交换,
再重新调整堆,重复该过程直到排序完成。时间复杂度:最好情况下O(nlogn) ,最坏情况下
O(nlogn) ,平均情况下O(nlogn) 。空间复杂度:O(1) 。归并排序和快速排序的使用场景
归并排序是稳定排序算法,适合排序稳定的场景;
快速排序是不稳定排序算法,不适合排序稳定的场景,快速排序是目前基于比较的内部排序中
被认为是最好的方法,当待 排序的关键字是随机分布时,快速排序的平均时间最短;
排序稳定是什么 意思?

排序稳定指的是在排序过程中,对于具有相同排序关键字的元素,在排序后它们的相对位置保持
不变。
换句话说,如果在排序前两个 元素 A 和 B 的值相等,并且 A 在 B 的前面,那么在排序后 A 仍然在
B 的前面,这样的排序就是稳定排序。稳定排序保持了相同元素之间的顺序关系,适用于需要保持
原始顺序的场景。
稳定和不稳定排序算法有什么 特点?
稳定排序算法的特点:
相同元素的相对位置不会改变,排序后仍然保持原始顺序。
适用于需要保持元素间相对顺序关系的场景,如按照年龄排序后按姓名排序。
不稳定排序算法的特点:
相同元素的相对位置可能会改变,排序后不保证原始顺序。
可能会更快,但不适用于需要保持元素间相对顺序关系的场景。
MySQL
MySQL 的存储引擎有哪些?为什么 常用InnoDB ?
MySQL 的存储引擎常用的主要有 3 个:
InnoDB 存储引擎:支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务
的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选 择InnoDB 有很大的优
势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB ,因为支持事务的提交
(commit )和回滚(rollback )。
MyISAM 存储引擎:插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读
出记录,那么选择MyISAM 能实现处理高效率。如果应用的完整性、并发性要求比 较低,也可
以使 用。如果数据表主要用来插入和查询记 录,则MyISAM 引擎能提供较高的处理效率
MEMORY 存储引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要
很快 的读写速度,对数据的安全性要求较低,可以选择MEMOEY 。它对表的大小有要求,不能
建立太大的表。所以,这类数据库只使用在相对较小的数据库表。如果只是临时存放数 据,数
据量不大,并且不 需要较高的数据安全性,可以选择将数据保存在内存中的Memory 引擎,
MySQL 中使用该引擎作为临 时表,存放查询的中间结果
常用InnoDB 的原因是支持事务,且最小锁的粒度是行级锁。
B+ 树和 B 树的比较
B 树和 B+ 都是通过多叉树的方式,会将树的高度变矮,所以这两个 数据结构非常适合检索存于磁
盘中的数据。
但是 MySQL 默认的存储引擎 InnoDB 采用的是 B+ 作为索引的数据结构,原因有:
B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储
即存索引又存记录的 B 树,B+ 树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更
「矮胖」,查询底层节点的磁盘 I/O 次数会更少。
B+ 树有大量的冗余节点(所有非叶子节点都是冗余索引),这些冗余索引让 B+ 树在插入、删
除的效率都更高,比如删除根节点的时候,不会像 B 树那样会发生复杂的树的变化 ;
B+ 树叶子节点之间用链表连接了起来,有利于范围查询,而 B 树要实现范围查询,因此只能通
过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。
除了聚簇索引,还有什么 索引?
还有二级索引、联合索引、前缀索引、唯一索引等。
二级索引存放的有哪些数据?
索引又可以分成聚簇索引和非聚簇索引(二级索引),它们区别就在于叶子节点存放的是什么 数
据:
聚簇索引的叶子节点存放的是实际数据,所有完整的用户记录都存放在聚簇索引的叶子节点;
二级索引的叶子节点存放的是主键值,而不是实际数据。
索引失效的情况
索引失效的情况:
当我们使 用左或者左右模糊匹配的时候,也就是 like %xx 或者 like %xx% 这两种方式都会
造成索引失效;
当我们在查询条件中对索引列使用函数,就会导致索引失效。
当我们在查询条件中对索引列进行表达式计算,也是无法走索引的。
MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较。如果字
符串是索引列,而条件语句中的输入参数是数字的话,那么索引列会发生隐式类型转换,由于
隐式类型转换是通过 CAST 函数实现的,等同于对索引列使用了函数,所以就会导致索引失
效。
联合索引要能正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引的匹配,
否则就会导致索引失效。
事务隔离级别有哪些?
四个隔离级别如下:
读未提交,指一个事 务还没提交时,它做的变更就能被其他事 务看到;
读提交,指一个事 务提交之 后,它做的变更才能被其他事 务看到;
可重复读,指一个事 务执行过程中看到的数据,一直跟这个事 务启动时看到的数据是一致的,
MySQL InnoDB 引擎的默认隔离级别;
串行化;会对记录加上读写锁,在多个事 务对这条记录进行读写操作时,如果发生了读写冲 突
的时候,后访问的事务必须等前一个事 务执行完成,才能继续执行;
按隔离水平高低排序如下:

什么 情况下会出现幻读?
在一个事 务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不
一样的情况,就意味着发生了「幻读」现象。
举个 栗子。
假设有 A 和 B 这两个事 务同时在处理,事务 A 先开始从数据库查询账户余额大于 100 万的记录,
发现共有 5 条,然后事务 B 也按相同的搜索条件也是查询出了 5 条记录。

接下来,事务 A 插入了一条余额超过 100 万的账号,并提交了事 务,此时数据库超过 100 万余额
的账号个数就变为 6。
然后事务 B 再次查询账户余额大于 100 万的记录,此时查询到的记录数量有 6 条,发现和前一次
读到的记录数量不一样了,就感觉发生了幻觉一样,这种现象就被称为幻读。
事务的 MVCC 是怎么实现的?
对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 Read View 来实现的,它们的
区别在于创建 Read View 的时机不同:
「读提交」隔离级别是在每个 select 都会生成一个新的 Read View ,也意味着,事务期间的多
次读取同一条数据,前后两次读的数据可能会出现不一致,因为可能这期间另外一个事 务修改
了该记 录,并提交了事 务。
「可重复读」隔离级别是启动事务时生成一个 Read View ,然后整个事 务期间都在用这个 Read View ,这样就保证了在事务期间读到的数据都是事务启动前 的记录。
这两个 隔离级别实现是通过「事务的 Read View 里的字段」和「记录中的两个 隐藏列」的比对,
来控制并发事务访问同一个记录时的行为,这就叫 MVCC (多版本并发控制)。
在创建 Read View 后,我们可以将记录中的 trx_id 划分 这三种情况:

一个事 务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:
如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建
Read View 前已经提交的事务生成的,所以该版本的记录对当前事务可见。
如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建Read View 后才启动的事务生成的,所以该版本的记录对当前事务不可见。
如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间,需要判断 trx_id 是否
在 m_ids 列表中:
如果记录的 trx_id 在 m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见。
如果记录的 trx_id 不在 m_ids 列表中,表示生成该版本记录的活跃事务已经被提交,所以该版
本的记录对当前事务可见。
这种通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC (多版本并发控制)。
事务之间怎么避免脏读的?
针对不同的隔离级别,并发事务时可能发生的现象也会不同。

也就是说:
在「读未提交」隔离级别下,可能发生脏读、不可重复读和幻读现象;
在「读提交」隔离级别下,可能发生不可重复读和幻读现象,但是不可能发生脏读现象;
在「可重复读」隔离级别下,可能发生幻读现象,但是不可能脏 读和不可重复读现象;
在「串行化」隔离级别下,脏读、不可重复读和幻读现象都不可能会发生。
所以,要解 决脏读现象,就要升级到「读提交」以上的隔离级别,这样事务只能读到其他事 务已
经提交完成的数据,而不会读到未提交事 务的数据,就避免脏读的问题。
Redis
Redis 支持哪几种数据类型?
Redis 提供了丰 富的数据类型,常见的有五种数据类型:String (字符串),Hash (哈希),List
(列表),Set (集合)、Zset (有序集合)。


img
随着 Redis 版本的更新,后面又支持了四种数据类型:BitMap (2.2 版新增)、HyperLogLog (2.8 版新增)、GEO (3.2 版新增)、Stream (5.0 版新增)。Redis 五种数据类型的应用场景:
String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
List 类型的应用场景:消息队列(但是有两个 问题:1. 生产者需要自行实现全局唯一 ID ;2. 不
能以消费组形式 消费数据)等。
Hash 类型:缓存对象、购物⻋等。
Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
Zset 类型:排序场景,比如排行榜、电话和姓名排序等。
Redis 后续版本又支持四种数据类型,它们的应用场景如下:
BitMap (2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总
数等;
HyperLogLog (2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;
GEO (3.2 版新增):存储地理位置信息的场景,比如滴滴 叫⻋;
Stream (5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个 特有的特
性:自动生成全局唯一消息ID ,支持以消费组形式 消费数据。
热 key 是什么 ?怎么解决?
Redis 热key 是指被频繁访问的key ,可能会导致单个key 的访问量过大,影响系统性能。解决方法包括:
开启内存淘汰机制,并选择使用LRU 算法来淘汰不常用的key ,保证内存中存储的是最热⻔的数
据。
设置key 的过期时间,确保key 在一段时间后自动删 除,防止长时间占用内存。
对热点 key 进行分片,将数据分散存储在不同的节点上,减轻单个key 的压力。
String 是使用什么 存储的?为什么不 用 c 语言中的字符串?
Redis 的 String 字符串是用 SDS 数据结构存储的。
下图就是 Redis 5.0 的 SDS 的数据结构:

结构中的每个成员变量分别 介绍下:
len ,记录了字符串长度。这样获取字符串长度的时候,只需要返回这个成员变量值就行,时间
复杂度只需要 O(1)。
alloc ,分配给字符数组的空间长度。这样在修改字符串的时候,可以通过 alloc - len 计算
出剩 余的空间大小,可以用来判断空间是否满足修改需求,如果不满足的话,就会自动将 SDS
的空间扩展至执行修改所需的大小,然后才执 行实际的修改操作,所以使 用 SDS 既不需要手动
修改 SDS 的空间大小,也不 会出现前面所说的缓冲区溢出的问题。
flags ,用来表示不同类型的 SDS 。一共设计 了 5 种类型,分别 是 sdshdr5 、sdshdr8 、
sdshdr16 、sdshdr32 和 sdshdr64 ,后面在说明区别之处。
buf[] ,字符数组,用来保存实 际数据。不仅 可以保 存字 符串,也可以保 存二进制数据。总的来说,Redis 的 SDS 结构在原本字符数组之上 ,增加了三个 元数据:len 、alloc 、flags ,用来解决 C 语言字符串的缺陷。
O(1)复杂度获取字符串长度
C 语言的字符串长度获取 strlen 函数,需要通过遍历的方式来统计字符串长度,时间复杂度是 O
(N)。
而 Redis 的 SDS 结构因为加入了 len 成员变量,那么获取字符串长度的时候,直接返回这个成员
变量的值就行,所以复杂度只有 O(1)。
二进制安全
因为 SDS 不需要用 “\0” 字符来标识字符串结尾了,而是有个专 ⻔的 len 成员变量来记录长度,所
以可存储包含 “\0” 的数据。但是 SDS 为了 兼容部分 C 语言标准库的函数, SDS 字符串结尾还是
会加上 “\0” 字符。
因此, SDS 的 API 都是以处理二进制的方式来处理 SDS 存放在 buf[] 里的数据,程序不会对其中的数据做任何 限制,数据写入的时候时什么 样的,它被读取时就是什么 样的。
通过使用二进制安全的 SDS ,而不是 C 字符串,使得 Redis 不仅 可以保 存文本数据,也可以保 存
任意格式的二进制数据。
不会发生缓冲区溢出
C 语言的字符串标准库提供的字符串操作函数,大多数(比如 strcat 追加字符串函数)都是不安全
的,因为这些函数把缓冲区大小是否满足操作需求的工作交由开发者来保证,程序内部并不会判
断缓冲区大小是否足够用,当发生了缓冲区溢出就有可能造成程序异常结束。
所以,Redis 的 SDS 结构里引入了 alloc 和 len 成员变量,这样 SDS API 通过 alloc - len 计
算,可以算出剩 余可用的空间大小,这样在对字符串做修 改操作的时候,就可以由程序内部判断
缓冲区大小是否足够用。
而且,当判断出缓冲区大小不够用时,Redis 会自动将扩大 SDS 的空间大小,以满足修改所需的
大小。
Java
编译型语言和解释型语言的区别?
编译型语言和解释型语言的区别在于:
编译型语言:在程序执行之前,整个源代码会被编译成机器码或者字节码,生成可执行文件。
执行时直接运行编译后的代码,速度快,但跨平台性较差。
解释型语言:在程序执行时,逐行解释执行源代码,不生成独立的可执行文件。通常由解释器
动态解释并执行代码,跨平台性好,但执行速度相对较慢。
典型的编译型语言如C、C++ ,典型的解释型语言如Python 、JavaScript 。
动态数组的实现有哪些?
ArrayList 和Vector 都支持动态扩容,都属于动态数组。
ArrayList 和 Vector 的比较
线程安全性:Vector 是线程安全的,ArrayList 不是线程安全的。
扩容策略:ArrayList 在底层数组不够用时在原来的基础上扩展0.5 倍,Vector 是扩展1倍。
HashMap 的扩容条件是什么 ?
Java7 的 HashMap 扩容必须满足两个 条件:
当前数据存储的数量(即size() )大小必须大于等于阈值当前加 入的数据是否发生了hash 冲突
因为上 面这两个 条件,所以存在下面这些情况:
第一种情况,就是hashmap 在存值的时候(默认大小为16 ,负载因子0.75 ,阈值12 ),可能达到
最后存满16 个值的时候,再存入第17 个值才会发生扩容现象,因为前16 个值,每个值在底层数
组中分别 占据一个位置,并没有发生hash 碰撞。
第二种情况,有可能存储更多值(超多16 个值,最多可以存26 个值)都还没有扩容。原理:前
11 个值全部hash 碰撞,存到数组的同一个位置(虽然hash 冲突,但是这时元素个数小于阈值
12 ,并没有同时满足扩容的两个 条件。所以不会扩容),后面所有存入的15 个值全部分散到数组
剩下的15 个位置(这时元素个数大于等于阈值,但是每次 存入的元素并没有发生hash 碰撞,也
没有同时满足扩容的两个 条件,所以也不 会扩容),前面11+15=26 ,所以在存入第27 个值的时候才同时满足上面两个 条件,这时候才会发生扩容现象。
Java8 不再像 Java7 中那样需要满足两个 条件,Java8 中扩容只需要满足一个条件:
当前存放新 值的时候已有元素的个数大于等于阈值
MQ
用的什么 消息队列,消息队列怎么选型的?
项目用的是 RocketMQ 消息队列。选择RocketMQ 的原因是:
开发语言优势。RocketMQ 使用 Java 语言开发,比起使用 Erlang 开发的 RabbitMQ 来说,有着
更容易上手的阅读体验和受众。在遇到 RocketMQ 较为底层的问题时,大部分熟悉 Java 的同学
都可以深入阅读其源码,分析、排查问题。
社区氛围活跃。RocketMQ 是阿里巴巴 开源且内部在大量使用的消息队列,说明 RocketMQ 是
的确经得起残酷的生产环境考验的,并且能够针对线上环境复杂的需求场景提供相应的解决方
案。
特性丰富。根据 RocketMQ 官方文档的列举,其高级特性达到了 12 种 ,例如顺序消息、事务
消息、消息过滤、定时消息等。顺序消息、事务消息、消息过滤、定时消息。RocketMQ 丰富
的特性,能够为我们在复杂的业务场景下尽可能多地提供思路及解决方案。
有没有消息积压的问题
导致消息积压突然增加,最粗粒 度的原因,只有两种:要么是发送变快了,要么是消费变慢了。
要解 决积压的问题,可以通过扩容消费端的实例数来提升总体的消费能力。
如果短时间内没有足够的服务器资源进行扩容,没办法的办法是,将系统降级,通过关闭一些不
重要的业务,减少发送方发送的数据量,最低限度让系统还能正常运转 ,服务一些重要业务。
RocketMQ 消息可靠性怎 么保证?
使用一个消息队列,其实就分为三 大块:生产者、中间件、消费者,所以要保证消息就是保证三个环节都不能丢失数据。

消息生产阶段:生产者会不会丢消息,取决于生产者对于异常情况的处理是否合 理。从消息被
生产出来,然后提交给 MQ 的过程中,只要能正常收到 ( MQ 中间件) 的 ack 确认响应,就
表示发送成功,所以只要处理好返回值和异常,如果返回异常则进行消息重发,那么这个阶段
是不会出现消息丢失的。
消息存储阶段:RabbitMQ 或 Kafka 这类专业 的队列中间件,在使用时是 部署一个集群,生产
者在发布消息时,队列中间件通常会写「多个节点」,也就是有多个副本,这样一来,即便其中
一个节点挂了,也能保证集群的数据不丢 失。
消息消费阶段:消费者接收消息+消息处理之后,才回复 ack 的话,那么消息阶段的消息不会丢
失。不能收到消息就回 ack ,否则可能消息处理中途挂掉 了,消息就丢失了。
Kafka 和 RocketMQ 消息确认机制有什么不 同?
Kafka 的消息确认机制有三种:0,1,-1 :
ACK=0 :这是最不可靠的模式。生产者在发送消息后不会等待来自服务器的确认。这意味着消息可能会在发送之后丢失,而生产者将无法知道它是否成功到 达服务器。
ACK=1 :这是默认模式,也是一种折衷方式。在这种模式下,生产者会在消息发送后等待来自分区领导者(leader )的确认,但不会等待所有副本(replicas )的确认。这意味着只要消息被
写入分区领导者,生产者就会收到确认。如果分区领导者成功写入消息,但在同步到所有副本
之前宕机,消息可能会丢失。
ACK=-1 :这是最可靠的模式。在这种模式下,生产者会在消息发送后等待所有副本的确认。只有在所有副本都成功写入消息后,生产者才会收到确认。这确保了消息的可靠性,但会 导致更
长的延迟。
RocketMQ 提供了三 种消息发送方式:同步发送、异步发送和单向发送:
同步发送:是指消息发送方发出一条消息后,会在收到服务端同步响应之后才发下一条消息的
通讯方式。应用场景非常广泛,例如重要通知邮件、报名短信通知、营销短信系统等。
异步发送:是指发送方发出一条消息后,不等服务端返回响应,接着发送下一条消息的通讯方
式,但是需要实现异步发送回调接口(SendCallback )。消息发送方在发送了一条消息后,不需
要等待服务端响应即可发送第二条消息。发送方通过回调接口接收服务端响应,并处理响应结
果。适用于链路耗时较长,对响应时间较为敏感的业务场景,例如,视频上传后通知启动转码
服务,转码完成后通知推送转码结果等。
单向发送:发送方只负责 发送消息,不等待服务端返回响应且没有回调函数触发,即只发送请
求不等待应答。此方式发送消息的过程耗时非常短,一般在微秒级别。适用于某些耗时非常
短,但对可靠性要求并不高的场景,例如日志收集。
Kafka 和 RocketMQ 的 broker 架构有 什么 区别
Kafka 的 broker 架构 :Kafka 的 broker 架构 采用了分布式的设计 ,每个 Kafka broker 是一个独
立的服务实例,负责 存储和处理一部分消息数据。Kafka 的 topic 被分区存储在不同的 broker
上,实现了水平扩展和高可用性。
RocketMQ 的 broker 架构 :RocketMQ 的 broker 架构 也是分布式的,但是每个 RocketMQ broker 有主从之 分,一个主 节点和多个从 节点组成一个 broker 集群。主节点负责 消息的写入和
消费者的拉取,从节点负责 消息的复制和消费者的负载均衡,提高了消息的可靠性和可 用性。
其他
手撕:字符串乘 法,输出 2 的 1000 次方
反问:流程,业务
