幂等性
冪等性的概念
對于同一操作發起的請求(一次或者多次請求),任意多次執行對資源本身產生的影響均與一次執行產生的影響相同,不會因為多次相同操作而產生副作用。
比如一個用戶注冊,點擊“注冊”,由于某些原因(比如服務器負載大),長時間轉圈圈,你多次點擊“注冊”,這多次請求都是完全相同的,應該只插入一條用戶記錄,而不是點多次“注冊”每次都插入一條用戶記錄。
比如下單購買,點擊“提交”,卡住了,什么破手機|網絡,瘋狂點“提交”,這多次請求完全相同,應該只產生一個訂單,而不是每次點擊都產生一個訂單。
http請求方式(restful)
在其他條件不變的情況下(資源沒有被其它操作修改):
get 查詢,相同的查詢操作,比如select username where id=1 from tb_user;執行多次,查詢到的結果都是相同的,get是冪等的
post 插入,相同的插入操作,比如insert into tb_order (...) values (...); 執行多次,雖然可能會因為主鍵已存在、某個字段的值要唯一等原因插入失敗,但每次都會嘗試插入一條新紀錄,如果滿足約束,每次都會插入一條記錄,結果可能不同(產生了新紀錄),post不是冪等的
put 更新,相同的更新操作,比如update tb_user username='chy',age=20 where id=1; 不管執行多少次,id=1的這條記錄,name都是chy,age都是20,結果都是相同的,put是冪等的
delete 刪除,相同的刪除操作,比如delete from tb_user where id=1 ,不管執行多少次,結果都是刪除了id=1的記錄,結果相同,delete是冪等的
消息消費者與冪等性
消息可能會被重復投遞、消費,比如第一次投遞的消息還在queue中,為確保消息投遞的可靠性,延時又投遞了一次消息,消費者應確保同一消息只進行一次消費。
冪等常見的實現方式
核心思想:
使用唯一的業務單號來標識一個業務,如果業務單號相同,就認為是同一筆業務,只執行一次。(去掉重復的業務)
存儲執行過的業務的業務單號,先根據業務單號查詢這筆業務是否已經執行過了,如果沒執行過,才執行。
如果系統是單線程的,查詢單號、執行這筆業務2個過程都不用加鎖;如果過多個線程并發訪問執行過的業務單號(有讀有寫),就需要給執行過的業務單號加鎖(冪等是查詢時加鎖),執行過程往往也需要給共享數據加鎖。
業務單號可以用流水號,也可以唯一id+指紋碼,總之要能唯一標識一筆業務。
比如order的id,只用id是可以唯一標識一個訂單,但不能標識是這個order的增改刪查哪個業務,也不能標識是這個order的第幾次修改操作。需要再加一個指紋碼,比如時間戳、業務規則什么的。
1、數據庫主鍵去重
單獨用一張tb_order_handled來存儲執行過的訂單的業務單號,使用列no存儲業務單號(主鍵、unique約束),外鍵order_id關聯order表的id,
執行業務時先select count(*) from tb_order_handled where no=xxx; 根據業務單號查詢這筆業務是否已執行,數量為1表示有這條記錄,這筆業務已處理過,不再處理;為0表示沒處理過,就處理這筆業務。
這種方式的缺點是增加了數據庫IO次數,數據庫性能很容易成為系統性能的瓶頸,往往需要分庫分表把業務單號所在的表拆分出來,來提升數據庫性能。
如果這個服務只有數據庫更新操作的話,也可以使用數據庫的樂觀鎖(版本號機制)。
2、分布式鎖(推薦)
把處理過的業務單號作為鍵值對的key,存儲在redis上,處理業務時先查詢redis上有沒有這個key,有就說明處理過了,不再處理,沒有就說明這筆業務沒有處理過,開始處理。
簡單、且性能高,推薦。
3、緩沖區
把一段時間內要處理的業務都放到緩沖區中,先去掉重復的業務,再執行。
缺點:處理有時延、不能馬上返回處理結果。不太常用。
總結
- 上一篇: 分时系统和实时系统的区别
- 下一篇: 鞋子臭味怎么快速去除 酒精除臭