併發請求導致的業務處理安全風險及解決方案

2021-07-04 03:14:27 字數 2357 閱讀 5505

一段簡單的購買程式,看起來沒有任何問題。

剩餘餘額、商品庫存、購買許可權等判斷面面俱到,從頭到腳包裝的嚴嚴實實。

但是為何人一多就頻頻漏點吶?何解?

還是以**購買為例,****是web程式和資料庫兩部分,業務處理流程:

#!shell

使用者金額是否大於商品**—>商品庫存是否充足—>購買操作:生成訂單—>扣除使用者金額—>商品庫存減1

流程的每一部分都是web與資料庫打交道,查詢或者運算元據庫。

#!php

$goods=$db->firstrow("select * from ".tb('goods')." where goods_id=''");

if(empty($goods)) showerror('商品不存在');

/* 金額是否充足 */

if($user->money

/* 商品庫存 */

if($goods['num']==0) showerror('庫存不足');

/* 購買操作 begin */

//生成訂單

createorder($goods,$user,time());

$user->update('money'=>$user->money-$goods['price']); //使用者金額減少

$db->execute("update ".tb('goods')." set num=num-1 where goods_id=''");//商品庫存-1

showsuccess('購買成功');

/* 購買操作 end */

正常來看這個業務處理是沒有問題的,下面想象下多人同時購買(併發請求,如秒殺活動)的情境可能會引發的問題?

如果乙個使用者同時有兩次購買請求,一次購買已進行到新增訂單但未扣除使用者金額,另一次購買在第一步使用者金額判斷便不準確了。

當商品庫存僅為1時,同時有多個請求,而當前沒有乙個請求走到商品庫存減少位置,多次購買都能成功,而**卻無貨可發。

總結來說,當有大量的購買操作同時進行,如果資料庫的處理速度跟不上程式的請求速度,就會出現判斷不準確的問題,造成使用者以單個商品的金額購買多個商品、某些使用者付款了但得不到商品等,算是乙個安全風險。

核心思想:將一次業務處理流程(如購買操作)作為乙個最小操作單元,同一時間只能有乙個操作。

1.  整個操作加記憶體鎖。如在memcache裡,開始購買時設定購買狀態為進行中,購買結束後清除購買狀態,程式開始時即從memcache裡判斷是否有正在進行的購買操作,如有則退出。

2. 限制每個使用者的購買間隔,如10秒內僅允許購買一次,最好也是放在記憶體裡。

3. 當然,優化資料庫及程式以加快處理速度也是有必要的。

解決方案程式示例(php+mysql+memcached)

#!php

/** * 通過memcache解決併發購買問題

*/$goods=$db->firstrow("select * from ".tb('goods')." where goods_id=''");

if(empty($goods)) showerror('商品不存在');

$mmc=memcache_init();

$lastbuytime=$mmc->get('lastbuytime_'.$user->userid);

if($lastbuytime>0 && $lastbuytime>time()-10) showerror('10秒內只能進行一次購買');

$buying=$mmc->get('buying');

if($buying==1) showerror('有正在進行的購買,請稍候');

/* 金額是否充足 */

if($user->money

/* 商品庫存 */

if($goods['num']==0) showerror('庫存不足');

/* 購買操作 begin */

//生成訂單

createorder($goods,$user,time());

$user->update('money'=>$user->money-$goods['price']); //使用者金額減少

$db->execute("update ".tb('goods')." set num=num-1 where goods_id=''");//商品庫存-1

/* 購買操作 end */

$mmc->set('buying',0);

$mmc->set('lastbuytime_'.$user->userid,time());

showsuccess('購買成功');

併發及併發的執行緒安全處理

目錄 執行緒安全性 原子性提供了互斥訪問,同一時刻只有乙個執行緒可以來對它操作 原子包 具有原子性,執行緒安全的,atomicint 原始碼實現unsafe類的getandaddint實現原理 迴圈判斷當前的值和主記憶體值是否一致,相等就加一,用到的算 法cas全稱compareandswapint...

如何處理重複請求 併發請求的

你可能會想到的是,只要請求有唯一的請求編號,那麼就能借用redis做這個去重 只要這個唯一請求編號在redis存在,證明處理過,那麼就認為是重複的 string key req12343456788 請求唯一編號 long expiretime 1000 1000毫秒過期,1000ms內的重複請求會...

Java使用限流處理大量的併發請求

在web應用中,同一時間有大量的客戶端請求同時傳送到伺服器,例如搶購 秒殺等。這個時候如何避免將大量的請求同時傳送到業務系統。第一種方法 在容器中配置最大請求數,如果大於改請求數,則客戶端阻塞。該方法有效的阻止了大量的請求同時訪問業務系統,但對使用者不友好。第二種方法 使用過濾器,保證一定數量的請求...