大家好,我是【架构摆渡人】,一只十年的程序猿。这是消息队列的第一篇文章,这个系列会给大家分享很多在实际工作中有用的经验,如果有收获,还请分享给更多的朋友。
今天跟大家聊聊如何用数据库来做消息的存储,这样就可以将消息队列的整体复杂度进行降低,如果后续你们需要自己造更贴近公司业务的轮子,我觉得可以用数据库来存储。
假设你们的业务消息量每天是10亿条,数据存储最近7天的量,也就是70亿条。我们以单表2000W条数据作为上限,1个库放10张表,那么总共需要40个库来承载这些数据量。
当然这40个库可以不用40个单独的数据库实例,这样成本有点高,当然主要还是取决于你们消息的读写并发有多高,如果很高的话数据库实例越多,性能肯定越好。不多的话就5个数据库实例,每个实例上建8个库即可。(根据消息读取和写入的量结合数据库规格的性能进行评估)
因为我们总共也就能存放80亿条的数据,如果数据量多了必然会影响查询性能或者磁盘空间不够的问题,而且对于已消费的消息,其实后面就没有业务价值了。
所以我们还需要每天进行数据的归档操作,归档你可以将这些数据移到Nosql中,也可以进行删除,看业务场景决定。有了归档,这样就能保持当前的数据库集群能够承载每日的消息容量。
接下来聊聊最重要的存储设计,就是我要用数据库来存,我的表该怎么设计?
前面我们也讲到了,我们大概需要400张表来存储这些数据。我们对某个Topic进行消息发送,那么数据必然要落到这400张表中的某一张表里面,所以我们很自然的想以某个字段来分表就行了呗,比如Topic,每个Topic写入的数据必定在同一张表里面。
这样也没啥问题,但是在实际的业务中,必定是有的Topic消息量会很大,有的会很少。对于消息量很大的,可能几个小时就突破2000W的量了,那么这个表必定越来越大。所以我们在设计数据存储的时候一定要考虑到如何将某个Topic下的数据均匀的存储到多张表里面去。
可能有人就会说了,既然不能固定按Topic进行分表,那么就对总的表数量取模好啦,这样数据就会分布在每张表里面。但是这样对于consumer在消费的时候就有影响了,因为consumer压根就不知道自己要消费Topic的数据存在哪,必须得从所有的库表里面进行查询才能拿到数据,性能不太好。
要解决这个问题,我们先回顾下开源的MQ是怎么设计的,以RocketMQ来说,一个Topic下的消息会存储到多个Queue中(也就相当于kafka中的partition),也就是每个Topic下的Queue并不是一定的数量,而是可以在创建Topic时进行指定,所以我们在用表做存储的时候,也可以参考这个设计,本来每个Topic对应的消息量就不同。
所以一张表也就相当于一个Queue,用于存储消息内容。我们可以记录Topic和Queue之间的关系,记录后有如下的作用:
当然这里只是说了整体的设计,还有很多细节需要去完善,比如Topic消息量小的如何处理,就算是只分配一个表,但是我们总的表数量是有限的,所以还会涉及到多Topic共用一个表的情况。
大概的我们可能会有下面的一些表:
不同的场景可以选择不同的技术方案,用数据库作为MQ的存储也是一种不错的思路。但为什么开源的都是自己写磁盘呢?其实这也跟架构有关系,因为一旦依赖外部存储,也就是整个架构会比较复杂。而且MQ这种本来就是用来扛大流量的,所以要对底层进行优化,用三方的也就意味着性能其实依赖了三方。
通过数据库来存储MQ的数据,相对来说技术难点会比写磁盘要简单点,大家也可以按照这个思路,自己去设计实现一款MQ。
原创:架构摆渡人(公众号ID:jiagoubaiduren),欢迎分享,转载请保留出处。
推荐一款开源的mock框架:https://github.com/yinjihuan/fox-mock 基于Java Agent实现的自测,联调Mock利器