可以參考資料庫樂觀鎖機制,比如執行一條更新庫存的 sql 語句,在併發場景,為了效能和資料可靠性,會在更新時加上查詢時的版本,並且更新這個版本資訊。可能你要對乙個事情進行操作,這個操作可能會執行成百上千次,但是操作結果都是相同的,這就是冪等性。
在海量訂單生成的業務高峰期,生產端有可能就會重**生了訊息,這時候消費端就要實現冪等性,這就意味著我們的訊息永遠不會被消費多次,即使我們收到了一樣的訊息。
業界主流的冪等性有兩種操作:
1.唯一 id + 指紋碼 機制,利用資料庫主鍵去重
2.利用redis的原子性去實現
大家肯定懂唯一 id 的,就不多說了,為什麼需要指紋碼呢?這是為了應對使用者在一瞬間的頻繁操作,這個指紋碼可能是我們的一些規則或者時間戳加別的服務給到的唯一資訊碼,它並不一定是我們系統生成的,基本都是由我們的業務規則拼接而來,但是一定要保證唯一性,然後就利用查詢語句進行判斷這個id是否存在資料庫中。
好處就是實現簡單,就乙個拼接,然後查詢判斷是否重複。
壞處就是在高併發時,如果是單個資料庫就會有寫入效能瓶頸
解決方案:根據 id 進行分庫分表,對 id 進行演算法路由,落到乙個具體的資料庫,然後當這個 id 第二次來又會落到這個資料庫,這時候就像我單庫時的查重一樣了。利用演算法路由把單庫的冪等變成多庫的冪等,分攤資料流量壓力,提高效能。
相信大家都知道 redis 的原子性操作,我這裡就不需要過多介紹了。
使用 redis 的原子性去實現需要考慮兩個點
一是 是否 要進行資料落庫,如果落庫的話,關鍵解決的問題是資料庫和快取如何做到原子性? 資料庫與快取進行同步肯定要進行寫操作,到底先寫 redis 還是先寫資料庫,這是個問題,涉及到快取更新與淘汰的問題
二是如果不落庫,那麼都儲存到快取中,如何設定定時同步的策略? 不入庫的話,可以使用雙重快取等策略,保障乙個訊息副本,具體同步可以使用類似 databus 這種同步工具。
先大概說一說可能會有哪些重複消費的問題。
首先就是比如rabbitmq、rocketmq、kafka,都有可能會出現消費重複消費的問題,正常。因為這問題通常不是mq自己保證的,是給你保證的。然後我們挑乙個kafka來舉個例子,說說怎麼重複消費吧。
kafka實際上有個offset的概念,就是每個訊息寫進去,都有乙個offset,代表他的序號,然後consumer消費了資料之後,每隔一段時間,會把自己消費過的訊息的offset提交一下,代表我已經消費過了,下次我要是重啟啥的,你就讓我繼續從上次消費到的offset來繼續消費吧。
但是凡事總有意外,比如我們之前生產經常遇到的,就是你有時候重啟系統,看你怎麼重啟了,如果碰到點著急的,直接kill程序了,再重啟。這會導致consumer有些訊息處理了,但是沒來得及提交offset,尷尬了。重啟之後,少數訊息會再次消費一次。
其實重複消費不可怕,可怕的是你沒考慮到重複消費之後,怎麼保證冪等性。
給你舉個例子吧。假設你有個系統,消費一條往資料庫裡插入一條,要是你乙個訊息重複兩次,你不就插入了兩條,這資料不就錯了?但是你要是消費到第二次的時候,自己判斷一下已經消費過了,直接扔了,不就保留了一條資料?
一條資料重複出現兩次,資料庫裡就只有一條資料,這就保證了系統的冪等性
冪等性,我通俗點說,就乙個資料,或者乙個請求,給你重複來多次,你得確保對應的資料是不會改變的,不能出錯。
(1)比如你拿個資料要寫庫,你先根據主鍵查一下,如果這資料都有了,你就別插入了,update一下好吧
(2)比如你是寫redis,那沒問題了,反正每次都是set,天然冪等性
(3)比如你不是上面兩個場景,那做的稍微複雜一點,你需要讓生產者傳送每條資料的時候,裡面加乙個全域性唯一的id,類似訂單id之類的東西,然後你這裡消費到了之後,先根據這個id去比如redis里查一下,之前消費過嗎?如果沒有消費過,你就處理,然後這個id寫redis。如果消費過了,那你就別處理了,保證別重複處理相同的訊息即可。
還有比如基於資料庫的唯一鍵來保證重複資料不會重複插入多條,我們之前線上系統就有這個問題,就是拿到資料的時候,每次重啟可能會有重複,因為kafka消費者還沒來得及提交offset,重複資料拿到了以後我們插入的時候,因為有唯一鍵約束了,所以重複資料只會插入報錯,不會導致資料庫**現髒資料
如何保證mq的消費是冪等性的,需要結合具體的業務來看
RabbitMQ消費端冪等性概念及解決方案
使用者對於同一操作發起的一次請求或者多次請求的結果是一致的。比如資料庫的樂觀鎖,在執行更新操作前,先去資料庫查詢version,然後執行更新語句,以version作為條件,如果執行更新時有其他人先更新了這張表的資料,那麼這個條件就不生效了,也就不會執行操作了,通過這種樂觀鎖的機制來保障冪等性.消費端...
十 RabbitMQ 冪等性
目錄使用者對於同一操作發起的一次請求或者多次請求的結果是一致的,不會因為多次點選而產生了 舉個最簡單的例子,那就是支付,使用者購買商品後支付,支付扣款成功,但是返回結果的時候網路異常,此時錢已經扣了,使用者再次點選按鈕,此時會進行第二次扣款,返回結果成功,使用者查詢餘額發現多扣錢了,流水記錄也變成了...
原子性 冪等性
原子性 如果這個操作所處的層 layer 的更高層不能發現其內部實現與結構,那麼這個操作是乙個原子 atomic 操作。原子操作可以是乙個步驟,也可以是多個操作步驟,但是其順序不可以被打亂,也不可以被切割而只執行其中的一部分。將整個操作視作乙個整體是原子性的核心特徵。冪等性 再簡單一點說,在乙個業務...