瀏覽代碼

修改了kafka的面经笔记

seamew 1 年之前
父節點
當前提交
cf77857cbe

二進制
面经/问答/assets/image-20230524101601923.png


二進制
面经/问答/assets/image-20230524101812043.png


二進制
面经/问答/assets/image-20230524103611405.png


+ 21 - 6
面经/问答/kafka.md

@@ -7,23 +7,38 @@ kafka的存储方案是**顺序追加写日志 + 稀疏哈希索引**
 ![image-20230519214345638](assets/image-20230519214345638.png)
 ![image-20230519214345638](assets/image-20230519214345638.png)
 
 
 1. kafka 中消息是以主题 Topic 为基本单位进行归类的,这里的 Topic 是逻辑上的概念,实际上在磁盘存储是根据分区 Partition 存储的, 即每个 Topic 被分成多个 Partition,分区 Partition 的数量可以在主题 Topic 创建的时候进行指定。
 1. kafka 中消息是以主题 Topic 为基本单位进行归类的,这里的 Topic 是逻辑上的概念,实际上在磁盘存储是根据分区 Partition 存储的, 即每个 Topic 被分成多个 Partition,分区 Partition 的数量可以在主题 Topic 创建的时候进行指定。
-
 2. Partition 分区主要是为了解决 Kafka 存储的水平扩展问题而设计的, 如果一个 Topic 的所有消息都只存储到一个 Kafka Broker上的话, 对于 Kafka 每秒写入几百万消息的高并发系统来说,这个 Broker 肯定会出现瓶颈, 故障时候不好进行恢复,所以 Kafka 将 Topic 的消息划分成多个 Partition, 然后均衡的分布到整个 Kafka Broker 集群中。
 2. Partition 分区主要是为了解决 Kafka 存储的水平扩展问题而设计的, 如果一个 Topic 的所有消息都只存储到一个 Kafka Broker上的话, 对于 Kafka 每秒写入几百万消息的高并发系统来说,这个 Broker 肯定会出现瓶颈, 故障时候不好进行恢复,所以 Kafka 将 Topic 的消息划分成多个 Partition, 然后均衡的分布到整个 Kafka Broker 集群中。
-
 3. Partition 分区内每条消息都会被分配一个唯一的消息 id,即我们通常所说的 偏移量 Offset, 因此 kafka 只能保证每个分区内部有序性,并不能保证全局有序性。
 3. Partition 分区内每条消息都会被分配一个唯一的消息 id,即我们通常所说的 偏移量 Offset, 因此 kafka 只能保证每个分区内部有序性,并不能保证全局有序性。
-
 4. 然后每个 Partition 分区又被划分成了多个 LogSegment,这是为了防止 Log 日志过大,Kafka 又引入了日志分段(LogSegment)的概念,将 Log 切分为多个 LogSegement,相当于一个巨型文件被平均分割为一些相对较小的文件,这样也便于消息的查找、维护和清理。这样在做历史数据清理的时候,直接删除旧的 LogSegement 文件就可以了。
 4. 然后每个 Partition 分区又被划分成了多个 LogSegment,这是为了防止 Log 日志过大,Kafka 又引入了日志分段(LogSegment)的概念,将 Log 切分为多个 LogSegement,相当于一个巨型文件被平均分割为一些相对较小的文件,这样也便于消息的查找、维护和清理。这样在做历史数据清理的时候,直接删除旧的 LogSegement 文件就可以了。
-
 4. Log 日志在物理上只是以文件夹的形式存储,而每个 LogSegement 对应磁盘上的一个日志文件和两个索引文件,以及可能的其他文件(比如以".snapshot"为后缀的快照索引文件等)
 4. Log 日志在物理上只是以文件夹的形式存储,而每个 LogSegement 对应磁盘上的一个日志文件和两个索引文件,以及可能的其他文件(比如以".snapshot"为后缀的快照索引文件等)
 
 
+/index文件中会生成三份文件XX.log,XX.index,XX.timeindex,日志是分块存储的,时间戳索引从XX.timeindex中找到对应的offset,再从XX.index中索引XX.log。
+
 ### 页缓存
 ### 页缓存
 
 
 页缓存相对来说比较简单,页缓存在操作系统层面是保存数据的一个基本单位,Kafka 避免使用 JVM,直接使用操作系统的页缓存特性提高处理速度,进而避免了JVM GC 带来的性能损耗。
 页缓存相对来说比较简单,页缓存在操作系统层面是保存数据的一个基本单位,Kafka 避免使用 JVM,直接使用操作系统的页缓存特性提高处理速度,进而避免了JVM GC 带来的性能损耗。
 
 
 ### 零拷贝
 ### 零拷贝
 
 
-
+kafka就采用零拷贝技术来消费数据
 
 
 ### 批量操作
 ### 批量操作
 
 
-在 kafka 中页提高了大量批处理的 API ,可以对数据进行统一的压缩合并,通过更小的数据包在网络中进行数据发送,再进行后续处理,这在大量数据处理中,效率提高是非常明显的。
+在 kafka 中页提高了大量批处理的 API ,可以对数据进行统一的压缩合并,通过更小的数据包在网络中进行数据发送,再进行后续处理,这在大量数据处理中,效率提高是非常明显的。
+
+## kafka如何保证顺序消费
+
+1. 1 个 Topic 只对应一个 Partition。
+2. (推荐)发送消息的时候指定 key/Partition。
+
+## Kafka 如何保证消息不重复消费
+
+**kafka 出现消息重复消费的原因:**
+
+- 服务端侧已经消费的数据没有成功提交 offset(根本原因)。
+- Kafka 侧 由于服务端处理业务时间长或者网络链接等等原因让 Kafka 认为服务假死,触发了分区 rebalance。
+
+**解决方案:**
+
+* 消费消息服务做幂等校验,比如 Redis 的 set、MySQL 的主键等天然的幂等功能。这种方法最有效。
+* 将 **`enable.auto.commit`** 参数设置为 false,关闭自动提交,开发者在代码中手动提交 offset。

+ 85 - 0
面经/问答/操作系统.md

@@ -189,3 +189,88 @@ TLB即为页表缓存、转址旁路缓存、快表等。有了 TLB 后,那么
 1. **静态分配策略**:一个进程必须在执行前就申请到它所需要的全部资源
 1. **静态分配策略**:一个进程必须在执行前就申请到它所需要的全部资源
 2. **层次分配策略**:一个进程得到某一次的一个资源后,它只能再申请较高一层的资源
 2. **层次分配策略**:一个进程得到某一次的一个资源后,它只能再申请较高一层的资源
 
 
+## 零拷贝
+
+**传统的拷贝**
+
+![image-20230524103611405](assets/image-20230524103611405.png)
+
+**一共需要2次CPU拷贝,很消耗时间。**
+
+
+
+零拷贝(Zero-Copy)是一种 `I/O` 操作优化技术,可以快速高效地将数据从文件系统移动到网络接口,而不需要将其从内核空间复制到用户空间。
+
+技术的核心就要是减少CPU占用和上下文切换
+
+它主要由以下4点技术结合实现:
+
+### 1. DMA技术
+
+DMA(Direct Memory Access)是一种硬件实现的技术,能够直接将数据从设备传输到内存中,而无需CPU参与其中。
+
+### 2. 缓冲区技术
+
+在实现零拷贝时,需要使用多个缓冲区来存储数据。通过缓冲区技术可以实现不同层级的内存之间的数据传输。
+
+### 3. 文件映射技术
+
+文件映射技术可以将文件或磁盘块映射到内存空间中,使得应用程序可以直接访问这些文件或磁盘块,从而实现零拷贝。
+
+**mmap + write**
+
+![image-20230524101601923](assets/image-20230524101601923.png)
+
+`mmap()` 系统调用函数会直接把内核缓冲区里的数据「**映射**」到用户空间,这样,操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。
+
+具体过程如下:
+
+- 应用进程调用了 `mmap()` 后,DMA 会把磁盘的数据拷贝到内核的缓冲区里。接着,应用进程跟操作系统内核「共享」这个缓冲区;
+- 应用进程再调用 `write()`,操作系统直接将内核缓冲区的数据拷贝到 socket 缓冲区中,这一切都发生在内核态,由 CPU 来搬运数据;
+- 最后,把内核的 socket 缓冲区里的数据,拷贝到网卡的缓冲区里,这个过程是由 DMA 搬运的。
+
+但这还不是最理想的零拷贝,因为仍然需要通过 CPU 把内核缓冲区的数据拷贝到 socket 缓冲区里,而且仍然需要 4 次上下文切换,因为系统调用还是 2 次
+
+### 4. 网络协议技术
+
+网络协议技术可以帮助实现在网络上进行零拷贝传输,例如在TCP/IP协议栈中使用sendfile系统调用。
+
+**sendfile**
+
+![image-20230524101812043](assets/image-20230524101812043.png)
+
+- 第一步,通过 DMA 将磁盘上的数据拷贝到内核缓冲区里;
+- 第二步,缓冲区描述符和数据长度传到 socket 缓冲区,这样网卡的 SG-DMA 控制器就可以直接将内核缓存中的数据拷贝到网卡的缓冲区里,此过程不需要将数据从操作系统内核缓冲区拷贝到 socket 缓冲区中,这样就减少了一次数据拷贝;
+
+## PageCache 有什么作用?
+
+**零拷贝使用了 PageCache 技术**,***PageCache\***其实本质上就是一种**磁盘高速缓存**,通过 DMA 把磁盘里的数据搬运到内存里,这样就可以用读内存替换读磁盘。
+
+**PageCache 来缓存最近被访问的数据**,当空间不足时淘汰最久未被访问的缓存。所以,读磁盘数据的时候,优先在 PageCache 找,如果数据存在则可以直接返回;如果没有,则从磁盘中读取,然后缓存 PageCache 中。
+
+还有一点,读取磁盘数据的时候,需要找到数据所在的位置,但是对于机械磁盘来说,就是通过磁头旋转到数据所在的扇区,再开始「顺序」读取数据,但是旋转磁头这个物理动作是非常耗时的,为了降低它的影响,**PageCache 使用了「预读功能」**。
+
+比如,假设 read 方法每次只会读 `32 KB` 的字节,虽然 read 刚开始只会读 0 ~ 32 KB 的字节,但内核会把其后面的 32~64 KB 也读取到 PageCache,这样后面读取 32~64 KB 的成本就很低,如果在 32~64 KB 淘汰出 PageCache 前,进程读取到它了,收益就非常大。
+
+所以,PageCache 的优点主要是两个:
+
+- 缓存最近被访问的数据;
+- 预读功能;
+
+这两个做法,将大大提高读写磁盘的性能。
+
+## 大文件传输用什么方式实现?
+
+**在传输大文件(GB 级别的文件)的时候,PageCache 会不起作用,那就白白浪费 DMA 多做的一次数据拷贝,造成性能的降低,即使使用了 PageCache 的零拷贝也会损失性能**
+
+**在高并发的场景下,针对大文件的传输的方式,应该使用「异步 I/O + 直接 I/O」来替代零拷贝技术**。
+
+直接 I/O 应用场景常见的两种:
+
+- 应用程序已经实现了磁盘数据的缓存,那么可以不需要 PageCache 再次缓存,减少额外的性能损耗。在 MySQL 数据库中,可以通过参数设置开启直接 I/O,默认是不开启;
+- 传输大文件的时候,由于大文件难以命中 PageCache 缓存,而且会占满 PageCache 导致「热点」文件无法充分利用缓存,从而增大了性能开销,因此,这时应该使用直接 I/O。
+
+另外,由于直接 I/O 绕过了 PageCache,就无法享受内核的这两点的优化:
+
+- 内核的 I/O 调度算法会缓存尽可能多的 I/O 请求在 PageCache 中,最后「**合并**」成一个更大的 I/O 请求再发给磁盘,这样做是为了减少磁盘的寻址操作;
+- 内核也会「**预读**」后续的 I/O 请求放在 PageCache 中,一样是为了减少对磁盘的操作;