如何降低複雜度,用資料庫做訊息佇列的儲存?

架構擺渡人發表於2022-05-16

大家好,我是【架構擺渡人】,一隻十年的程式猿。這是訊息佇列的第一篇文章,這個系列會給大家分享很多在實際工作中有用的經驗,如果有收穫,還請分享給更多的朋友。

今天跟大家聊聊如何用資料庫來做訊息的儲存,這樣就可以將訊息佇列的整體複雜度進行降低,如果後續你們需要自己造更貼近公司業務的輪子,我覺得可以用資料庫來儲存。

容量設計

假設你們的業務訊息量每天是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對應哪些Queue(資料表),可以輪詢的寫入這些表,這樣資料就比較平均。

  • 建立Topic的時候要預估一下資料量,然後指定分配多少個Queue用於儲存。(後續也能線上修改Queue的數量)
  • Consumer消費某個Topic訊息的時候,同樣也知道對應哪些Queue,在Consumer初始化的時候就會分配好哪個Consumer消費哪個Queue,這樣每個Consumer只會去固定的一個表中查詢資料,效能也非常好。如果後面Consumer增加或者減少,Queue增加或者減少就需要重新分配了,這就是訊息佇列中經常提到的重平衡的一個概念。

當然這裡只是說了整體的設計,還有很多細節需要去完善,比如Topic訊息量小的如何處理,就算是隻分配一個表,但是我們總的表數量是有限的,所以還會涉及到多Topic共用一個表的情況。

大概的我們可能會有下面的一些表:

  • consumer: 消費者,記錄當前消費者的資訊,比如ip, 程式ID, 應用型別,啟動時間等等。
  • consumerGroup: 消費組資訊。
  • topic:主題表。
  • queue: 訊息表,記錄訊息內容,對應的Topic等等。
  • topicQueue: Topic和queue的關係。
  • queueOffset: 佇列消費偏移量,記錄consumer的消費資訊。

總結

不同的場景可以選擇不同的技術方案,用資料庫作為MQ的儲存也是一種不錯的思路。但為什麼開源的都是自己寫磁碟呢?其實這也跟架構有關係,因為一旦依賴外部儲存,也就是整個架構會比較複雜。而且MQ這種本來就是用來扛大流量的,所以要對底層進行優化,用三方的也就意味著效能其實依賴了三方。

通過資料庫來儲存MQ的資料,相對來說技術難點會比寫磁碟要簡單點,大家也可以按照這個思路,自己去設計實現一款MQ。

原創:架構擺渡人(公眾號ID:jiagoubaiduren),歡迎分享,轉載請保留出處。

推薦一款開源的mock框架:https://github.com/yinjihuan/fox-mock 基於Java Agent實現的自測,聯調Mock利器

相關文章