放荡的人生|面试题详解:Kafka为什么能那么快的6个原因( 二 )


“很久很久以前就有人做过基准测试:《每秒写入2百万(在三台廉价机器上)》
由于磁盘有限 , 不可能保存所有数据 , 实际上作为消息系统 Kafka 也没必要保存所有数据 , 需要删除旧的数据 。 又由于顺序写入的原因 , 所以 Kafka 采用各种删除策略删除数据的时候 , 并非通过使用“读 - 写”模式去修改文件 , 而是将 Partition 分为多个 Segment , 每个 Segment 对应一个物理文件 , 通过删除整个文件的方式去删除 Partition 内的数据 。 这种方式清除旧数据的方式 , 也避免了对文件的随机写操作 。
3. 充分利用 Page Cache“引入 Cache 层的目的是为了提高 Linux 操作系统对磁盘访问的性能 。 Cache 层在内存中缓存了磁盘上的部分数据 。 当数据的请求到达时 , 如果在 Cache 中存在该数据且是最新的 , 则直接将数据传递给用户程序 , 免除了对底层磁盘的操作 , 提高了性能 。 Cache 层也正是磁盘 IOPS 为什么能突破 200 的主要原因之一 。
在 Linux 的实现中 , 文件 Cache 分为两个层面 , 一是 Page Cache , 另一个 Buffer Cache , 每一个 Page Cache 包含若干 Buffer Cache 。 Page Cache 主要用来作为文件系统上的文件数据的缓存来用 , 尤其是针对当进程对文件有 read/write 操作的时候 。 Buffer Cache 则主要是设计用来在系统对块设备进行读写的时候 , 对块进行数据缓存的系统来使用 。
使用 Page Cache 的好处:

  • I/O Scheduler 会将连续的小块写组装成大块的物理写从而提高性能
  • I/O Scheduler 会尝试将一些写操作重新按顺序排好 , 从而减少磁盘头的移动时间
  • 充分利用所有空闲内存(非 JVM 内存) 。 如果使用应用层 Cache(即 JVM 堆内存) , 会增加 GC 负担
  • 读操作可直接在 Page Cache 内进行 。 如果消费和生产速度相当 , 甚至不需要通过物理磁盘(直接通过 Page Cache)交换数据
  • 如果进程重启 , JVM 内的 Cache 会失效 , 但 Page Cache 仍然可用
Broker 收到数据后 , 写磁盘时只是将数据写入 Page Cache , 并不保证数据一定完全写入磁盘 。 从这一点看 , 可能会造成机器宕机时 , Page Cache 内的数据未写入磁盘从而造成数据丢失 。 但是这种丢失只发生在机器断电等造成操作系统不工作的场景 , 而这种场景完全可以由 Kafka 层面的 Replication 机制去解决 。 如果为了保证这种情况下数据不丢失而强制将 Page Cache 中的数据 Flush 到磁盘 , 反而会降低性能 。 也正因如此 , Kafka 虽然提供了 flush.messages 和 flush.ms 两个参数将 Page Cache 中的数据强制 Flush 到磁盘 , 但是 Kafka 并不建议使用 。
4. 零拷贝技术Kafka 中存在大量的网络数据持久化到磁盘(Producer 到 Broker)和磁盘文件通过网络发送(Broker 到 Consumer)的过程 。 这一过程的性能直接影响 Kafka 的整体吞吐量 。
“操作系统的核心是内核 , 独立于普通的应用程序 , 可以访问受保护的内存空间 , 也有访问底层硬件设备的权限 。
为了避免用户进程直接操作内核 , 保证内核安全 , 操作系统将虚拟内存划分为两部分 , 一部分是内核空间(Kernel-space) , 一部分是用户空间(User-space) 。
传统的 Linux 系统中 , 标准的 I/O 接口(例如read , write)都是基于数据拷贝操作的 , 即 I/O 操作会导致数据在内核地址空间的缓冲区和用户地址空间的缓冲区之间进行拷贝 , 所以标准 I/O 也被称作缓存 I/O 。 这样做的好处是 , 如果所请求的数据已经存放在内核的高速缓冲存储器中 , 那么就可以减少实际的 I/O 操作 , 但坏处就是数据拷贝的过程 , 会导致 CPU 开销 。
我们把 Kafka 的生产和消费简化成如下两个过程来看[2]:
  1. 网络数据持久化到磁盘 (Producer 到 Broker)