1>***貓: python基礎類: 技能類: 業務類: 你對電商的業務瞭解嗎? 我問你一個具體的電商的業務啊:電商在大多數領域中都是有庫存的,我在下訂單的時候,我的庫存的變化,你知道嗎?(我問的是你怎麼去處理庫存的變化?)因為客戶下單的時候,有些是去減庫存了,有些不減對吧?你的理解呢? 你事務的時 ...
1>***貓:
-
python基礎類:
- 字元串反轉的常用處理方式:
# 方法一:使用字元串切片 s = "hello python" result = s[::-1] print(result) # [Out]nohtyp olleh # 方法二:使用列表的reverse方法 l = list(s) l.reverse() result = "".join(l) print(result) # 方法三:使用reduce from functools import reduce s = "hello python" result = reduce(lambda x, y: y + x, s) print(result) # 方法四:使用遞歸函數 def func(s): if len(s) < 1: return s return func(s[1:]) + s[0] result = func(s) print(result) # 方法五:使用棧 def func(s): l = list(s) # 模擬全部入棧 result = "" while len(l) > 0: result += l.pop() # 模擬出棧 return result result = func(s) print(result) # 方法六:for迴圈 def func(s): result = "" max_index = len(s) - 1 for index, value in enumerate(s): result += s[max_index - index] return result result = func(s) print(result)
- 你說一下python中的迭代器、生成器、裝飾器
# 可以被next()函數調用並不斷返回下一個值的對象稱為迭代器:Iterator # 在Python中,這種一邊迴圈一邊計算的機制,稱為生成器:generator # 在代碼運行期間動態增加功能的方式,稱之為“裝飾器”: Decorator # 生成器和迭代器的區別: # 1、語法方面來講: # 生成器是用函數中yield語句來創建的。迭代器的創建首先跟函數無關,可以用iter([1,2])來創建。 # 2、使用方面來講: # 由於生成器是使用函數的方式創建的,所以生成器裡面的所有過程都會被執行,但請註意生成器裡面的過程只有在被next()調用或者for迴圈調用時,裡面的過程才會被執行
- 你說一下python中的迭代器、生成器、裝飾器
- 如果讓你實現一個迭代器,你會怎麼做
# 迭代器有兩個基本的方法:iter() 和 next()。 # 字元串,列表或元組對象都可用於創建迭代器: list = [1, 2, 3, 4] it = iter(list) print(next(it)) print(next(it)) # 迭代器對象可以使用常規for語句進行遍歷: list = [1, 2, 3, 4] it = iter(list) for x in it: print(x, end=" ") # 也可以使用 next() 函數: import sys list = [1, 2, 3, 4] it = iter(list) while True: try: print(next(it)) except StopIteration: sys.exit() # 創建一個迭代器 # 把一個類作為一個迭代器使用需要在類中實現兩個方法 __iter__() 與 __next__() 。 # # 如果你已經瞭解的面向對象編程,就知道類都有一個構造函數,Python 的構造函數為 __init__(), 它會在對象初始化的時候執行。 # __iter__() 方法返回一個特殊的迭代器對象, 這個迭代器對象實現了 __next__() 方法並通過 StopIteration 異常標識迭代的完成. # __next__() 方法(Python 2 里是 next())會返回下一個迭代器對象。 # 創建一個返回數字的迭代器,初始值為 1,逐步遞增 1: # StopIteration 異常用於標識迭代的完成,防止出現無限迴圈的情況,在 __next__() 方法中我們可以設置在完成指定迴圈次數後觸發 StopIteration 異常來結束迭代。 # 在 20 次迭代後停止執行: class MyNumbers: def __iter__(self): self.a = 1 return self def __next__(self): if self.a <= 20: x = self.a self.a += 1 return x else: raise StopIteration myclass = MyNumbers() myiter = iter(myclass) for x in myiter: print(x)
- 怎麼樣獲取一個生成器?
# 在調用生成器運行的過程中,每次遇到 yield 時函數會暫停並保存當前所有的運行信息,返回 yield 的值, 併在下一次執行 next() 方法時從當前位置繼續運行。
- 你有用過yield對象嗎?得到的是一個什麼樣的對象?
# 調用一個生成器函數,返回的是一個迭代器對象。 # 以下實例使用 yield 實現斐波那契數列: import sys def fibonacci(n): a, b, counter = 0, 1, 0 while True: if(counter > n): return yield a a, b = b, a + b counter += 1 f = fibonacci(10) # f 是一個迭代器,由生成器返回生成 while True: try: print(next(f), end=" ") except StopIteration: sys.exit()
- 你有瞭解過python中的協程嗎?
# 協程,又稱微線程,纖程。英文名Coroutine。 # 協程的概念很早就提出來了,但直到最近幾年才在某些語言(如Lua)中得到廣泛應用。 # 子程式,或者稱為函數,在所有語言中都是層級調用,比如A調用B,B在執行過程中又調用了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。 # 所以子程式調用是通過棧實現的,一個線程就是執行一個子程式。 # 子程式調用總是一個入口,一次返回,調用順序是明確的。而協程的調用和子程式不同。 # 協程看上去也是子程式,但執行過程中,在子程式內部可中斷,然後轉而執行別的子程式,在適當的時候再返回來接著執行。 def consumer(): r = '' while True: n = yield r if not n: return print('[CONSUMER] Consuming %s...' % n) r = '200 OK' def produce(c): c.send(None) n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) print('[PRODUCER] Consumer return: %s' % r) c.close() c = consumer() produce(c)
-
技能類:
- 你有用過git嗎?
Git是分散式版本控制系統 集中式VS分散式: 集中式版本控制系統,版本庫集中存放在中央伺服器,必須要聯網才能工作,沒有歷史版本庫。 分散式版本控制系統,沒有“中央伺服器”,每個開發人員電腦上都有一個完整的版本庫。 分散式優勢:安全性更高,無需聯網,若“中央伺服器”故障,任何一個其他開發者本地都有最新的帶歷史記錄的版本庫。 主要區別在於歷史版本庫的存放,集中式歷史版本只存在於中央伺服器,而分散式中每個本地庫都有歷史記錄存放。
- 你有合過代碼嗎?
git status 查看下當前代碼狀態,有需要提交的就提交,沒有需要提交的就保持原樣 git pull 拉取遠程代碼,使得本地代碼保持最新 git branch -a 查看最新代碼所在分支 remotes/origin/HEAD -> origin/master 最新的分支會有remotes/origin/HEAD ->指明 git merge origin/master 執行合併命令把最新分支代碼合併到本地當前分支 git diff 查看衝突信息 git status 查看下狀態看看那些文件需要手工調整 git add . 把修改好的文件添加到索引 git commit -m '合併XXX分支代碼' 提交代碼 git push 把合併好的代碼推送到遠程 如果合併過程中出現問題,可以使用以下命令回退到日誌的合併之前的位置 git reset --hard commit_id
- 你有佈署過代碼嗎?
- 佈署的步驟是怎樣的?你是怎麼樣把你的代碼放到伺服器中的?
# 1、提交代碼當在本地開發&測試完畢後,將代碼合併到 master 分支,並 Push 到遠程的 Git 倉庫。 # 通常在開發中使用 Git 作為代碼版本管理工具,通過創建一個 dev 分支來進行開發工作,在發佈的時候再將代碼合併到 master 分支,這樣可以保證 master 分支永遠都是穩定的版本 git push origin master # 2、拉取並部署代碼到預發機通過以下命令從 Git 倉庫獲取到最新的代碼 git pull # 因為開發運行環境和線上運行環境的資料庫、緩存等配置的差異,拉取的代碼一般無法直接在預發機上直接運行。通常的做法是,在預發機上執行一個 shell 腳本,將線上的配置覆蓋開發環境的配置。 cp code/path/to/config-dist code/path/to/config # 預發機的主要作用是留出緩衝的空間,檢驗代碼是否線上上環境可以正常工作。對於一個 Web 項目,我們可以設置功能變數名稱的 host 配置,直接訪問預發機。 # 3、同步代碼到線上一般情況下,一個 Web 項目都會有多個業務機,通過負載均衡將請求流量平均分配的 N 台機器,以提高服務的承載能力和可用性。 # 因此,這裡面臨著一個發佈代碼到 N 台機器的問題。顯然,我們不能一臺台的發佈,這樣效率太低了。通常,我們通過在預發機上執行 shell 腳本,將代碼 Rsync 到 N 台機器上。 rsync /path/to/code [email protected]::path/to/code --exclude-list=exclude.list # 4、快速回滾發佈完代碼後,我們會在預發機的 Git 倉庫上執行 : git tag v20160522 # 記錄此次發佈的版本。如果在發佈後發現出了問題,可以在預發機的 Git 倉庫執行如下命令: git tag -l # 找出上一次發佈的版本,並回滾代碼: git reset --hard v20160521 # 然後,再通過步驟 3 的方式,將回滾的代碼同步到 N 台業務機上。 # # 作者:Ceelog # 鏈接:https://www.jianshu.com/p/79dc6e0278e2 # 來源:簡書 # 簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。
- docker你有使用過嗎?談一談
# docker鏡像可以完全看作一臺全新的電腦使用,無論什麼鏡像都是對某一東西進行了配置,然後打包後可以快速移植到需要的地方直接使用 # 省去複雜的配置工作 # 比如python web項目部署,如果是新部署,需要裝系統,配置各類環境,費時費力還容易出錯, # 而docker就可以省去配置環境的麻煩,直接把所需的包丟進去,實現分分鐘部署一個項目 # 給ubuntu安裝docker,我安裝的是docker ee: wget -qO- https://get.docker.com/ | sh # 安裝好之後,便是啟動docker sudo service docker start # 啟動後,是沒有任何鏡像的,不過可以通過pull命令獲取相關鏡像 sudo docker images sudo docker pull nginx sudo docker pull nginx 預設獲取最新版本,即TAG為latest的,如果要獲取其他版本,則需要使用 sudo docker pull nginx:xxxx # 獲取鏡像後,通過docker run使其運行起來 sudo docker ps -a 列出所有容器, 不加 -a 僅列出正在運行的,像退出了的或者僅僅只是創建了的就不列出來 sudo docker run -d -p 8800:80 --name nginx_xiao nginx #運行指定的鏡像 dudo docker run -d --privileged=true -p 83:80 --name nginx83 nginx #提升許可權 #宿主主機埠:容器內部埠 # -d 後臺運行 # -p 8800:80 是指定對外暴露的埠 容器內部用80 對應外部宿主主機的的8800 代理一樣 # --name指定容器的名字 最後的nginx 代碼要運行的鏡像名字 有tag的加上tag 如 nginx:xxx 預設為latest # 然後訪問宿主主機地址+8800埠 # pull到的鏡像肯定有很多需要修改的地方,比如配置文件等或者要自己增加些什麼玩意兒進去 sudo docker exec -it 54d26bbce3d6 /bin/bash # 通過exec命令進入到容器內部進行操作, 其中字元串部分可以是容器id或容器名字 # 進入之後就和操作新的系統一樣,操作完成之後輸入exit退出 # 那麼問題又來了, 進入容器內部並修改了東西後,怎麼生成新的鏡像供下次直接使用 sudo docker commit nginx_huang huang/nginx:v1.0 nginx_huang 表示我們剛修改的容器名字或者id huang/nginx:v1.0 為保存的鏡像名字 :後面為tag # 剛剛commit的鏡像僅僅是保存在本地的,如果要提交到網路上供其他人pull 使用呢? 如 https://cloud.docker.com/ # 1.在https://cloud.docker.com/上註冊一個賬號 # 2.提交本地鏡像到https://cloud.docker.com/上去 # 這樣別人就可以通過docker pull xiaochangwei/nginx:v1.0 來獲取並使用這個鏡像了 sudo docker commit nginx_huang huang/nginx:v1.0 鏡像名裡面包含了 我註冊的賬戶名,這裡需要一致,否則無法push # 到這裡鏡像怎麼獲取,查看,啟動,編輯,保存,提交 容器查看 都知道了,但是怎麼停止、啟動、刪除容器呢 # 1.通過 sudo docker ps -a查看存在的容器信息 # 2.通過 sudo docker start/stop/restart xxx 來啟動、停止、重啟指定的容器 # 2.通過 sudo docker rm xxx 指定容器名或者容器id來刪除,刪除前請先停止容器,保證在非運行狀態 # 同樣鏡像的刪除按如下操作 # 1.通過sudo docker images 列出所有鏡像 # 2.通過sudo docker rmi xxx 來刪除指定的鏡像,鏡像存在依賴關係,先刪除最下層,最後刪除頂層,建議根據鏡像名字來刪除
- 你平時是怎麼去debug你的代碼的?比如說有些代碼是第一次寫,你不知道你寫的是不是對的,一但出現了問題,你要怎麼去定位它,檢測它?
# 1.操作步驟: # 1-1.添加斷點:直接在標記處點擊滑鼠左鍵即可。(刪除斷點只需再點擊斷點處即可) def add(x, y): z = x + y return z def sub(x, y): z = x - y return z def debug_test(): # 點擊左側,添加斷點 a = 10 b = 5 Sum = add(a, b) Sub = sub(a, b) print(Sum) print(Sub) if __name__ == "__main__": debug_test() # 1-2.Debug下運行代碼:七星瓢蟲shift + F9 # 1-3.按照所需調試進行代碼調試。Debug的調試方式如下所示: # 分別為: # 1.show execution point (F10) 顯示當前所有斷點 # 2.step over(F8) 單步調試(若函數A記憶體在子函數a時,不會進入子函數a內執行單步調試,而是把子函數a當作一個整體,一步執行。) def add(x, y): z = x + y return z def sub(x, y): z = x - y return z def debug_test(): # 3 # 斷點處 a = 10 # 4 b = 5 # 5 Sum = add(a, b) # 6 Sub = sub(a, b) # 7 print(Sum) # 8 print(Sub) # 9 if __name__ == "__main__": # 1 debug_test() # 2 # 10 # 4.step into my code(Alt + Shift +F7) 執行下一行但忽略libraries(導入庫的語句)(目前感覺沒什麼用) def add(x, y): z = x + y # 7 return z # 8 def sub(x, y): z = x - y # 11 return z # 12 def debug_test(): # 3 # 斷點處 a = 10 # 4 b = 5 # 5 Sum = add(a, b) # 9 Sub = sub(a, b) # 13 print(Sum) # 14 print(Sub) # 15 if __name__ == "__main__": # 1 debug_test() # 2 # 16 # 5.force step into(Alt + Shift +F7) 執行下一行忽略lib和構造對象等 (目前感覺沒什麼用) # 6.step out(Shift+F8)當目前執行在子函數a中時,選擇該調試操作可以直接跳出子函數a,而不用繼續執行子函數a中的剩餘代碼。並返回上一層函數。 # 7.run to cursor(Alt +F9) 直接跳到下一個斷點 # --------------------- # 作者:放下扳手&拿起鍵盤 # 來源:CSDN # 原文:https://blog.csdn.net/william_hehe/article/details/80898031
- 你有多長時間的軟體開發經驗?
- 你有用過git嗎?
-
業務類:
-
你對電商的業務瞭解嗎?
# 電商項目用戶部分:主要分為三大類——1、用戶瀏覽商品 ,2、購買商品 ,3、管理訂單 # 電商項目管理部分:主要也為三大類——1、商品數據整合網站 ,2、接受用戶信息數據存入後臺(註冊和管理),3、處理用戶訂單問題
-
我問你一個具體的電商的業務啊:電商在大多數領域中都是有庫存的,我在下訂單的時候,我的庫存的變化,你知道嗎?(我問的是你怎麼去處理庫存的變化?)因為客戶下單的時候,有些是去減庫存了,有些不減對吧?你的理解呢?
# 電商項目用戶部分:主要分為三大類——1、用戶瀏覽商品 ,2、購買商品 ,3、管理訂單 # 電商項目管理部分:主要也為三大類——1、商品數據整合網站 ,2、接受用戶信息數據存入後臺(註冊和管理),3、處理用戶訂單問題 # 一個簡單的使用場景:一件商品的庫存只有5件,同時A用戶買了5個,B用戶買了5個,都提交數據,照成庫存不足的問題。 # 邏輯:根據一般電商商品的模型類,生成訂單一般包括訂單類(Order)和訂單詳情類(DetailOrder),這兩張表根據外鍵order_id 進行關聯,所以是同生共死的關係,所以我們在這裡用事務來控制。那麼python如何解決庫存問題呢? # python 提供了2種方法解決該問題的問題:1,悲觀鎖;2,樂觀鎖 # 悲觀鎖:在查詢商品儲存的時候加鎖 select_for_update() 在發生事務的commit或者是事務的rollback時,自動釋放該鎖,這樣其他用戶就可以接著查詢該商品。 # 樂觀鎖:樂觀鎖不是真正的鎖,在創建訂單之前查詢商品的庫存,在創建訂單詳情表前,update更新查詢數據,如果兩次查詢的庫存量一樣就創建詳情表,並減去庫存,否則,迴圈三次,如果都不一樣,就發生rollback。 # 使用場景:併發量高的時候使用悲觀鎖,缺點:加鎖消耗資源 # 併發量低的時候使用樂觀鎖,缺點:樂觀鎖迴圈耗費時間。 # --------------------- # 作者:huangyali_python # 來源:CSDN # 原文:https://blog.csdn.net/huangyali_python/article/details/79511654
-
你事務的時候有沒有出現死鎖的情況?
# 所謂死鎖: 是指兩個或兩個以上的進程或線程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程,如下就是死鎖 # 解決方法,遞歸鎖,在Python中為了支持在同一線程中多次請求同一資源,python提供了可重入鎖RLock。 from threading import Thread,Lock import time mutexA=Lock() mutexB=Lock() class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('\033[41m%s 拿到A鎖\033[0m' %self.name) mutexB.acquire() print('\033[42m%s 拿到B鎖\033[0m' %self.name) mutexB.release() mutexA.release() def func2(self): mutexB.acquire() print('\033[43m%s 拿到B鎖\033[0m' %self.name) time.sleep(2) mutexA.acquire() print('\033[44m%s 拿到A鎖\033[0m' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t=MyThread() t.start() # 這個RLock內部維護著一個Lock和一個counter變數,counter記錄了acquire的次數,從而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲得資源。上面的例子如果使用RLock代替Lock,則不會發生死鎖,二者的區別是:遞歸鎖可以連續acquire多次,而互斥鎖只能acquire一次 from threading import Thread,RLock import time mutexA=mutexB=RLock() #一個線程拿到鎖,counter加1,該線程內又碰到加鎖的情況,則counter繼續加1,這期間所有其他線程都只能等待,等待該線程釋放所有鎖,即counter遞減到0為止 class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('\033[41m%s 拿到A鎖\033[0m' %self.name) mutexB.acquire() print('\033[42m%s 拿到B鎖\033[0m' %self.name) mutexB.release() mutexA.release() def func2(self): mutexB.acquire() print('\033[43m%s 拿到B鎖\033[0m' %self.name) time.sleep(2) mutexA.acquire() print('\033[44m%s 拿到A鎖\033[0m' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t=MyThread() t.start()
-
你剛剛說到的下單之後的庫存鎖的問題?你是在下單的時候去扣庫存,還是在支付完成了以後去扣?
# 一、扣減庫存的三種方案 # (1)下單減庫存 # 用戶下單時減庫存 # 優點:實時減庫存,避免付款時因庫存不足減庫存的問題 # 缺點:惡意買家大量下單,將庫存用完,但是不付款,真正想買的人買不到 # (2)付款減庫存 # 下單頁面顯示最新的庫存,下單時不會立即減庫存,而是等到支付時才會減庫存。 # 優點:防止惡意買家大量下單用光庫存,避免下單減庫存的缺點 # 缺點:下單頁面顯示的庫存數可能不是最新的庫存數,而庫存數用完後,下單頁面的庫存數沒有刷新,出現下單數超過庫存數,若支付的訂單數超過庫存數,則會出現支付失敗。 # (3)預扣庫存 # 下單頁面顯示最新的庫存,下單後保留這個庫存一段時間(比如10分鐘),超過保留時間後,庫存釋放。若保留時間過後再支付,如果沒有庫存,則支付失敗。例如:要求30分鐘內支付訂單。 # 優點:結合下單減庫存的優點,實時減庫存,且緩解惡意買家大量下單的問題,保留時間內未支付,則釋放庫存。 # 缺點:保留時間內,惡意買家大量下單將庫存用完。併發量很高的時候,依然會出現下單數超過庫存數。 # 二、如何解決惡意買家下單的問題 # 這裡的惡意買家指短時間內大量下單,將庫存用完的買家。 # (1)限制用戶下單數量 # 優點:限制惡意買家下單 # 缺點:用戶想要多買幾件,被限制了,會降低銷售量 # (2)標識惡意買家 # 優點:賣家設定一個備用庫存,當支付時,庫存已用完,扣減備用庫存數,這就是常見的補貨場景 # 缺點:因高併發場景下,數據可能存在不一致性的問題 # 三、如何解決下單成功而支付失敗(庫存不足)的問題 # (1)備用庫存 # 商品庫存用完後,如果還有用戶支付,直接扣減備用庫存。 # 優點:緩解部分用戶支付失敗的問題 # 缺點:備用庫存只能緩解問題,不能從根本上解決問題。另外備用庫存針對普通商品可以,針對特殊商品這種庫存少的,備用庫存量也不會很大,還是會出現大量用戶下單成功卻因庫存不足而支付失敗的問題。 # 四、如何解決高併發下庫存超賣的場景 # 庫存超賣最簡單的解釋就是多成交了訂單而發不了貨。 # 場景: # 用戶A和B成功下單,在支付時扣減庫存,當前庫存數為10。因A和B查詢庫存時,都還有庫存數,所以A和B都可以付款。 # A和B同時支付,A和B支付完成後,可以看做兩個請求回調後臺系統扣減庫存,有兩個線程處理請求,兩個線程查詢出來的庫存數 inventory=10, # 然後A線程更新最終庫存數 lastInventory=inventory - 1 = 9, # B線程更新庫存數 lastInventory=inventory - 1 = 9。 # 而實際最終的庫存應是8才對,這樣就出現庫存超賣的情況,而發不出貨。 # 那如何解決庫存超賣的情況呢? # 1.SQL語句更新庫存時,如果扣減庫存後,庫存數為負數,直接拋異常,利用事務的原子性進行自動回滾。 # 2.利用SQL語句更新庫存,防止庫存為負數 # UPDATE [庫存表] SET 庫存數 - 1 WHERE 庫存數 - 1 > 0 # 如果影響條數大於1,則表示扣減庫存成功,否則訂單失敗,並退款。 # 五、秒殺場景下如何扣減庫存 # (1)下單減庫存 # 因秒殺場景下,大部分用戶都是想直接購買商品的,可以直接用下單減庫存。 # 大量用戶和惡意用戶都是同時進行的,區別是正常用戶會直接購買商品,惡意用戶雖然在競爭搶購的名額,但是獲取到的資格和普通用戶一樣,所以下單減庫存在秒殺場景下,惡意用戶下單並不能造成之前說的缺點。 # 而且下單直接扣減庫存,這個方案更簡單,在第一步就扣減庫存了。 # (2)將庫存放到redis緩存中 # 查詢緩存要比查詢資料庫快,所以將庫存數放在緩存中,直接在緩存中扣減庫存。然後在通過MQ非同步完成資料庫處理。 # (3)使用量自增方式 # 可以先增加已使用量,然後與設定的庫存進行比較,如果超出,則將使用量減回去。 # # 項目中用到了很多機制,但是沒有總結出來,學習架構需要不斷地總結。 # # 六 第三方支付 # 1 支付成功多次回調:把減庫存放在微信支付的成功回調URL的方法裡面。但是微信支付成功之後微信支付平臺會發送8次請求到回調地址。這樣的做法就會導致庫存減少,一定要驗證返回的編號是否已經完成扣減。
-
如果用戶不支付,你的庫存怎麼辦?(我還是沒有聽到你說,處理庫存的點)
# 設置支付時間,下單應該是鎖定庫存但是不減庫存。如果你整個下單支付是隊列方式的話,就支付完成之後立即減庫存。超時就釋放掉鎖
-
如果支付失敗了,你對庫存有一個什麼樣的動作?
# 支付完成後對庫存進行更新,更新庫存在事務中進行同時加鎖,加的鎖要避免堵塞,支付失敗或者支付異常回滾,重新更新庫存 # 方案1:在下單就鎖定庫存 # 優點:可以解決庫存減扣問題 # 缺點:體驗差,如果只下單未付款,庫存被鎖定,讓有意願購買的用戶無從下單,對銷售業務有很大影響; # # 方案2:支付後減扣庫存 # 優點:防止惡意下單,只要有足夠的實際庫存,隨便多少意向客戶下單 # 缺點:下單頁面顯示的庫存數可能不是最新的庫存數,其他用戶可能提示庫存不足,可能出現超賣問題。 # # 方案3:調起支付界面前鎖定庫存 # # 優點:防止惡意下單,只要有足夠的實際庫存,隨便多少意向客戶下單 # 缺點:體驗差,有可能在支付時提示庫存被其他用戶鎖定,提示庫存不足 # # 方案4:下單占庫存根據庫存大小決定庫存鎖定時間 # # 庫存充足下單占庫存,設定庫存最大占用時間,按庫存大小限購數量按策略縮減庫存占用時間 # # # # # # 具體以哪種方案為主還是要看公司的業務做決定 # # # # # 下麵主要講解方案3 支付前鎖定庫存的的實現步驟,以下是使用到的關鍵表 # # ------------------------------------------------------------------------------------- # 訂單表 # 訂單唯一編號 庫存鎖定狀態(1庫存已鎖定 2庫存已釋放 3庫存已確認,庫存減扣成功) # ------------------------------------------------------------------------------------- # 訂單詳細表 # 訂單編號 商品id 購買數量 # ------------------------------------------------------------------------------------- # 商品庫存表 # 商品id 庫存數量 實際鎖定庫存數量 預鎖定庫存數量 限購數量 # ------------------------------------------------------------------------------------- # # A商品庫存 1個 # # # 用戶1 下單1個 # 用戶2 下單1個 # # # 業務場景及解決方式: # 1.支付前預占庫存 :用戶1和用戶2可能同時點擊支付按鈕,此時對於商品鎖定庫存來說只能有一個用戶會鎖定A商品的庫存,剩下一個肯定鎖定失敗,此時應該提示其中一個用戶(庫存可能不足,請稍後再試),這裡更新庫存減扣應該都有前置條件的或者說版本號 # # 2.限制支付時間,需要設置一個支付時間 # # (設置三方支付過期時間為30分鐘)調起支付頁面鎖定庫存30分鐘,30分鐘後還未支付則還原預減庫存 # # 3.檢測惡意下單用戶加入到店鋪黑名單 # # 4.加入限購 # # 5.優化方式 # 另一個用戶跳轉到三方支付界面, # 如果此用戶取消支付,客戶端應該發送一條消息告訴服務端恢復庫存鎖定, # 如果此用戶支付成功,客戶端應該發送一條消息告訴服務端確認減扣庫存, # 客戶端可能因某種情況發送失敗,此時要考慮使用定時任務恢復超過多久庫存還處於(根據鎖定狀態)鎖定中的訂單商品庫存,應該先調用支付系統關閉原有未完成支付的訂單,然後再恢復商品鎖定庫存 # 支付系統非同步通知支付成功修改庫存,此時根據庫存鎖定狀態進行不同的業務處理(1庫存已鎖定 2庫存已釋放 3庫存已確認,庫存減扣成功) # --------------------- # 作者:www.weixuehu.com #
-