冪等:一次操作和多次操作的結果是一致的。
接口冪等性
是指用戶對於同一操作發起的一次請求或者多次請求的結果是一致的,不會因為多次點擊而產生了副作用。
防重設計主要為了避免產生重復數據,對接口返回沒有太多要求。而冪等設計除了避免產生重復數據之外,還要求每次請求都返回一樣的結果。
解決方案:
1、悲觀鎖
for update
for update 會從數據庫中查詢最新的數據,並且加上排他鎖。其他沒競爭到的會等待重試。
普通的select是查快照數據,不符合(說明錢已經轉走了,被人加鎖update過了),直接結束。
2、樂觀鎖
表中增加一個timestamp
或者version
字段,這里以version
字段為例。
先查詢要修改表的版本號version,
再用包含這個version的請求去update數據庫,並把version+1。
3、唯一索引
表中加唯一索引字段
4、建防重表
根據業務場景定義冪等的字段,如該表可以只包含兩個字段:id
和 唯一索引
,唯一索引可以是多個字段比如:name、code等組合起來的唯一標識,例如:susan_0001。
5、狀態機
很多時候業務表是有狀態的,比如訂單表中有:1-下單、2-已支付、3-完成、4-撤銷等狀態。
如在支付場景中,訂單的狀態只能從上一級狀態修改到下一級狀態。
update操作,數據庫會加上悲觀鎖(行鎖),鎖住當前行。(update要改的數據前會先select * from ........ for update,此時只有一個請求能獲取到這把鎖)
例如有多個請求過來,只有一個請求能獲取到這把鎖,再把數據更新為2 -> 3,釋放鎖。后面的請求就查不到id123 且status為2的數據了。
這個與redis分布式鎖很相似。
6、分布式鎖
數據庫的分布式鎖性能不好,比如可以用redis實現。
比如可以吧唯一標識的訂單號、token、唯一索引這些放入redis的中,實現分布式鎖。
7、token + redis
分為兩步操作
1、 先獲取token並存放到redis中
2、redis查詢toeken信息是否存在,存在表示是第一次操作,進行業務操作(可以選擇在進行業務前后進行token刪除。先刪除token會好點,失敗的業務用戶可以重新獲取token補償)。這里應該還會用當前的token做分布式鎖,返回時刪除token信息,釋放分布式鎖。
參考:
https://www.zhihu.com/question/27744795