RocketMQ消息存储-全流程解析|存储文件介绍|内存映射

  • 1.消息存储全流程解析

RocketMQ消息存储-全流程解析|存储文件介绍|内存映射

  • 2.消息写入依赖类时序

RocketMQ消息存储-全流程解析|存储文件介绍|内存映射

 

  • 3.CommitLog文件
    • 一个消息的日志文件,是如何存到磁盘中?rocketmq vs kafka
      1. Kafka:一个topic下多个Queue,消息存入到的多个Queue的对应的日志文件,每个Queue对应一个PrivateLog文件,各个Queue间的log文件是独立的。
      2. 把所有的Queue对应的消息都存储到一个log,sharelog文件中。保证消息的顺序写入。
    • 消息存储文件,所有消息主题的消息都存储在CommitLog文件中。每一条消息长度不同,消息体结构如下图。
    • producer顺序的写入commitlog文件。Consumer消费消息时,是随机读commitlog文件。
    • commitlog文件默认大小是1G。RocketMQ主要通过MappedByteBuffer对文件进行读写操作,采用MappedByteBuffer这种内存映射的方式有几个限制,其中之一是一次只能映射1.5~2G 的文件至用户态的虚拟内存,这也是为何RocketMQ默认设置单个CommitLog日志数据文件为1G的原因。
    • 文件命名格式:20位数字,默认每个文件大小1G。文件名值为本文件的起始偏移量(物理偏移量),即是1G的字节数。即第一个文件0;第二个文件1g=1024*1024*1024= 1,073,741,824Bytes,文件名即1073741824。
      1. 假设1073742827为物理偏移量(物理偏移量即全局偏移量),则其对应的相对偏移量为1003(1003=1073742827-1073741824),并且该偏移量位于第二个commitlog中。
    • 每一个commitlog文件都有一个MappedFile对应。MappedFile的管理是通过MappedFileQueue其中的字段mappedFiles(CopyOnWriteArrayList类型)存储了MappedFile。
  • 4.内存映射
    • 4.1堆外内存池
      • 为什么要使用TransientStorePool?一般有两种,有两种方式进行读写
        • (1)第一种,Mmap+PageCache的方式,读写消息都走的是pageCache,这样子读写都在pagecache里面不可避免会有锁的问题,在并发的读写操作情况下,会出现缺页中断降低,内存加锁,污染页的回写。异步刷盘的第一种方式和同步刷盘方式
        • (2)第二种,DirectByteBuffer(堆外内存)+PageCache的两层架构方式,这样子可以实现读写消息分离,写入消息时候写到的是DirectByteBuffer——堆外内存中,读消息走的是PageCache(对于,DirectByteBuffer是两步刷盘,一步是刷到PageCache,还有一步是刷到磁盘文件中),带来的好处就是,避免了内存操作的很多容易堵的地方,降低了时延,比如说缺页中断降低,内存加锁,污染页的回写。
    • 4.2ByteBuffer
      • Java NIO中有三种ByteBuffer
        • HeapByteBuffer:ByteBuffer.allocate()使用的就是这种缓冲区,叫堆缓冲区,因为它是在JVM堆内存的,支持GC和缓存优化。但是它不是页对齐的,也就是说如果要使用JNI的方式调用native代码时,JVM会先将它拷贝到页对齐的缓冲空间。
        • DirectByteBuffer:ByteBuffer.allocateDirect()方法被调用时,JVM使用C语言的malloc()方法分配堆外内存。由于不受JVM管理,这个内存空间是页对齐的且不支持GC,和native代码交互频繁时使用这种缓冲区能提高性能。不过内存分配和销毁的事就要靠你自己了。
        • MappedByteBuffer:FileChannel.map()调用返回的就是这种缓冲区,这种缓冲区用的也是堆外内存,本质上其实就是对系统调用mmap()的封装,以便通过代码直接操纵映射物理内存数据。