一个95后开发者关于消息发送的实践
公众号:杨建荣的学习笔记 · 作者:杨建荣 · 发布:2018-07-31 23:59:45 · 原文链接
这篇文章最开始投给我的时候,没有引起太多的重视,但是看了内容之后,真是被里面的细节吸引了。
这是95后开发者在阿里组织的中间件性能挑战赛里面的解题思路,我要给一个大大的赞。在内容面前,其他的形式都会略逊一筹。
-大赛背景-
第四届阿里中间件性能挑战赛是由阿里巴巴集团发起,阿里巴巴中间(Aliware)、阿里云天池联合举办,是集团唯一的工程性品牌赛事。本文是优胜奖选手参赛队“YLJZ”的解题思路,他们是来自北京邮电大学的95后开发者。
一、赛题分析与解题思路
首先需要解决索引的问题。因为线上只有8G内存,而消息共有100G大小,故消息只能放到SSD上存储。需要思考该怎样设计索引才能够快速地把消息从SSD上取出来。最快最便捷的方法就是为每条消息都建立一条索引,然而如果这样光是索引就会占了很大的空间,内存放不下,而放硬盘会增多IO次数,这是不可取的。故索引的设计有如下的两个原则或目的:
1. 尽可能地减少读取消息时所需要进行的IO次数
2. 尽可能地减少索引所占用的内存
接着需要决定硬盘读写的方式。会有如下四种:
1. 顺序写、随机读
2. 随机写、顺序读
3. 顺序写、顺序读
4. 随机写、随机读
毋庸置疑,顺序写、顺序读的方式是最快的,这样可以最大化硬盘吞吐量。然而内存有限,无法做到整个队列的消息聚合,所以读写都是顺序的方案非常难以实现。因为在第一阶段中,写数据量超级大,而在第三阶段中仅读取10%的数据,如果使用随机写必定会超时,但随机读未必会超时!最终我们选择了顺序写、随机读的方案,并且为每个队列的消息做页聚合。
决定好硬盘读写方式之后,就可以确定好优化方向:
1. 在第一阶段,数据写盘不能停,并且使写盘速度达到最大带宽。
2.在第二阶段的随机索引中,最大化IOPS,并且尽量不取出多余数据。
3.在第三阶段中,最大化IOPS,尽可能地预先读取数据,尽量避免阻塞(get)调用者线程。
二、数据存储模型
基于以上的分析,并且依照索引设计的原则和目的,我们得出如下的数据存储模型。
使用一个大文件存储数据,消息按页(4K)存储,一页只存一个队列的消息,一个队列存多页。消息的索引方式参考了Kafka存储模型,使用稀疏索引,每个队列在内存中都设置了一个Page Table,其保存消息队列对应使用的页地址和积累队列长度。每条消息存储时会在消息前边加上表示消息长度的消息头。当要检索某条消息时,先在内存中二分查询队列的Page Table得到消息所在页地址,接着把该页数据从硬盘读出来,最后根据消息头表示的长度遍历定位到消息的页内偏移从而读出消息数据。

