一、什么是冪等性
可以參考數據庫樂觀鎖機制,比如執行一條更新庫存的 SQL 語句,在並發場景,為了性能和數據可靠性,會在更新時加上查詢時的版本,並且更新這個版本信息。可能你要對一個事情進行操作,這個操作可能會執行成百上千次,但是操作結果都是相同的,這就是冪等性。
update t_reps set count = count - 1, version = version + 1 where version = 1
二、消費端的冪等性保障
在海量訂單生成的業務高峰期,生產端有可能就會重復發生了消息,這時候消費端就要實現冪等性,這就意味着我們的消息永遠不會被消費多次,即使我們收到了一樣的消息。
業界主流的冪等性有兩種操作:
1.唯一 ID + 指紋碼 機制,利用數據庫主鍵去重
2.利用redis的原子性去實現
三、唯一 ID + 指紋碼 機制
大家肯定懂唯一 ID 的,就不多說了,為什么需要指紋碼呢?這是為了應對用戶在一瞬間的頻繁操作,這個指紋碼可能是我們的一些規則或者時間戳加別的服務給到的唯一信息碼,它並不一定是我們系統生成的,基本都是由我們的業務規則拼接而來,
但是一定要保證唯一性,然后就利用查詢語句進行判斷這個id是否存在數據庫中。
好處:實現簡單,就一個拼接,然后查詢判斷是否重復。
壞處:在高並發時,如果是單個數據庫就會有寫入性能瓶頸
解決方案 :根據 ID 進行分庫分表,對 id 進行算法路由,落到一個具體的數據庫,然后當這個 id 第二次來又會落到這個數據庫,這時候就像我單庫時的查重一樣了。利用算法路由把單庫的冪等變成多庫的冪等,分攤數據流量壓力,提高性能。
四、redis的原子性去實現
我們都知道redis是單線程的,並且性能也非常好,提供了很多原子性的命令。比如可以使用 setnx 命令。
在接收到消息后將消息ID作為key執行 setnx 命令,如果執行成功就表示沒有處理過這條消息,可以進行消費了,執行失敗表示消息已經被消費了。
使用 redis 的原子性去實現主要需要考慮兩個點:
第一:我們是否要進行數據落庫,如果落庫的話,關鍵解決的問題是數據庫和緩存如何做到原子性?
- 采用延時雙刪策略
偽代碼如下:
public void write(String key,Object data){
redis.delKey(key);
db.updateData(data);
Thread.sleep(1000);
redis.delKey(key);
}
轉化為中文描述就是:
(1)先淘汰緩存
(2)再寫數據庫(這兩步和原來一樣)
(3)休眠1(根據業務自己設定)秒,再次淘汰緩存
這么做,可以將1秒內所造成的緩存臟數據,再次刪除。
URL:https://www.cnblogs.com/rjzheng/p/9041659.html
第二:如果不進行落庫,那么都存儲到緩存中,如何設置定時同步的策略(同步到關系型數據庫)?緩存又如何做到數據可靠性保障呢
關於不落庫,定時同步的策略,目前主流方案有兩種:
第一種為雙緩存模式,異步寫入到緩存中,也可以異步寫到數據庫,但是最終會有一個回調函數檢查,這樣能保障最終一致性,不能保證100%的實時性。
第二種是定時同步,比如databus同步。