为什么rocketmq读写都是在磁盘上性能还这么好?

一、前置知识

1、什么是Linux的用户空间与内核空间

Linux 操作系统和驱动程序运行在内核空间,应用程序(java程序和jvm)运行在用户空间。用户空间不能直接访问硬件设备,需要通过内核空间间接访问,网卡、磁盘、显卡等。

2、什么是页缓存(pagecache)

页缓存是操作系统用来作为磁盘的一种缓存,减少磁盘的I/O操作。
使用pagecache时:

  • 读:
    进程发起read()请求,首先会检查请求的数据是否缓存到了page cache中,如果有,那么直接从内存中读取,不需要访问磁盘,这被称为cache命中(cache hit)。如果cache中没有请求的数据,即cache未命中(cache miss),就必须从磁盘中读取数据。然后内核将读取的数据缓存到cache中,这样后续的读请求就可以命中cache了。page可以只缓存一个文件部分的内容,不需要把整个文件都缓存进来。(先从page cache中读,没有就从磁盘读(未命中)。然后将未命中的数据,读到page cache中(提升下次读的命中率))
  • 写:
    进程发起write()请求,同样是直接往cache中写入,后备存储中的内容不会直接更新。内核会将被写入的page标记为dirty,并将其加入dirty list中。内核会周期性地将dirty list中的page写回到磁盘上,从而使磁盘上的数据和内存中缓存的数据一致。

3、什么是DMA

操作系统:DMA

二、rocketmq读写性能高的原因

1、顺序读写

对磁盘读写时,如果是顺序读写,那么磁头几乎不用换道,或者换道的时间很短。读写效率会提高很多。(rocketmq 写是顺序写,读并不是,但是它提高的读机制使得读类似顺序读
rocketmq 将消息写入CommitLog 文件夹中的mappedFile文件(这个文件超过1G后会新建一个)时,是按照顺序写入的。不论消息属于哪个 Topic 的哪个 Queue 。都会按照顺序依次存储到CommitLog 文件夹中的mappedFile文件。

2、传统读取文件然后进行网络传输

在这里插入图片描述

3、使用mmap技术

mmap将一个文件或者其它对象映射进内存。mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

因为已经将文件映射到内存,所以就减少了一次cpu拷贝

在这里插入图片描述

通过代码实现mmap

package com.lihua.rocketmq.zero_copy;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 零拷贝——mmap
 * @author 15594
 */
public class MMap {
    public static void main(String[] args) throws IOException {
        File file = new File("C:\\Users\\15594\\IdeaProjects\\rocketmq\\src\\main\\java\\com\\lihua\\rocketmq","1.txt");
        System.out.println(file.getAbsolutePath());
        if (file.isFile()){
            System.out.println(file.isFile());
            file.createNewFile();
        }
        RandomAccessFile randomAccessFile = new RandomAccessFile(file,"rw");
        //映射文件的大小,rocketmq里面的mappedFile是1G
        int len  = 2048;
        //将指定的“1”文件进行内存映射
        MappedByteBuffer mmap = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, len);
        //写入数据
        mmap.put("1".getBytes());
        mmap.put("2".getBytes());
        mmap.put("3".getBytes());
        //刷入磁盘(刷盘)
        mmap.flip();
        //读取数据
        byte[] b = new byte[10];
        mmap.get(b,0,3);
        System.out.println(new String(b));

    }

}

4、Kafka使用的sendfile零拷贝技术

在这里插入图片描述

5、rockermq性能高的原因

  1. mmap零拷贝:
    RocketMQ对文件的读写操作是通过mmap零拷贝进行的,将对文件的操作转化为直接对内存地址进行操作,从而极大地提高了文件的读写效率。
  2. 预读取机制:
    consumequeue中的数据是顺序存放的,还引入了PageCache的预读取机制,使得对consumequeue文件的读取几乎接近于内存读取,即使在有消息堆积情况下也不会影响性能。
    若用户要读取数据,其首先会从PageCache中读取,若没有命中,则OS在从物理磁盘上加载该数据到PageCache的同时,也会顺序对其相邻数据块中的数据进行预读取
  3. 文件预分配:
    CommitLog 的大小默认是1G,当超过大小限制的时候需要准备新的文件,而 RocketMQ 就起了一个后台线程 AllocateMappedFileService,不断的处理 AllocateRequest,AllocateRequest其实就是预分配的请求,会提前准备好下一个文件的分配,防止在消息写入的过程中分配文件,产生抖动。

6、如何解决commitlog文件的随机读

RocketMQ中可能会影响性能的是对commitlog文件的读取。因为对commitlog文件来说,读取消息时会产生大量的随机访问,而随机访问会严重影响性能。不过,如果选择合适的系统IO调度算法,比如设置调度算法为Deadline(采用SSD固态硬盘的话),随机读的性能也会有所提升。

三、为什么rocketmq不用sendfile技术

因为使用sendfile 在数据传输过程中是不会进入用户进程的(也就是不会进入java程序),不进入java程序,那么就无法对数据进行操作(修改、排序)。

四、参考

rocker工作原理

参考yes的Kafka 和 RocketMQ 底层存储之那些你不知道的事

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Redis之所以读写效率高,主要有以下几个原因: 1. 纯内存操作:Redis的数据都存储在内存中,而内存的读写速度比磁盘快得多,因此Redis的读写效率很高。 2. 单线程模型:虽然Redis是单线程的,但是它采用了多路复用技术,可以同时处理多个客户端请求,从而提高了并发性能。 3. 高效的数据结构:Redis支持多种数据结构,如字符串、哈希表、列表、集合、有序集合等,这些数据结构都是经过优化的,可以快速地进行读写操作。 4. 异步IO:Redis采用了异步IO技术,可以在等待IO操作的同时处理其他请求,从而提高了系统的响应速度。 综上所述,Redis之所以能够在单线程的情况下保持高效的读写性能,主要是因为它采用了多种优化技术,包括纯内存操作、单线程模型、高效的数据结构和异步IO等。 ### 回答2: Redis之所以能够实现高效的读写操作,即使是在单线程的情况下,主要有以下几个原因: 首先,Redis采用了基于内存的数据存储方式,相比于传统的磁盘存储方式,内存读写的速度快得多。在内存中进行数据读写可以极大的提高读写效率,因为内存的存取速度远远高于磁盘。 其次,Redis通过使用数据结构的操作来实现高效的读写。例如,通过使用哈希表来存储键值对数据,可以在O(1)的时间复杂度内进行快速的数据读写。此外,Redis还支持其他一些高效的数据结构,如链表、有序集合等,使得数据的操作更加灵活高效。 另外,Redis采用了非阻塞的I/O模型,利用了操作系统的事件通知机制,可以在单个线程中处理大量的并发请求。当有新的数据需要读取或写入时,Redis不需要依赖额外的线程来处理,而是通过事件循环机制,在单个线程内高效地处理所有的请求,避免了线程切换的开销。 此外,Redis还采用了多路复用技术,通过一个线程来监听多个网络连接,避免了为每个连接创建一个新线程的开销。这样,即使是在大量的并发请求下,Redis也能够快速地响应客户端的读写操作。 综上所述,尽管Redis是单线程的,但通过利用内存存储、高效的数据结构操作、非阻塞的I/O模型和多路复用技术等手段,实现了高效的数据读写操作,使得Redis能够在单个线程中处理大量的并发请求,从而提高了读写效率。 ### 回答3: Redis是一种基于内存的键值对存储系统,它之所以能够在单线程情况下实现高效的读写操作,是因为它具有以下几个优势: 首先,Redis采用了异步的I/O模型。Redis在高效处理大量并发请求时,通过使用多路复用技术,可以同时管理多个客户端连接,并通过异步的方式处理这些连接上的读写请求。这种异步I/O模型能够有效地降低网络开销和系统负载,提高读写操作的响应速度。 其次,Redis使用了高效的数据结构。Redis支持各种丰富的数据结构,如字符串、哈希、列表、集合、有序集合等。这些数据结构都是经过精心设计和优化的,能够在内存中高效地存储和访问数据。例如,Redis使用跳表和压缩列表等数据结构来实现有序集合和列表,这些数据结构在执行插入、删除和查找等操作时都具有较高的效率。 此外,Redis还采用了内存管理和持久化机制等多种优化策略。Redis通过对内存的精细管理,使用各种内存回收策略和压缩算法,可以最大限度地提高内存的利用率。同时,Redis还支持数据的持久化,可以将内存中的数据写入磁盘并在重启后重新加载,保证数据的持久性和可靠性。这样一来,Redis可以在不丢失数据的情况下,通过将热数据存储在内存中,快速响应读写操作。 总之,Redis之所以能够在单线程下实现高效的读写操作,主要得益于它采用了异步的I/O模型、高效的数据结构、内存管理和持久化机制等多种优化策略。这些优势使得Redis能够在处理大量并发请求时,保持良好的性能表现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值