ActiveMQ消息持久化

什么是消息持久化

在发送者将消息发送出去后,消息中心首先将消息存储到本地数据文件、内存数据库或者远程数据库等,而后试图将消息发送给接收者,发送成功则将消息从存储中删除,失败则继续尝试。
 
消息中心启动之后首先要检查制定的存储位置,若是有未发送成功的消息,则须要把消息发送出去。    
 

ActiveMQ持久化方式

AMQ、KahaDB、JDBC、LevelDB。

AMQ

AMQ是一种文件存储形式,它具备写入速度快和容易恢复的特色。消息存储在一个个文件中,文件的默认大小为32M,若是一条消息的大小超过了32M,那么这个值必须设置大一点。当一个存储文件中的消息已经所有被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。AMQ适用于ActiveMQ5.3以前的版本。

KahaDB

KahaDB是基于 文件的本地数据库储存形式,虽然没有AMQ的速度快,可是它具备强扩展性,恢复的时间比AMQ短,从5.4版本以后KahaDB作为默认的持久化方式。
KahaDB是一种 可嵌入式的事务性的持久化机制
 
KahaDB,主要特性有: 
一、日志形式存储消息。 
二、消息索引以B-Tree结构存储,能够快速更新 
三、彻底支持JMS事务
四、支持多种恢复机制
 
 
 
        上图展现的是KahaDB的结构图。消息存储在 基于文件的数据日志中。若是消息发送成功,变 标记为可删除的。
        系统会周期性的清除或者归档日志文件。
        消息文件的位置索引存储在内存中,这样能快速定位到。按期将内存中的 消息索引保存到metadata store中,避免大量消息未发送时,消息索引占用过多内存空间。
 
Data logs 
        数据日志中保存着消息以及目的地、订阅、事务等相关信息。这些信息在日志文件中并未按照一个特定的格式来保存,因此就须要索引各种信息,以便能快速定位到。 
Metadata cache 
        在 内存中保存日志文件中各种信息的索引,索引信息包含一个 MessageId与消息在日志文件中的偏移量的对应关系。
全部索引以B-Tree结构存在内存中,便于在一个有序的list上快速的查找,插入以及删除。内存中的消息索引会按期的保存到Metadata store中。具体时间周期能够设置checkpointInterval属性。理想状况下Metadata cache越大愈好,这样在定位消息的时候就不尽可能少的去Metadata store中获取索引了。实际能够参考db.data文件的大小来设置。indexCacheSize 即是设置缓存的大小。 
Metadata store 
        在db.data文件中保存消息日志中 消息的元数据,也是以B-Tree结构存储的,定时从Metadata cache更新数据。同时,Metadata store中也会备份一些在消息日志中存在的信息,这样可让Broker实例快速启动。即使metadata store文件被破坏或者误删除了。broker能够读取Data logs恢复过来,只是速度会相对较慢些。 
 
Metadata cache与Metadata store同步 
KahaDB提供了两种触发同步设置 
一、设定一个阀值,当Metadata cache与Metadata store中的索引不一样的数量达到这个阀值时,触发同步。indexWriteBatchSize 
即是设置这个阀值。 
二、设置一个时间周期,当时间周期到了后,无论Metadata cache与Metadata store是否不一样,都触发同步。 
经过checkpointInterval设置一个时间周期。 
 
一般为了达到更高的性能,会将indexWriteBatchSize值设置很大。只在到达checkpointInterval时间点时才同步。这样作的风险就是有可能在系统意外down机时丢失部分metadata信息。 

JDBC

能够将消息存储到数据库中,例如:Mysql、SQL Server、Oracle、DB2。
dataSource指定持久化数据库的bean,createTablesOnStartup是否在启动的时候建立数据表,默认值是true,这样每次启动都会去建立数据表了,通常是第一次启动的时候设置为true,以后改为false。
 

LevelDB

这种文件系统是从ActiveMQ5.8以后引进的,它和KahaDB很是类似,也是基于 文件的本地数据库储存形式,可是它提供比KahaDB更快的持久性。
与KahaDB不一样的是,它 不是使用传统的B-树来实现对日志数据的提早写,而是使用基于索引的LevelDB
 
 
 

与Kafka持久化对比

Kafka的存储布局很是简单。话题的每一个分区对应一个逻辑日志。物理上,一个日志为相同大小的一组分段文件。每次生产者发布消息到一个分区,代理就将消息追加到最后一个段文件中。当发布的消息数量达到设定值或者通过必定的时间后,段文件真正写入磁盘中。写入完成后,消息公开给消费者。
 
与传统的消息系统不一样,Kafka系统中存储的消息没有明确的消息Id。
 
消息经过 日志中的逻辑偏移量(只存储位置信息)来公开。这样就避免了维护配套密集寻址,用于映射消息ID到实际消息地址的随机存取索引结构的开销。消息ID是增量的,但不连续。要计算下一消息的ID,能够在其逻辑偏移的基础上加上当前消息的长度。
 
消费者始终从特定分区顺序地获取消息,若是消费者知道特定消息的偏移量,也就说明消费者已经消费了以前的全部消息。消费者向代理发出异步拉请求,准备字节缓冲区用于消费。每一个异步拉请求都包含要消费的消息偏移量。Kafka利用sendfile API高效地从代理的日志段文件中分发字节给消费者。