引言
ActiveMQ在不提供持久化的情况下,数据保存在内存中,一旦应用崩溃或者重启之后,数据都将会丢失,这显然在大部分情况下是我们所不希望的。对此ActiveMQ提供了两种持久化方式以供选择。
kahaDB
kahaDB是一个基于文件,支持事务的、可靠,高性能,可扩展的消息存储器,目前是activeMQ默认的持久化方式,配置也十分简单
xml
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
以上配置是将存储目录设置为${activemq.data}/kahadb
。
存储目录下文件说明:
-
db.data
:索引文件,本质上是BTree
的实现,存储到了db-*.log消息文件的索引。 -
db.redo
:用来进行数据恢复的redo文件 -
db-*.log
:存储消息内容的文件,包括消息元数据、订阅关系、事务等数据。
lock
:表示已启动一个实例。
kahaDB配置支持的参数:
参数 | 默认值 | 说明 |
---|---|---|
indexWriteBatchSize | 1000 | 当缓存中更新的索引到达1000时,将数据同步到磁盘中,数据是批量同步的。 |
indexCacheSize | 10000 | 在内存中最多分配多个页面来缓存索引。缓存的索引越多,命中的概率就越大,检索的效率就越高 |
journalMaxFileLength | 33554432 | 默认值32MB,配置单个消息文件的大小,超过一定大小以后重新创建一个新的文件进行保存。 |
enableJournalDiskSyncs | true | 表示采用同步写磁盘,即消息先存储到磁盘后再向Producer返回ACK |
cleanupInterval | 30000 | 当消息被消息者成功消费之后,Broker就可以将消息删除的时间间隔。 |
checkpointInterval | 5000 | 每隔5s将内存中的index缓存更新到磁盘文件中。 |
底层实现
从上图中可以看出:图中各个部分与KahaDB配置的存储目录下的文件是一 一对应的。
①在内存(cache)中的那部分B-Tree是Metadata Cache
通过将索引缓存到内存中,可以加快查询的速度(quick retrival of message data)。但是需要定时将 Metadata Cache
与 Metadata Store
同步。
**这个同步过程就称为:check point。**由checkpointInterval
选项 决定每隔多久时间进行一次checkpoint
操作。
②BTree Indexes
则是保存在磁盘上的,称为Metadata Store
,它对应于文件db.data
,它就是对Data Logs
以B树的形式 索引。有了它,Broker(消息服务器)可以快速地重启恢复,因为它是消息的索引,根据它就能恢复出每条消息的location
。
如果Metadata Store
被损坏,则只能扫描整个Data Logs来重建B树了,这个过程是很复杂且缓慢的。
③Data Logs
则对应于文件 db-*.log
,默认是32MB
Data Logs
以日志形式存储消息,它是生产者生产的数据的真正载体。
The data logs are used to store data in the form of journals,
where events of all kinds---messages, acknowledgments, subscriptions, subscription cancellations, transaction boundaries, etc.
---are stored in a rolling log
④Redo Log
则对应于文件 db.redo
redo log
的原理用到了"Double Write
"。关于"Double Write"可参考
简要记录下自己的理解:因为磁盘的页大小与操作系统的页大小不一样,磁盘的页大小一般是16KB,而OS的页大小是4KB。而数据写入磁盘是以磁盘页大小为单位进行的,即一次写一个磁盘页大小,这就需要4个OS的页大小(4*4=16)。如果在写入过程中出现故障(突然断电)就会导致只写入了一部分数据(partial page write)
而采用了"Double Write"之后,将数据写入磁盘时,先写到一个Recovery Buffer中,然后再写到真正的目的文件中。在ActiveMQ的源码PageFile.java
中有相应的实现。
扩展知识:Linux中的日志文件系统:因为Linux的 ext文件系统采用索引节点来存储文件的元数据,每次数据写入磁盘之后,需要更新索引节点表。而写入磁盘与更新索引节点表并不是"原子操作",比如,在数据写入磁盘后,系统发生故障,之前写入的数据就再也找不到了。
因此,日志文件系统给Linux系统增加了一层安全性:数据写入存储设备之前,先将数据(或者只将索引节点信息写日志)写入到临时文件中,该临时文件称日志。如果在数据写入时发生故障,还可以通过日志来进行一定的恢复。
附录
参考: