RabbitMQ--存储机制

存储机制

不管是持久化的消息还是非持久化的消息都可以被写入到磁盘。持久化的消息在到达队列时就被写入到磁盘,并且如果可以,持久化的消息也会在内存中保存一份备份,这样可以提高一定的性能, 当内存吃紧的时候会从内存中清除。非持久化的消息一般只保存在内存中,在内存吃紧的时候会被换入到磁盘中,以节省内存空间。这两种类型的消息的落盘处理都在RabbitMQ的“持久层”中完成。

持久层是一个逻辑上的概念,实际包含两个部分:队列索引(rabbit_queue_index) 和消息存储(rabbit_msg_store)

  • rabbit_queue_index 负责维护队列中落盘消息的信息,包括消息的存储地点、是否已被交付给消费者、是否已被消费者ack等。每个队列都有与之对应的一个rabbit_queue_index。
  • rabbit_msg_store 以键值对的形式存储消息,它被所有队列共享,在每个节点中有且只有一个。

从技术层面上来说,rabbit_ msg_ store 具体还可以分为msg_store_ persistent和msg_store_transient:

  • msg_store_persistent负责持久化消息的持久化,重启后消息不会丢失;
  • msg_store_transient 负责非持久化消息的持久化,重启后消息会丢失。通常情况下,习惯性地将msg_store_persistent 和msg_store_transient看成rabbit msg_ store 这样一个整体。

消息(包括消息体、属性和headers)可以直接存储在rabbit_queue_index 中,也可以被保存在rabbit_msg_store 中。默认在$RABBITMQ HOME/var/lib/mnesia/ [email protected]$HOSTNAME/路径下包含queues、msg_ store_ persistent、 msg_ store_ _transient 这3个文件夹,其分别存储对应的信息。

RabbitMQ--存储机制

最佳的配备是较小的消息存储在rabbit_queue_index中而较大的消息存储在rabbit_msg_store 中。这个消息大小的界定可以通过queue_index_embed_msgs_below 来配置,默认大小为4096,单位为B。注意这里的消息大小是指消息体、属性及headers整体的大小。当一个消息小于设定的大小阈值时就可以存储在rabbit_ queue_index 中,这样可以得到性能上的优化。

rabbit_queue_index 中以顺序(文件名从0开始累加)的段文件来进行存储,后缀为idx 每个段文件中包含固定的SEGMENT_ENTRY_COUNT 条记录,SEGMENT ENTRY COUNT默认值为16384。 每个rabbit_queue_index 从磁盘中读取消息的时候至少要在内存中维护一个段文件,所以设置queue_index_embed_msgs_below 值的时候要格外谨慎,一点点增大也可能会引起内存爆炸式的增长。

经过rabbit_msg_store 处理的所有消息都会以追加的方式写入到文件中,当一个文件的大小超过指定的限制(file_ size_ limit) 后,关闭这个文件再创建一个新的文件以供新的消息写入。文件名(文件后缀是“.rdq”)从0开始进行累加,因此文件名最小的文件也是最老的文件。在进行消息的存储时,RabbitMQ 会在ETS ( Erlang Term Storage)表中记录消息在文件中的位置映射(Index) 和文件的相关信息(FileSummary)。

RabbitMQ--存储机制

在读取消息的时候,先根据消息的ID (msg_ id) 找到对应存储的文件,如果文件存在并且未被锁住,则直接打开文件,从指定位置读取消息的内容。如果文件不存在或者被锁住了,则发送请求由rabbit_msg_store 进行处理。

消息的删除只是从ETS表删除指定消息的相关信息,同时更新消息对应的存储文件的相关信息。执行消息删除操作时,并不立即对在文件中的消息进行删除,也就是说消息依然在文件中,仅仅是标记为垃圾数据而已。当一个文件中都是垃圾数据时可以将这个文件删除。

当检测到前后两个文件中的有效数据可以合并在一个文件中,并且所有的垃圾数据的大小和所有文件(至少有3个文件存在的情况下)的数据大小的比值超过设置的阈值GARBAGE FRACTION (默认值为0.5)时才会触发垃圾回收将两个文件合并。

执行合并的两个文件一定是逻辑上相邻的两个文件。如下图所示,执行合并时首先锁定这两个文件,并先对前面文件中的有效数据进行整理,再将后面文件的有效数据写入到前面的文件,同时更新消息在ETS表中的记录,最后删除后面的文件。
RabbitMQ--存储机制