阿里巴巴2021第二届云原生编程挑战赛总结

针对冷热读写场景的RocketMQ存储系统设计

https://tianchi.aliyun.com/competition/entrance/531922/introduction?spm=5176.12281968.1008.4.65812448foYVfh

简单描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实现功能点

  1. 实现了一个单机的消息队列存储引擎,实现了顺序写入和随机读取的基本功能,并保证机器断电数据不丢失的持久化特性。
  2. 设计底层数据文件的存储结构,并基于topic和queueId实现文件分区。设置内存写缓冲区,实现多客户端写入聚合批量刷盘,并保证写入磁盘4KB对齐。
  3. 设计内存索引结构,并基于堆外内存、傲腾持久化内存(Aep)以及SSD设计多级缓存结构,以及相应的数据读取查询过程。基于Aep设计了实现了环形缓冲区,当写入数据量溢出时则覆盖旧数据。

项目描述

此项目是我之前参加的阿里巴巴云原生竞赛的项目,其主要内容是设计一个单机消息存储队列,实现基于topic和queueid的消息的的写入与读取功能,并且要保证机器断电数据不丢失的正确性。所以消息写入返回时,数据必须落入磁盘。同时阿里云还提供了有限的傲腾持久化内存给我们使用,所以基于多级内存、持久化内存以及固态硬盘多级存储架构,设计了索引结构以及读取过程。

技术要点

存储和索引结构

  1. 磁盘存储结构:将所有队列分组,写入5个数据文件,每个数据文件保存该组所有队列的写入数据(每条记录包括主题、队列id、和数据)。
  2. 内存合并写入缓存:每个数据文件对应一个内存写入缓冲区,该写入缓存需要加锁,因为会有多线程并发写入。该缓存区存储每条消息的主题、队列id、和数据。该写入缓存并不用于读取作用,4kb对齐刷写。
  3. AEP存储结构和AEP写入缓存:aep存储为环形存储,即设置固定的大小,当写入数据超过大小时,则从头覆盖写入。每个工作线程对应一个AEP存储和相应的AEP写入缓存(ThreadLocal),这样的好处在于避免并发问题,所有对于aep和缓存的读写操作都是顺序执行,不会有并发问题。由于是AEP环形存储结构以及固定大小的写入缓存,所以需要记录其有效数据的startoffset。当缓存没命中时,要到下一层中进行读取。
  4. 内存索引:由于有三层存储结构(内存、AEP、磁盘),所以需要设置索引结构辅助读取操作。由于数据断电不丢失,索引可以根据数据文件进行恢复,所以索引结构不需要持久化。在内存中按照topic和queueid分别存储每条消息的aepWriteBufferOffset、aepOffset、ssdOffset。

写入过程

  1. 根据topicid和queueid进行hash,获取分组编号groupid;
  2. 将消息写入当前线程的aep写入缓存,如果aep写入缓存已满,则将其刷写到aep数据文件中,再写入aep写入缓存。并添加该消息的aep写入缓存索引和aep数据文件索引。
  3. 将消息写入内存合并写入缓冲区(加锁),若缓冲区已满,则刷写到SSD(根据groupid获取对应数据文件)中并保持4K对齐,添加该消息的SSD数据文件索引,并唤醒写入阻塞的线程。否则,直接写入缓冲区,并阻塞当前线程直至数据被刷写到磁盘。

读取过程

  1. 根据topicid和queueid进行hash,获取分组编号groupid;
  2. 根据topic和queueid以及offset获取消息的对应索引(aepWriteBufferOffset、aepOffset、ssdOffset)。
  3. 读取当前线程的AEP写入缓存,如果不包含对应数据,则读取当前线程的AEP缓存,如果不包含对应数据,则读取groupid对应的SSD数据文件。

总结

  1. 每条消息共写入4次:AEP写入缓存、AEP缓存、内存合并写入缓冲区和SSD。其中AEP写入缓存、AEP缓存写入不需要加锁,应该其是线程独有(安全)的。
  2. 分组合并写入的优点是:1.多个线程的写入合并刷盘,减少了IO的次数,提高了写入效率,减少写入线程的阻塞时间。2.多个线程的写入快速积累数据到4kb倍数,提高flush效率。
  3. 数据读取按照三级缓存:AEP写入缓存、AEP缓存和SSD。

个人感想

之前一直对消息队列的存储设计并不了解,通过此次比赛,让我受益匪浅。对于一个消息队列,为了提高其并发性,需要减少加锁阻塞的消耗。所以设置工作线程池,并将所有队列进行hash分配线程,使得对于一个指定的queue,其读写操作一定是由同一个线程进行顺序操作的,这对于设置内存缓冲有极大好处,即不需要加锁。在此赛题中也是如此规定的,即一个线程固定绑定多个queue。所以对于每一个工作线程可以设置线程独有的缓冲区,在写入和读取指定queue时,对其缓冲区进行操作则不需要进行加锁。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值