微信搜「程式設計師檸檬」分享程式設計學習路線和學習資源,本文已收錄於Github:https://github.com/imcoderlemon/CodeClass
內含原創乾貨文章,千本計算機電子書,谷歌、阿里大神開源LeetCode題解,各類程式設計資源。
今年春節響應國家號召在家宅著抵抗疫情,拜年也改用微信紅包,春節發了很多也搶了很多微信紅包,也算支援了公司業務,微信支付融入生活,搶紅包已經是非常平常的事情。
搶紅包這一簡單的動作,每一次都是對紅包服務後臺的一次請求,在春節期間海量的服務請求下,其實是一個很典型的高並行程式設計模型。後臺開發程式設計師都有一個共識:實現一個功能很容易,難的是大量請求下提高服務效能。
在程式設計師眼裡,大家搶的不是紅包,是紅包後臺服務的鎖 !這裡的鎖不是我們日常生活中的鎖,後臺服務程式設計中鎖的概念:
實現多個程序或執行緒互斥的存取共用資源的一種機制
為便於說明,我們簡化模型,約定搶紅包服務是多執行緒服務,搶紅包操作包含以下3個步驟:
假設你發了100塊錢紅包,1000個人1秒內同時來搶(高並行),如果不加鎖是這樣的情況:
怎麼解決這個問題呢? 就用到我們上面說的加鎖來解決。
實現鎖的方式有很多,這裡列舉幾種常見的分類
顧名思義就是悲觀的做最壞打算的鎖機制,佔有鎖期間獨佔資源。
悲觀鎖把搶紅包這三個步驟打包成一個整體做成互斥操作,「在我搶了沒更新資料之前你別來查餘額,查到也不準確」。也可以類比資料庫的事務來理解。
事務必須具備以下四個屬性,簡稱ACID 屬性:
原子性(Atomicity):事務是一個完整的操作。事務的各步操作是不可分的(原子的);要麼都執 行,要麼都不執行
一致性(Consistency):當事務完成時,資料必須處於一致狀態
隔離性(Isolation):對資料進行修改的所有並行事務是彼此隔離的,這表明事務必須是獨立的,它不應以任何方式依賴於或影響其他事務
永久性(Durability):事務完成後,它對資料庫的修改被永久保持,事務紀錄檔能夠保持事務的永久性
它悲觀的認為你每次去搶紅包必然有其他人也同時在搶,所以你這條執行緒在搶的時候要獨佔資源,其他執行緒需要阻塞掛起等待你搶完才能進來搶,掛起的執行緒就幹不了其他事了。
魯迅先生說過,浪費CPU資源就是浪費生命!
而一旦你搶完紅包釋放了鎖,其他在等待中的執行緒又要搶佔資源、搶到了還要恢復執行緒上下文。
CPU不斷的切換執行緒上下文非常浪費伺服器資源,嚴重的會導致不能及時處理後續搶紅包請求,需要想辦法提高效率,於是有了樂觀鎖
樂觀鎖是對悲觀鎖的改進,樂觀的認為加鎖的時候沒有競爭,樂觀鎖不阻塞執行緒。
一種實現樂觀鎖的方法是資料庫內紅包餘額增加版本號,初始版本號是0,每次搶完紅包版本號加1後再去更新餘額,只有更新的版本號大於資料庫內的版本號才認為是合法的,予以更新;否則不予更新,執行緒不阻塞可以稍後重試,避免頻繁切換執行緒上下文。
樂觀鎖在搶紅包的步驟1、2不做加鎖判斷,在步驟3的時候才做加鎖判斷版本號。
可以看到,樂觀鎖在加鎖失敗的時候不掛起執行緒等待,避免了執行緒上下文頻繁的切換,提高紅包服務處理效能。
上面兩種鎖的形式都是基於對資料庫的更新來做的,在大請求高並行的時候,頻繁的存取資料庫,尤其是樂觀鎖重試會對資料庫產生很大的衝擊,在實際生產環境要儘量減少對資料庫的存取。
Redis 是一個開源(BSD許可)的,記憶體中的資料結構儲存系統,它可以用作資料庫、快取和訊息中介軟體。也可以用redis實現分散式鎖,與資料庫互動兩次:第一次獲取紅包餘額,第二次搶完更新紅包狀態。搶紅包和中間過程更新操作都在記憶體中進行,這可比資料庫操作快了幾個數量級,顯著改善服務並行效能。
redis分散式鎖:
利用Redis的SET操作在記憶體中儲存key-value鍵值對,加鎖就是獲取這個鍵值對的值,解鎖就是刪除這個鍵值對。
分散式鎖也不阻塞執行緒,關於這種分散式鎖的實現不在這裡展開說明,可以參考我另一篇公眾號文章: redis分散式鎖的3種實現方式分析,詳細分析了幾種分散式鎖特點和利弊。
原創不易,看到這裡動動手指,各位的「三連」是對我持續創作的最大支援,我們下篇文章再見。
微信搜「程式設計師檸檬」分享程式設計學習路線和學習資源,本文已收錄於Github:https://github.com/imcoderlemon/CodeClass
內含原創乾貨文章,千本計算機電子書,谷歌、阿里大神開源LeetCode題解,各類程式設計資源。