分布式系統敏感操作的併發處理(併發鎖)

2022-08-30 10:48:08 字數 1666 閱讀 5207

在實際工作中經常遇到對賬戶的操作(賬戶充值和賬戶消費),處理的邏輯如下:

// 1 查詢賬戶當前的金額

// 2 根據操作,計算操作後的金額

// 3 更新賬戶的金額

然而,在實際中經常會有併發操作的問題,下面通過在資料中執行sql的方式,模擬下不做併發處理的情況:

資料庫是mysql,隔離級別採用預設的可重複讀,表為t_money,只有兩列:id、money,只有一條記錄id=1, money=1000。分別起兩個客戶端,模擬併發操作的行為:

序號事務1

事務21

start transaction;

2start transaction;

3select * from t_money where id=1;

4select * from t_money wehre id=1;

5update t_money set money=900 where id=1;

6update t_money set money=1200 where id=1; (不能執行,被阻塞)

7select * from t_money where id=1;

8commit;

(事務1執行commit後,被阻塞的update執行)

9select * from t_money where id=1;

select * from t_money where id=1;

10commit;

11select * from t_money where id=1;

select * from t_money where id=1;

按照上面的步驟執行完成後,11步查出來賬戶id=1的money=1200。

按照業務的邏輯,消費和充值後,賬戶的金額應該為1100,而系統中id=1的賬戶金額居然為1200,這是絕對不能接受的!

1. 利用mysql的當前讀

將更新金額的語句,使用:

update t_money set money=money-100 where id=1;
update會使用「當前讀」,可以讀取到其它事物未提交的資料。當前讀遇到其它事務的寫操作時,會被阻塞,引起當前讀的語句:

select ... for update;

select ... lock in share mode;

update

delete

insert

2. redis併發鎖

也就是,操作前要獲得鎖,操作完成釋放鎖;沒有獲得鎖,不允許進行操作,直接返回併發錯誤。

在實際系統中,往往是分布式部署的,那麼就需要加分布式鎖。最容易想到(本人)的就是使用redis,在redis中使用setnx,偽**如下:

if(redis.setnx(id)) else
2. 優雅的redis併發鎖

在方案1中,在加鎖失敗後,直接返回併發異常,呼叫方需要重試。實際上,第一次請求時,雖然不能獲得鎖,但是可能在1s之後就可以獲得鎖了,我們何不如稍微等待下再重試呢?

更加優雅的加鎖,偽**:

if (redis.setnx(id))  else  else 

}

分布式系統session處理

分布式系統中同乙個使用者的請求可能會被分發到不同的伺服器上,而session是儲存在單個伺服器上,所以有可能會導致session失效,對於前端使用者最明顯的感覺就是需要重新登入。主要有如下幾種解決方案 1.session sticky 方式 由負載均衡來負責標記每次的請求,將同乙個會話請求傳送到同乙...

分布式 分布式系統的設計

在計算機領域,當單機效能達到瓶頸時,一般有兩種方式解決效能問題 而分布式系統的設計說白了就是 如何合理將乙個系統拆分成多個子系統部署到不同機器上。講設計方法前,先介紹分布式系統的特性 1 分布性 空間中隨機分布。這些計算機可以分布在不同的機房,不同的城市,甚至不同的國家。2 對等性 分布式系統中的計...

分布式系統中的死鎖處理

1死鎖發生的條件 當且僅當以下四個條件同時成立時,死鎖才會發生 1 互斥。同乙個資源在同一時刻最多只能被乙個程序占用。2 占有並等待。必然有乙個程序至少占用了系統中的乙個資源,同時在等待獲取被其他程序占用的資源。3 不可剝奪。乙個程序不能剝奪被其他程序占用的資源。4 迴圈等待。在等待圖中有乙個迴圈。...