成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java多線程并發數據錯亂了,接口冪等性如何設計?

開發 前端
如果要是基于數據庫表加一個唯一索引,就可以實現接口冪等了 ,可是如果業務邏輯過于復雜,有很多數據存儲,或者涉及很多表,此時就不能單單依賴一個唯一索引了,需要依靠在接口入口處加分布式鎖,然后才可以解決復雜接口的冪等性。

業務背景

今天給大家聊聊線上系統的接口冪等問題,以及如何通過分布式鎖來保障接口的冪等性,同時會給大家分享一下我們在基于分布式鎖實現接口冪等性的時候,一些生產實踐經驗的積累。

首先給大家說說,假如說要是我們線上系統的核心接口要是沒有冪等性保障機制的話,可能會出現什么情況?

其實非常簡單,假設你有一個系統,他有一個接口,這個接口接受請求的時候假設會在數據庫里插入一條數據,正常情況下一個用戶對這個接口發起一次請求應該就只有一條數據,結果可能某一天你會發現這個用戶通過這個接口插入了多條數據。

如下圖 1 所示:

圖 1

初版防重代碼

那么為什么會這樣呢?其實我們一般這類系統接口,但凡是寫的稍微好一點的,都會在接口里加入防重代碼。

就是會有代碼判斷一下,當前你要寫入的這條數據是否存在,如果他要是不存在的話,就會進行插入,但是如果他存在的話就不會允許你重復插入的。

這種防重代碼如下所示:

public void business(Request request) {   
// 1、先根據請求參數在db里查詢一下這條數據
Data data = findData(request);

// 2、如果這條數據在db里已經存在了,此時就直接返回了
if(data != null) {
return;
}

// 3、如果要是這條數據在db里不存在,此時就會執行數據插入邏輯了
insertData(request);
}

結合上面這段代碼的防重邏輯,我們可以看下圖 2 的運行邏輯展示:

圖 2

在插入數據之前一定會先根據請求參數查詢這條數據,如果查詢到了,則此時直接返回不會重復插入,但是如果沒有查詢到這條數據,則此時會插入這條數據。

那么大家可能問題來了,那既然都已經有這個防重邏輯了,即使你用相同的請求參數重復多次調用這個接口插入數據,也不應該重復插入數據啊!

按說確實是這樣子的,但是凡事總有例外,那就是大名鼎鼎的瞬時重試+多線程并發問題。

瞬時重試+多線程并發問題分析

下面我們給大家解釋一下,在上述的代碼防重邏輯下,如果要是短時間內用戶用相同的請求參數重復的發起了兩次請求,為什么會穿透防重邏輯,在數據庫里插入兩條一樣的數據。

大家要打起且精神來,仔細來看這個過程了, 首先用戶可能會因為過于激動、手抖或者是網絡抽風等各種原因,在一瞬間發起兩次請求參數完全相同的請求。

如下圖 3 所示:

圖 3

接著呢,這兩個請求到了我們的系統后,其實是分別由一個線程來處理的,不管你是用 tomcat 部署提供的 controller 接口,還是基于 dubbo 提供的 rpc 接口,其實每個請求過來都是由一個獨立的線程來處理的。

如下圖 4 所示:

圖 4

接著呢,這兩個線程會并發的運行相同的一段代碼邏輯,就是先根據請求參數查詢這條數據是否存在,存在就返回,不存在就進行插入。

這個時候可能會出現一個問題,因為是多線程并發,所以很可能這兩個線程會同時執行數據查詢邏輯,但是他們倆同時執行數據查詢邏輯的時候,有一個問題,那就是此時數據庫里沒數據啊!

所以說,這兩個線程并發運行,完全可能會同時發現從數據庫里查詢出來的數據是空的。

如下圖 5 所示:

圖 5

然后這個時候,兩個線程既然發現自己查詢到的數據都是空的,那當然都可以去插入數據了。

所以此時這兩個線程會基于這個請求參數分別插入一條數據,而這條數據其實對于業務來說是完全重復的,因為請求參數是完全相同的。

如下圖 6 所示:

圖 6

這個時候就會導致本次數據重復問題了,針對這種情況,我們一般把這種接口稱之為沒有冪等性。

因為如果一個接口是有冪等性的,其實對于這個接口如果說用相同的參數發起請求,那肯定是只會有一條數據,不可能會有重復數據的,這才叫做冪等性。

而現在的問題是,這個接口用相同的請求參數發起多次,結果數據有重復了,此時接口就沒有冪等性。

數據庫唯一索引實現冪等

針對上述這種接口冪等問題,其實比較簡單的一種解決方案,就是基于我們依賴的數據庫去實現冪等。

也就是說,用數據庫的唯一索引來實現即可,如果我們要是基于請求中的某一個或者多個業務字段組成一個唯一索引,那么其實你要往數據庫中用相同參數插入重復數據,那就是不可能的。

因為數據庫層面就會阻止你插入的,唯一索引會確保這一點,你要重復插入,他就會拋異常。

如下 7 所示:

圖 7

分布式鎖實現冪等

但是很多時候我們會發現一個問題,那就是我們可能不一定說每次都可以依賴數據庫的 唯一索引實現這種冪等性。

因為有可能你在業務邏輯里,除了依賴數據庫以外,還依賴了別的服務接口,或者是 elasticsearch、redis 等多種數據存儲,也可能是依賴了數據庫中的多張表里的數據,你不可能每張表都做一個唯一索引來確保冪等性。

所以對于有復雜業務邏輯的接口來說,要確保冪等性,往往需要引入一個關鍵組件,那就是分布式鎖。

所謂的分布式鎖,意思就是依賴外部的某個系統來加一把鎖,鎖加了以后后續還可以釋放這把鎖,現在比較常見的分布式鎖實現主要是依賴 redis 和 zookeeper 這兩個來實現的,我們這里就以 redis 分布式鎖來舉例說明。

先往簡單了說,我們可以在接口的入口代碼處,基于 redis 加一把分布式的鎖,這個時候只有一個線程可以成功加鎖。

加鎖之后,就這一個線程就可以去查詢這條數據是否存在,如果不存在,就可以插入一條數據進去,然后再釋放鎖,在這個過程中,另外一個線程因為獲取不到 redis 分布式鎖,所以只能干等著。

如下圖 8 所示:

圖 8

等第一個線程加鎖,然后查詢數據,發現數據不存在,接著插入一條數據,最后釋放鎖之后,接著第二個線程就才能得到機會再次加鎖,接著第二個線程加鎖后查詢數據,發現數據已經存在了,此時他就會直接返回,不會重復插入數據了。

如下圖 9 所示:

圖 9

如上圖,大家可以發現,只要在核心接口的入口處加一把分布式鎖,就可以實現多線程并發下,復雜業務邏輯不會被重復執行了,而且不依賴數據庫某個表的唯一索引,只要基于 redis 實現加鎖和釋放鎖就可以了。

而至于 redis 分布式鎖是如何實現的,就不在本文的討論中了,我們這次主要是給大家先分析一下線上系統接口的冪等問題,當沒有冪等性的時候,接口是如何在多線程并發場景下出現數據重復問題的。

總結

然后我們分析了,如果要是基于數據庫表加一個唯一索引,就可以實現接口冪等了 ,可是如果業務邏輯過于復雜,有很多數據存儲,或者涉及很多表,此時就不能單單依賴一個唯一索引了,需要依靠在接口入口處加分布式鎖,然后才可以解決復雜接口的冪等性。

責任編輯:武曉燕 來源: 石杉的架構筆記
相關推薦

2020-07-15 08:14:12

高并發

2025-02-26 08:20:18

2024-03-13 15:18:00

接口冪等性高并發

2021-03-28 09:45:05

冪等性接口數據

2021-01-18 14:34:59

冪等性接口客戶端

2021-01-20 07:16:07

冪等性接口token

2021-04-14 17:18:27

冪等性數據源MySQL

2023-09-01 15:27:31

2021-01-13 11:23:59

分布式冪等性支付

2025-03-17 08:07:11

2022-05-01 21:43:38

SQL設計模式

2025-02-23 08:00:00

冪等性Java開發

2023-03-07 08:19:16

接口冪等性SpringBoot

2024-06-24 01:00:00

2024-08-29 09:01:39

2022-05-23 11:35:16

jiekou冪等性

2020-06-28 11:14:36

多線程性能結構

2024-07-10 12:23:10

2024-11-01 09:28:02

2025-04-27 03:22:00

系統接口冪等性
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品久久一区 | 自拍视频在线观看 | 欧美激情一区二区三级高清视频 | 成人av一区二区三区 | www久| 91一区二区三区 | 久久久影院 | 欧美一级片在线观看 | 国产乱码精品一区二区三区中文 | 国产91久久久久蜜臀青青天草二 | 在线中文字幕第一页 | 噜噜噜色网 | 成人在线视频网站 | 九九九精品视频 | 欧美视频在线看 | 国产精品精品视频一区二区三区 | 女人夜夜春 | 亚洲午夜电影 | 亚洲a网 | 97精品视频在线 | 99久久久国产精品 | 免费观看一级毛片 | 久久久91| 伊人网在线播放 | 夜夜摸夜夜操 | 羞羞网站免费 | 男女午夜激情视频 | 成人亚洲视频 | 亚洲天堂中文字幕 | 天天躁日日躁狠狠躁白人 | 亚洲成av| 午夜天堂精品久久久久 | 亚洲另类视频 | 9久久婷婷国产综合精品性色 | 日韩有码一区 | 欧美 日本 国产 | 99reav| www.日韩| 国产在线不卡 | 国产精品久久九九 | 国产精品永久免费 |