RocketMQ消息存储在本地文件系统中,默认在当前用户家目录下的store目录中
-rw-r--r--. 1 root root 0 Jul 13 08:28 abort
-rw-r--r--. 1 root root 4096 Jul 13 08:28 checkpoint
drwxr-xr-x. 2 root root 34 Apr 30 08:30 commitlog
drwxr-xr-x. 2 root root 280 Jul 13 08:28 config
drwxr-xr-x. 9 root root 199 Apr 29 18:30 consumequeue
drwxr-xr-x. 2 root root 31 Apr 29 18:14 index
-rw-r--r--. 1 root root 4 Jul 13 08:28 lock
[root@node1 store]# cd commitlog/
[root@node1 commitlog]# ll
total 56
-rw-r--r--. 1 root root 1073741824 Apr 29 18:44 00000000000000000000
又称mappedFile.当前broker中所有信息都会落盘到这些mappedFile文件中.每一个mappedFile文件大小为1G(>=1G),文件名由20位十进制数构成,表示当前文件的第一条消息的起始位置偏移量
mappedFile文件由多个消息单元组成
名称 | 描述 |
---|---|
MsgLen | 消息长度 |
PhysicalOffset | 消息物理地址 |
Body | 消息体 |
BodyLength | 消息体长度 |
Topic | 主题 |
TopicLength | 主题长度 |
BornHost | 生产者信息 |
BornTimestamp | 消息发送时间戳 |
QueueId | 消息所在队列Id |
QueueOffset | 消息在Queue中存储的偏移量 |
[root@node1 consumequeue]# ll
total 0
drwxr-xr-x. 3 root root 15 Apr 29 18:27 %RETRY%pointGroup
drwxr-xr-x. 6 root root 42 Apr 29 18:20 RMQ_SYS_TRACE_TOPIC
drwxr-xr-x. 3 root root 15 Apr 29 18:14 RMQ_SYS_TRANS_HALF_TOPIC
drwxr-xr-x. 3 root root 15 Apr 29 18:25 RMQ_SYS_TRANS_OP_HALF_TOPIC
drwxr-xr-x. 5 root root 33 Apr 29 18:27 SCHEDULE_TOPIC_XXXX
drwxr-xr-x. 3 root root 15 Apr 29 18:30 TRANS_CHECK_MAX_TIME_TOPIC
drwxr-xr-x. 4 root root 24 Apr 29 18:29 txMsg
[root@node1 consumequeue]# cd txMsg/
[root@node1 txMsg]# ll
total 0
drwxr-xr-x. 2 root root 34 Apr 29 18:29 0
drwxr-xr-x. 2 root root 34 Apr 29 18:25 2
RocketMQ为每一个topic在~/store/consumequeue中创建一个目录,目录名为topic的名称.该目录下再会为该topic的Queue创建一个目录,目录名为Queue的Id.每个目录中存放了若干个consumequeue文件.
consumequeue文件是commitlog的索引文件可以定位到具体的消息
consume文件中每一条记录成为索引条目
每一个consumequeue文件中可以包含30W个索引条目,每一个索引条目大小固定,固定20个字节
一个consumequeue文件中所有消息的topic一定相同,但是每条消息的tag可能是不同的
名称 | 描述 |
---|---|
CommitLog offset | 消息在mappedFile文件中的偏移量CommitLog Offset |
Msg length | 消息长度 |
TagHashCode | 消息Tag的hashcode值 |
pageCache机制,页缓存技术,OS对文件的缓存机制,加速文件读写. 一般来说,程序对文件的顺序读写速度几乎接近与内存读写速度,主要是因为OS使用了pagecache技术对读写访问操作进行了优化,将一部分的内存用于PageCache
RocketMQ中可能会影响性能的是对commitlog文件的读取。因为对commitlog文件来说,读取消息时会产生大量的随机访问,而随机访问会严重影响性能。不过,如果选择合适的系统IO调度算法,比如设置调度算法为Deadline(采用SSD固态硬盘的话),随机读的性能也会有所提升
RocketMQ提供了根据Key进行消息查询功能.主要是通过store目录下的index子目录中的IndexFile进行索引快速查询. 索引的数据是消息中包含了Key的消息,被发送到broker时写入,如果消息中没有包含Key,则不会写入
每个broker会包含一组indexFile,每个IndexFile都以一个时间戳命名(创建时的时间戳).每个IndexFile由:IndexHeader slots indexes索引数据.每个Indexfile包含500W个slot,每个slot可能被挂载多个Index索引单元
IndexHeader固定40字节
key的hash值%500W得到具体的slot槽位,然后将该slot的值改为index索引单元的IndexNo,根据该IndexNo可以计算出该Index单元在indexfile中的位置.
为了解决hash冲突,每个索引单元增加的preIndexNo,用于记录该索引单元下的前一个索引单元.slot中始终存放的是最新的索引单元的IndexNo,通过slot的indexNo找到最新的索引单元再通过索引单元的preIndexNo去找之前的所有Index索引单元
indexNo是一个在indexFile中的流水号,从 0 开始依次递增。即在一个indexFile中所有indexNo是以此递增的。indexNo在index索引单元中是没有体现的,其是通过indexes中依次数出来的
根据业务Key查询时,查询条件除了Key之外,还需要指定一个查询时间戳,表示查询不大于该时间戳的最新消息.此时indexFile以时间戳命名的话就可以简化查询
index文件创建时机
计算消息key的slot槽位序号 (找slot序号)
slot = hash(msg.key) % 500W
计算slot序号为n的slot在Indexfile的起始位置 (定位slot)
slot(n) = 40 + (n - 1) * 4
40: indexHeader的位置
n-1: slot位置
4 一个slot占4字节
计算indexNo为m的index在indexFile中的位置 (定位索引单元)
index(m)位置 = 40 + 500w * 4 + (m - 1) * 20 (式子3)
40: indexHeader的位置
500w * 4: 500w个slot 每个slot 4字节
(m-1)*20: m为索引单元的位置 20 一个索引单元固定20字节