自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

并發(fā)扣款,如何保證數(shù)據(jù)的一致性?

開發(fā) 開發(fā)工具 前端
沈老師,我們有個(gè)業(yè)務(wù),同一個(gè)用戶在并發(fā)“查詢,邏輯計(jì)算,扣款”的情況下,余額可能出現(xiàn)不一致,請(qǐng)問(wèn)有什么優(yōu)化方法么?

繼續(xù)解答星球水友提問(wèn)。

沈老師,我們有個(gè)業(yè)務(wù),同一個(gè)用戶在并發(fā)“查詢,邏輯計(jì)算,扣款”的情況下,余額可能出現(xiàn)不一致,請(qǐng)問(wèn)有什么優(yōu)化方法么?

扣款的業(yè)務(wù)場(chǎng)景是怎樣的?

用戶購(gòu)買商品的過(guò)程中,要對(duì)余額進(jìn)行查詢與修改,大致的業(yè)務(wù)流程如下:第一步,從數(shù)據(jù)庫(kù)查詢用戶現(xiàn)有余額:

  1. SELECT money FROM t_yue WHERE uid=$uid; 

不妨設(shè)查詢出來(lái)的$old_money=100元。

第二步,業(yè)務(wù)層實(shí)施業(yè)務(wù)邏輯計(jì)算,比如:

  • 先查詢購(gòu)買商品的價(jià)格,例如是80元;
  • 再查詢產(chǎn)品是否有活動(dòng),以及活動(dòng)折扣,例如是9折;
  • 比對(duì)余額是否足夠,足夠時(shí)才往下走;
  1. if($old_money> 80*0.9){ 
  2.     $new_money=$old_money-80*0.9=28 
  3. } else { 
  4.     return "Not enough minerals"; 

第三步,將數(shù)據(jù)庫(kù)中的余額進(jìn)行修改。

  1. UPDATE t_yue SET money=$new_money WHERE uid=$uid; 

在并發(fā)量低的情況下,這個(gè)流程沒(méi)有任何問(wèn)題,原有金額100元,購(gòu)買了80元的九折商品(72元),剩余28元。

同一個(gè)用戶,并發(fā)扣款可能出現(xiàn)什么問(wèn)題?

在分布式環(huán)境中,如果并發(fā)量很大,這種“查詢+修改”的業(yè)務(wù)有一定概率出現(xiàn)數(shù)據(jù)不一致。

極限情況下,可能出現(xiàn)這樣的異常流程:

步驟一,業(yè)務(wù)1和業(yè)務(wù)2并發(fā)查詢余額,是100元。

畫外音:這些并發(fā)查詢,是在不同的站點(diǎn)實(shí)例/服務(wù)實(shí)例上完成的,進(jìn)程內(nèi)互斥鎖肯定解決不了。

步驟二,業(yè)務(wù)1和業(yè)務(wù)2并發(fā)進(jìn)行邏輯計(jì)算,算出各自業(yè)務(wù)的余額,假設(shè)業(yè)務(wù)1算出的余額是28元,業(yè)務(wù)2算出的余額是38元。

步驟三,業(yè)務(wù)1對(duì)數(shù)據(jù)庫(kù)中的余額先進(jìn)行修改,設(shè)置成28元。業(yè)務(wù)2對(duì)數(shù)據(jù)庫(kù)中的余額后進(jìn)行修改,設(shè)置成38元。

此時(shí)異常出現(xiàn)了,原有金額100元,業(yè)務(wù)1扣除了72元,業(yè)務(wù)2扣除了62元,最后剩余38元。

畫外音:假設(shè)業(yè)務(wù)1先寫回余額,業(yè)務(wù)2再寫回余額。

常見的解決方案?

對(duì)于此案例,同一個(gè)用戶,并發(fā)扣款時(shí),有小概率會(huì)出現(xiàn)異常,可以對(duì)每一個(gè)用戶進(jìn)行分布式鎖互斥,例如:在redis/zk里搶到一個(gè)key才能繼續(xù)操作,否則禁止操作。

這種悲觀鎖方案確實(shí)可行,但要引入額外的組件(redis/zk),并且會(huì)降低吞吐量。

對(duì)于小概率的不一致,有沒(méi)有樂(lè)觀鎖的方案呢?

對(duì)并發(fā)扣款進(jìn)行進(jìn)一步的分析發(fā)現(xiàn):

(1) 業(yè)務(wù)1寫回時(shí),舊余額100,這是一個(gè)初始狀態(tài);新余額28,這是一個(gè)結(jié)束狀態(tài)。理論上只有在舊余額為100時(shí),新余額才應(yīng)該寫回成功。

而業(yè)務(wù)1并發(fā)寫回時(shí),舊余額確實(shí)是100,理應(yīng)寫回成功。

(2) 業(yè)務(wù)2寫回時(shí),舊余額100,這是一個(gè)初始狀態(tài);新余額28,這是一個(gè)結(jié)束狀態(tài)。理論上只有在舊余額為100時(shí),新余額才應(yīng)該寫回成功。

可實(shí)際上,這個(gè)時(shí)候數(shù)據(jù)庫(kù)中的金額已經(jīng)變?yōu)?8了,所以業(yè)務(wù)2的并發(fā)寫回,不應(yīng)該成功。

如何低成本實(shí)施樂(lè)觀鎖?

在set寫回的時(shí)候,加上初始狀態(tài)的條件compare,只有初始狀態(tài)不變時(shí),才允許set寫回成功,Compare And Set(CAS),是一種常見的降低讀寫鎖沖突,保證數(shù)據(jù)一致性的方法。

此時(shí)業(yè)務(wù)要怎么改?

使用CAS解決高并發(fā)時(shí)數(shù)據(jù)一致性問(wèn)題,只需要在進(jìn)行set操作時(shí),compare初始值,如果初始值變換,不允許set成功。

具體到這個(gè)case,只需要將:

  1. UPDATE t_yue SET money=$new_money WHERE uid=$uid; 
  2. 升級(jí)為: 
  3. UPDATE t_yue SET money=$new_money WHERE uid=$uid AND money=$old_money; 

即可。

并發(fā)操作發(fā)生時(shí):業(yè)務(wù)1執(zhí)行:

  1. UPDATE t_yue SET money=28 WHERE uid=$uid AND money=100

業(yè)務(wù)2執(zhí)行:

  1. UPDATE t_yue SET money=38 WHERE uid=$uid AND money=100

這兩個(gè)操作同時(shí)進(jìn)行時(shí),只可能有一個(gè)執(zhí)行成功。

怎么判斷哪個(gè)并發(fā)執(zhí)行成功,哪個(gè)并發(fā)執(zhí)行失敗呢?

set操作,其實(shí)無(wú)所謂成功或者失敗,業(yè)務(wù)能通過(guò)affect rows來(lái)判斷:

  • 寫回成功的,affect rows為1
  • 寫回失敗的,affect rows為0

總結(jié)

高并發(fā)“查詢并修改”的場(chǎng)景,可以用CAS(Compare and Set)的方式解決數(shù)據(jù)一致性問(wèn)題。對(duì)應(yīng)到業(yè)務(wù),即在set的時(shí)候,加上初始條件的比對(duì)即可。

優(yōu)化不難,只改了半行SQL,但確實(shí)能解決問(wèn)題。

但希望大家有收獲,思路比結(jié)論重要。

【本文為51CTO專欄作者“58沈劍”原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)聯(lián)系原作者】

戳這里,看該作者更多好文

 

責(zé)任編輯:趙寧寧 來(lái)源: 架構(gòu)師之路
相關(guān)推薦

2022-10-19 12:22:53

并發(fā)扣款一致性

2024-01-10 08:01:55

高并發(fā)場(chǎng)景悲觀鎖

2025-03-27 08:20:54

2023-09-07 08:11:24

Redis管道機(jī)制

2024-12-26 15:01:29

2021-03-04 06:49:53

RocketMQ事務(wù)

2020-08-05 08:46:10

NFS網(wǎng)絡(luò)文件系統(tǒng)

2024-08-20 16:13:52

2023-05-26 07:34:50

RedisMySQL緩存

2024-10-28 12:41:25

2024-10-16 09:53:07

2022-03-29 10:39:10

緩存數(shù)據(jù)庫(kù)數(shù)據(jù)

2021-12-14 07:15:57

MySQLRedis數(shù)據(jù)

2023-12-11 12:27:31

并發(fā)Zookeeper數(shù)據(jù)

2022-04-06 15:19:32

數(shù)據(jù)庫(kù)MySQL一致性

2019-09-08 22:45:48

并發(fā)扣款一致性冪等性

2024-01-22 08:52:00

AQS雙異步數(shù)據(jù)一致性

2024-07-04 12:36:50

2020-04-01 15:50:17

TiDBMySQL數(shù)據(jù)庫(kù)

2021-04-24 16:58:03

數(shù)據(jù)庫(kù)工具技術(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)