操作系统
腾讯 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 次⽅
反问:流程,业务
