(1). 所有数据单独存储到一个 Commit Log,完全顺序写,随机读。
(2). 对最终用户展现的队列实际只存储消息在 Commit Log 的位置信息,并且串行方式刷盘。
这样做的好处如下:
(1). 队列轻量化,单个队列数据量非常少。
(2). 对磁盘的访问串行化,避免磁盘竟争,不会因为队列增加导致 IOWAIT 增高。
每个方案都有缺点,它的缺点如下:
(1). 写虽然完全是顺序写,但是读却变成了完全的随机读。
(2). 读一条消息,会先读 Consume Queue,再读 Commit Log,增加了开销。
(3). 要保证 Commit Log 与 Consume Queue 完全的一致,增加了编程的复杂度。
以上缺点如何克服:
(1). 随机读, 尽可能让读命中 PAGECACHE, 减少 IO 读操作, 所以内存越大越好。 如果系统中堆积的消息过多,读数据要访问磁盘会不会由于随机读导致系统性能急剧下降,答案是否定的。
a) 访问 PAGECACHE 时,即使只访问 1k 的消息,系统也会提前预读出更多数据,在下次读时,就可能命中内存。
b) 随机访问 Commit Log 磁盘数据, 系统 IO 调度算法设置为 NOOP 方式,会在一定程度上将完全的随机读变成顺序跳跃方式,而顺序跳跃方式读较完全的随机读性能会高 5 倍以上,可参见以下针对各种 IO方式的性能数据。
http://stblog.baidu-tech.com/?p=851
另外 4k 的消息在完全随机访问情况下,仍然可以达到 8K 次每秒以上的读性能。
(2). 由于 Consume Queue 存储数据量极少,而且是顺序读,在 PAGECACHE 预读作用下,Consume Queue 的读性能几乎与内存一致,即使堆积情况下。所以可认为 Consume Queue 完全不会阻碍读性能。
(3). Commit Log 中存储了所有的元信息,包含消息体,类似于 Mysql、Oracle 的 redolog,所以只要有 CommitLog 在,Consume Queue 即使数据丢失,仍然可以恢复出来。