消息队列消息存储设计(架构实战营 模块八作业)
消息表
对于 mysql 来说,控制表的大小以及对表进行顺序的读写,性能是最高的,因此每个 topic 用一张单独的表存储,并且每张表支持设置最大消息数量,超出最大消息数量则会创建新的表来存储。消息队列往往有对 topic 分区(partition)存储的要求,因此每一个分区也使用一张单独的表存储。因此,表名规则为:message_{topic}_n_m,其中 n 为分区序号,m 为表的序号。例如,业务方创建名为 test 的 topic,分区数为 4,每张表支持最大消息数量为 100w,对于第一个分区的表来说,表名为 message_test_0_0,当第一张表超出 100w 条消息后,会创建第二张表,表名为 message_test_0_1。
其中 id 设置为 primary key,消费者通过 id 读取对应的消息。submit_time 设置为普通索引,满足根据时间清理过期消息。
消费信息表
consume_log 表
光有消息表还不够,还需要一张表记录消费状态。也有几个要点:
我们支持多个消费者组,每个消费者组共享一条消费进度记录,所以表中需要有 group 字段来区分是哪个消费者组的消费记录;
消费偏移量是最重要的,记录了消费到哪条数据,用 offset 来标识,即 message 表中的 msg_id,因为 msg_id 在一个数据分组,一个 topic 下是严格递增的;
所以最终消息消费记录表设计如下:
如果不考虑太复杂的设计,核心主要就这两个表。
索引分析
我们先从几个高频场景来分析:
发送消息
客户端完成分片路由,分配到某个 shard,发送消息数据
消息队列服务器根据 topic 找到对应的消息表 message_{topic},然后插入数据,msg_id 自增
消费消息
客户端完成分片的路由,向对应服务器拉取消息,带上参数 topic,group
查询 consumer_log 表,根据 topic,group 通过索引找到对应的数据,取出 offset
根据 offset 查询对应的 message 表,通过主键 id,找出下个 offset 的记录,取出并准备返回
更新 consumer_log 的 offset 信息
返回数据
以上 2,3,4 在一个事务中执行。
从 2 可以看出,message_log 需要加个 topic+group 组合索引,命名为 idx_topic_group
从 3 可以看出,message 表需要 msg_id 的索引,但是其本身已经是主键,无需额外增加
版权声明: 本文为 InfoQ 作者【Gor】的原创文章。
原文链接:【http://xie.infoq.cn/article/64d5708f91801a120b3ac3fd7】。未经作者许可,禁止转载。
评论