互聯網線上項目開發最大坑點-併發衝突處理

互聯網線上項目開發最大坑點-併發衝突處理

大家可能都有這樣的經驗,自個兒在家裏很多功能很容易實現,一下就做完了,但是在做線上產品的時候,就變得無比複雜,需要花費很多的時間。

自己寫的程序在家跑,所有的業務都很正常,一旦發佈到線上,就會出現很多bug,而且很多bug在測試的時候很難重現,這是在互聯網開發的時候經常遇到的現象。

這些難以重現的bug,大部分是由於併發產生的,爲了能讓大家充分的瞭解併發的問題,並且建立併發環境下的程序設計思維,我們爲大家準備了幾個小案例

大家來看一下,這個網站典型的場景,遇到內容比較多的情況下,我們會使用分頁

如果是在移動端,我們採用的是上拉刷新和下拉刷新,比如手機微博

分頁功能由來已久,我們現在來看下常見的分頁有什麼問題呢?

大家看下我展示的這個項目是用thinkPHP來編寫的。很多流行的框架和開源項目都對分頁做了支持,那麼大家來看我演示一個案例。

看到的現在的第一頁是最新的新聞,如果我點擊下一頁,相對來說是老一點的新聞。

這個時候我們來做一件事情,我們新打開一個瀏覽器窗口,剛剛的分頁頁面不要關閉,我們在後臺裏面再發布5篇新聞,

假設我們在後臺發佈新聞的同時,用戶還在瀏覽第二頁,當發佈完畢以後,用戶又點了下一頁,我們觀察到了奇特的現象

我們看到第3頁和剛剛看到的第二頁完全一樣,原本用戶點下一頁希望看到再早一些的新聞,結果看到了一樣的數據,是我們的分頁程序出問題了嗎?非也。

當我們再點下一頁,程序又正常工作了。剛剛出現的奇怪的那一幕,其實就是由於併發產生的衝突。

在什麼地方我們做了併發操作了呢?其實就是在一個用戶瀏覽頁面的同時,還有人在往數據庫裏面寫入數據。你會發現thinkPHP這樣的框架,還是PHPCMS這樣的開源系統,他們都存在這樣的bug。

併發產生的問題,往往難以捕捉,更難以重現,而我們準備的這個案例,算是併發案例衝突中相對容易重新的典型案例

我們如果調整一下剛剛操作的順序,我們會得到一些其他的結果。

比如說:我們分頁條目數是每頁5條,在用戶瀏覽某一頁的時候,後臺管理員發佈了新的新聞,新聞的數量小於5條的情況下,我們點下一頁,會看到有幾條重複的新聞,也有更早的新聞

這樣影響用戶體驗,但畢竟我們點一下一的頁時候內容還是能接的上的,用戶瀏覽某一頁的時候,後臺發佈的新聞數量大於分頁條目數(5條)

那麼再點擊下一頁,其中會有若干條新聞被跳過去了,無論用戶點多少次下一頁,都看不到那些條目,這種產生的併發衝突後果是很嚴重的,而且普遍存在。

那麼再點擊下一頁,其中會有若干條新聞被跳過去了,無論用戶點多少次下一頁,都看不到那些條目,這種產生的併發衝突後果是很嚴重的,而且普遍存在。

他具有很好的隱蔽性,在過去很多年幾乎不被察覺

在大門戶網站時代,網站編輯並不會頻繁的發佈新聞,而用戶也很少守着新聞列表去逐篇閱讀,然而在微博誕生以後,這種問題就暴露出來了。

由於社區類應用信息的產生室友用戶產生的,不在是靠編輯在後臺發佈的,就好像微博,每時每分秒,都有很多用戶分享心得內容

這樣一來你在查看微博列表的同時,內容就已經更新 了 很多,而且很多人打發無聊的時間,很多人也會去逐一查看,不停的上劃,把所有遺漏的條目全部看一遍

這是如果項目設計不合理,產生併發事故,就會對用戶體驗造成極大的影響。

待會我會再講併發衝突解決方案的時候告訴大家如何解決這種問題

現在我們來看一個更爲常見的例子,大家可能還記得,我們在微信羣搞的抽獎的互動,我們的上百份禮品,瞬間就被秒殺光。

在做這類搶購與秒殺抽獎等應用的時候,併發將導致更多的問題。通常比較容易出現的bug有實際商品的訂單量大於庫存量。

通俗點來說就是,明明已經售完,但還是有用戶買到了商品,庫存值變爲負的。

又或者明明秒殺到商品的用戶,訂單失敗。

還有企業的項目,在商品秒殺期間,明明用戶數量不多,卻導致服務器宕機。諸如此類的問題就不一一列舉了。

由於是文字直播,打字速度比較慢,大家可以看現在正在進行的視屏直播

我們接下來來看一下用常規思維來梳理業務流程程序是怎樣編寫的。

還是以商城秒殺業務爲例。首先我們需要用產品庫存這樣的一個字段來記錄庫存信息,每當有用戶購買商品的時候,先查看庫存,判斷庫存大於0的時候,用戶才能購買

當用戶完成購買流程後,將庫存數量減一,直到所有商品賣完,重複此過程,直到庫存賣完秒殺活動結束。

如果按常規的思路來設計,這樣的流程是沒有問題的,商品畢竟是一件一件賣出的,但是,在互聯網併發的情況下,就完全不是這樣的。

要知道熱銷商品很有可能在同一時間,有多個用戶都在進行購買流程操作

按照之前的業務設計,假如有ABCD 4個用戶同時在秒殺某件商品時,庫存僅剩2件,按照之前的業務流程設計,查詢庫存大於0,就可以繼續後面的購買操作並付款

然而當任意用戶購買成功後庫存即減一,ABCD4個用戶都認爲自己查詢時都有庫存,因此他們都可以完成購買流程,導致的結果就是庫存數爲負數。

也就是說,商品實際銷售量大於活動的商品數量,這樣會導致公司的虧損。

有些公司爲了解決這個問題,採用了一種思路,雖說4個人同時操作,但是交易成功的這次網絡請求到達服務器的時間總會有個先後順序

那麼可以將訂單支付成功之後的庫存減一之後的值也隨訂單保存,如果這個值小於0,就證明有用戶購買了產品,已經是賣完的,於是標記訂單失敗。

這樣看上去避免公司造成額外的損失,但卻會給用戶帶來極大的不滿,是一種極差的用戶體驗。它並沒有真正的解決我們的問題。

當然還有些公司解決方案也不高明,我們知道無論是數據庫還是文件都可以給他加鎖,在很早期的程序設計和軟件開發裏面,鎖是解決併發問題的萬能靈藥。

無論是c++,或java,提到多進程或多線程的時候,往往也會提到鎖這個字。那麼作爲最早期的通用解決方案,用到秒殺方案是否合適呢?

我們來看一下加鎖後的工作流程:還是ABCD 4個用戶同時秒殺,他們都去查詢庫存。當某一個用戶,比如A的請求,優先到達時,我們就將數據表鎖住,不讓其他的數據庫連接來動這張表,待用戶A完成購買流程,將庫存量減一後,把鎖打開,其他的連接纔可以再次操作這張表。

如此一來,可以保障一個用戶查看庫存以及庫存減一這段時間內,不可能還有其他用戶可以對錶做出修改,這一併發衝突的問題就沒有了。不過這樣的做法真的合適嗎?

要知道ABCD 4個用戶都是在同一時間段去秒殺的,由於A用戶在操作中鎖表,導致其他用戶只能等待,而且A完成整個業務需要消耗一段時間,只能等A完成以後其他用戶才能操作

這樣一來單位時間內的業務處理量會大幅降低,我們所看到的現象就是網站卡死,或者服務器宕機

關於併發性能如何設計,我們可能需要單獨的一次或幾次課來爲大家講解。不過鎖這種很原始的併發衝突解決方案,我們可以看到他並不適合互聯網項目。

之所以大家會有併發衝突的程序,是因爲大部分程序員,思維模式都是線性的。

作爲程序邏輯思維來講,線性思維是沒有錯的,因爲計算機執行指令的時候本身就是線性的。然而如果把業務也看做是線性的,就會產生問題了。

任何一個程序操作,他都會消耗一定的時間,即便你的CPU速度再快,也只是縮短了這個時間範圍而已,..

如果只有一個用戶操作,比如我們在後臺發佈文章,看自己發佈的新聞,我們是無法感知併發帶來的衝突的。這就對我們的程序員提出了更高的要求。

理論上來講,所有跨越時間段的操作過程中如果涉及到數據修改就會有可能產生併發衝突,因此我們在設計程序的時候,要保障應用程序的質量,就需要去做併發衝突處理,只是實現業務需求與實現業務的同時做好質量需求,就是好程序員與壞程序員的差別。

那麼分析了產生併發衝突的原因以後,就比較容易思考解決方案了。大體的思路有兩種:一種是將併發操作變爲單線操作,另一種是讓所有跨越時間的段的操作不去更改數據。

我們現在來看一下分頁,或者上拉或者下拉刷新的解決方法。我們剛剛提出的2種的解決思路,哪一種比較合適呢?對於發佈數據和瀏覽數據,比如微博,我們有可能把這種併發操作變爲單線操作嗎?好像不太容易。

那麼我們能夠走得路就剩下第二條,也就是跨時間段的過程中不要改變數據,我們剛剛產生的bug到底是什麼數據改變導致了bug。回顧下我們的代碼實現的本質,就容易找到其中的緣由了。

通常我們在實現分頁的時候,首頁看到的是最新的數據,那麼從數據庫中取數據的sel語句是select * from news order by desc limt 0,10,這樣取到最新的數據,如果點擊下一頁,查詢語句不變,只是分頁條目不在是第0-9,而是第10-19條,如果在這個過程中有新的數據插入,我們會發現有一個東西變了,就是原有數據在數據庫的排序序號變了,如果我新發布一條數據,原來的第一條最新的新聞就會變成第二條,原來的第10條會變成第11條。這就是一個時間段內的操作過程中有數據發生了改變。

既然我們無法把這樣的併發操作變成單線操作,我們可以選擇不讓數據發生改變,這樣併發bug就可以得到很好的解決了。

需要了解詳細解決方案的,我會把無bug程序實例分享給大家。課後可以聯繫赫赫要資料,或者是聽我們的視屏直播課,陳老師有詳細的解決。

跨時間段的讓數據不改變不好走,那我們可以選擇第一種思路,讓併發操作變爲單線操作,之前提到的加鎖是解決方案之一,但是對用戶體驗不好性能很差,基本上無法再互聯網項目中使用。如果不能加鎖,那麼常用的解決方案是什麼?

我們可以用隊列。如果我們將所有的用戶請求進行排隊,有一個服務來訂閱這個隊列,那麼不管有多少用戶訪問,最終到服務器端,處理服務的就只有一個進程。這樣就實現了一個由併發操作轉換成單線操作。

關於消息隊列的使用以及本地服務的相關知識,在源代碼的技術經理/架構師在線課中爲大家詳盡的展示。

這個課程的衆籌將會在明天晚上8點開始,衆籌的規則和細節都已經出來了,前期衆籌的費用主要用來購買阿里雲的服務,搭建實驗環境。

今天的文字直播到此就結束了。希望對這些內容感興趣的朋友多多支持一下,關注一下源代碼平臺。由於微信文字同步直播,很多實現細節不太方便展示,大家可以看優酷直播的視屏錄像。

發佈了59 篇原創文章 · 獲贊 4 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章