XDL_NO.15 互聯(lián)網(wǎng)線上項(xiàng)目開發(fā)最大坑點(diǎn)-并發(fā)沖突處理

互聯(lián)網(wǎng)線上項(xiàng)目開發(fā)最大坑點(diǎn)-并發(fā)沖突處理

程序猿 都有的經(jīng)歷蘸鲸!

  大家可能都有這樣的經(jīng)驗(yàn)疙描,自個兒在家里很多功能很容易實(shí)現(xiàn)管行,一下就做完了格带,但是在做線上產(chǎn)品的時候,就變得無比復(fù)雜饿悬,需要花費(fèi)很多的時間令蛉。
  自己寫的程序在家跑,所有的業(yè)務(wù)都很正常狡恬,一旦發(fā)布到線上珠叔,就會出現(xiàn)很多bug,而且很多bug在測試的時候很難重現(xiàn)弟劲,這是在互聯(lián)網(wǎng)開發(fā)的時候經(jīng)常遇到的現(xiàn)象运杭。

這些難以重現(xiàn)的bug,大部分是由于并發(fā)產(chǎn)生的函卒,為了能讓大家充分的了解并發(fā)的問題,并且建立并發(fā)環(huán)境下的程序設(shè)計思維撇眯,我們?yōu)榇蠹覝?zhǔn)備了幾個小案例:

1报嵌、大家來看一下,這個網(wǎng)站典型的場景熊榛,遇到內(nèi)容比較多的情況下锚国,我們會使用分頁;如果是在移動端玄坦,我們采用的是上拉刷新和下拉刷新血筑,比如手機(jī)微博分頁功能由來已久,我們現(xiàn)在來看下常見的分頁有什么問題呢煎楣?

案列 1

這個項(xiàng)目是用thinkPHP來編寫的豺总。很多流行的框架和開源項(xiàng)目都對分頁做了支持,那么大家來看我演示一個案例择懂。

mmexport1472088993660.jpg

看到的現(xiàn)在的第一頁是最新的新聞喻喳,如果我點(diǎn)擊下一頁,相對來說是老一點(diǎn)的新聞困曙。

這個時候我們來做一件事情表伦,我們新打開一個瀏覽器窗口,剛剛的分頁頁面不要關(guān)閉慷丽,我們在后臺里面再發(fā)布5篇新聞蹦哼,

假設(shè)我們在后臺發(fā)布新聞的同時,用戶還在瀏覽第二頁要糊,當(dāng)發(fā)布完畢以后纲熏,用戶又點(diǎn)了下一頁,我們觀察到了奇特的現(xiàn)象

我們看到第3頁和剛剛看到的第二頁完全一樣,原本用戶點(diǎn)下一頁希望看到再早一些的新聞赤套,結(jié)果看到了一樣的數(shù)據(jù)飘痛,是我們的分頁程序出問題了嗎?非也容握。

當(dāng)我們再點(diǎn)下一頁宣脉,程序又正常工作了。剛剛出現(xiàn)的奇怪的那一幕剔氏,其實(shí)就是由于并發(fā)產(chǎn)生的沖突塑猖。

mmexport1472089172058.jpg

在什么地方我們做了并發(fā)操作了呢?其實(shí)就是在一個用戶瀏覽頁面的同時谈跛,還有人在往數(shù)據(jù)庫里面寫入數(shù)據(jù)羊苟。你會發(fā)現(xiàn)thinkPHP這樣的框架,還是PHPCMS這樣的開源系統(tǒng)感憾,他們都存在這樣的bug蜡励。

并發(fā)產(chǎn)生的問題,往往難以捕捉阻桅,更難以重現(xiàn)凉倚,而我們準(zhǔn)備的這個案例,算是并發(fā)案例沖突中相對容易重新的典型案例

我們?nèi)绻{(diào)整一下剛剛操作的順序嫂沉,我們會得到一些其他的結(jié)果稽寒。

比如說:我們分頁條目數(shù)是每頁5條,在用戶瀏覽某一頁的時候趟章,后臺管理員發(fā)布了新的新聞杏糙,新聞的數(shù)量小于5條的情況下,我們點(diǎn)下一頁蚓土,會看到有幾條重復(fù)的新聞宏侍,也有更早的新聞

這樣影響用戶體驗(yàn),但畢竟我們點(diǎn)一下一的頁時候內(nèi)容還是能接的上的北戏,用戶瀏覽某一頁的時候负芋,后臺發(fā)布的新聞數(shù)量大于分頁條目數(shù)(5條)

那么再點(diǎn)擊下一頁,其中會有若干條新聞被跳過去了嗜愈,無論用戶點(diǎn)多少次下一頁旧蛾,都看不到那些條目,這種產(chǎn)生的并發(fā)沖突后果是很嚴(yán)重的蠕嫁,而且普遍存在锨天。

那么再點(diǎn)擊下一頁,其中會有若干條新聞被跳過去了剃毒,無論用戶點(diǎn)多少次下一頁病袄,都看不到那些條目搂赋,這種產(chǎn)生的并發(fā)沖突后果是很嚴(yán)重的,而且普遍存在。

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

在大門戶網(wǎng)站時代蜂筹,網(wǎng)站編輯并不會頻繁的發(fā)布新聞,而用戶也很少守著新聞列表去逐篇閱讀宋欺,然而在微博誕生以后,這種問題就暴露出來了胰伍。

由于社區(qū)類應(yīng)用信息的產(chǎn)生室友用戶產(chǎn)生的齿诞,不在是靠編輯在后臺發(fā)布的,就好像微博骂租,每時每分秒祷杈,都有很多用戶分享心得內(nèi)容

這樣一來你在查看微博列表的同時,內(nèi)容就已經(jīng)更新 了 很多渗饮,而且很多人打發(fā)無聊的時間但汞,很多人也會去逐一查看,不停的上劃互站,把所有遺漏的條目全部看一遍

這是如果項(xiàng)目設(shè)計不合理特占,產(chǎn)生并發(fā)事故,就會對用戶體驗(yàn)造成極大的影響云茸。

案列2

現(xiàn)在我們來看一個更為常見的例子,大家可能還記得谤饭,我們在微信群搞的抽獎的互動标捺,我們的上百份禮品,瞬間就被秒殺光揉抵。
在做這類搶購與秒殺抽獎等應(yīng)用的時候亡容,并發(fā)將導(dǎo)致更多的問題。通常比較容易出現(xiàn)的bug有實(shí)際商品的訂單量大于庫存量冤今。

通俗點(diǎn)來說就是闺兢,明明已經(jīng)售完,但還是有用戶買到了商品戏罢,庫存值變?yōu)樨?fù)的屋谭。

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

還有企業(yè)的項(xiàng)目桐磁,在商品秒殺期間,明明用戶數(shù)量不多讲岁,卻導(dǎo)致服務(wù)器宕機(jī)我擂。諸如此類的問題就不一一列舉了衬以。

我們接下來來看一下用常規(guī)思維來梳理業(yè)務(wù)流程程序是怎樣編寫的。

還是以商城秒殺業(yè)務(wù)為例校摩。首先我們需要用產(chǎn)品庫存這樣的一個字段來記錄庫存信息看峻,每當(dāng)有用戶購買商品的時候,先查看庫存衙吩,判斷庫存大于0的時候互妓,用戶才能購買

當(dāng)用戶完成購買流程后,將庫存數(shù)量減一分井,直到所有商品賣完车猬,重復(fù)此過程,直到庫存賣完秒殺活動結(jié)束尺锚。

如果按常規(guī)的思路來設(shè)計珠闰,這樣的流程是沒有問題的,商品畢竟是一件一件賣出的瘫辩,但是伏嗜,在互聯(lián)網(wǎng)并發(fā)的情況下,就完全不是這樣的伐厌。

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

按照之前的業(yè)務(wù)設(shè)計,假如有ABCD 4個用戶同時在秒殺某件商品時挣轨,庫存僅剩2件军熏,按照之前的業(yè)務(wù)流程設(shè)計,查詢庫存大于0卷扮,就可以繼續(xù)后面的購買操作并付款

然而當(dāng)任意用戶購買成功后庫存即減一荡澎,ABCD4個用戶都認(rèn)為自己查詢時都有庫存,因此他們都可以完成購買流程晤锹,導(dǎo)致的結(jié)果就是庫存數(shù)為負(fù)數(shù)摩幔。

也就是說,商品實(shí)際銷售量大于活動的商品數(shù)量鞭铆,這樣會導(dǎo)致公司的虧損或衡。

有些公司為了解決這個問題,采用了一種思路车遂,雖說4個人同時操作封断,但是交易成功的這次網(wǎng)絡(luò)請求到達(dá)服務(wù)器的時間總會有個先后順序

那么可以將訂單支付成功之后的庫存減一之后的值也隨訂單保存,如果這個值小于0舶担,就證明有用戶購買了產(chǎn)品澄港,已經(jīng)是賣完的,于是標(biāo)記訂單失敗柄沮。

這樣看上去避免公司造成額外的損失回梧,但卻會給用戶帶來極大的不滿废岂,是一種極差的用戶體驗(yàn)。它并沒有真正的解決我們的問題狱意。

當(dāng)然還有些公司解決方案也不高明湖苞,我們知道無論是數(shù)據(jù)庫還是文件都可以給他加鎖,在很早期的程序設(shè)計和軟件開發(fā)里面详囤,鎖是解決并發(fā)問題的萬能靈藥财骨。

無論是c++,或java,提到多進(jìn)程或多線程的時候藏姐,往往也會提到鎖這個字隆箩。那么作為最早期的通用解決方案,用到秒殺方案是否合適呢羔杨?

我們來看一下加鎖后的工作流程:還是ABCD 4個用戶同時秒殺捌臊,他們都去查詢庫存。當(dāng)某一個用戶兜材,比如A的請求理澎,優(yōu)先到達(dá)時,我們就將數(shù)據(jù)表鎖住曙寡,不讓其他的數(shù)據(jù)庫連接來動這張表糠爬,待用戶A完成購買流程,將庫存量減一后举庶,把鎖打開执隧,其他的連接才可以再次操作這張表。

如此一來户侥,可以保障一個用戶查看庫存以及庫存減一這段時間內(nèi)殴玛,不可能還有其他用戶可以對表做出修改,這一并發(fā)沖突的問題就沒有了添祸。不過這樣的做法真的合適嗎?

要知道ABCD 4個用戶都是在同一時間段去秒殺的寻仗,由于A用戶在操作中鎖表刃泌,導(dǎo)致其他用戶只能等待,而且A完成整個業(yè)務(wù)需要消耗一段時間署尤,只能等A完成以后其他用戶才能操作

這樣一來單位時間內(nèi)的業(yè)務(wù)處理量會大幅降低耙替,我們所看到的現(xiàn)象就是網(wǎng)站卡死,或者服務(wù)器宕機(jī)

關(guān)于并發(fā)性能如何設(shè)計曹体,我們可能需要單獨(dú)的一次或幾次課來為大家講解俗扇。不過鎖這種很原始的并發(fā)沖突解決方案,我們可以看到他并不適合互聯(lián)網(wǎng)項(xiàng)目箕别。

究根詰底

之所以大家會有并發(fā)沖突的程序铜幽,是因?yàn)榇蟛糠殖绦騿T滞谢,思維模式都是線性的。

作為程序邏輯思維來講除抛,線性思維是沒有錯的狮杨,因?yàn)橛嬎銠C(jī)執(zhí)行指令的時候本身就是線性的。然而如果把業(yè)務(wù)也看做是線性的到忽,就會產(chǎn)生問題了橄教。

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

如果只有一個用戶操作翩迈,比如我們在后臺發(fā)布文章持灰,看自己發(fā)布的新聞,我們是無法感知并發(fā)帶來的沖突的帽馋。這就對我們的程序員提出了更高的要求搅方。

理論上來講,所有跨越時間段的操作過程中如果涉及到數(shù)據(jù)修改就會有可能產(chǎn)生并發(fā)沖突绽族,因此我們在設(shè)計程序的時候姨涡,要保障應(yīng)用程序的質(zhì)量,就需要去做并發(fā)沖突處理吧慢,只是實(shí)現(xiàn)業(yè)務(wù)需求與實(shí)現(xiàn)業(yè)務(wù)的同時做好質(zhì)量需求涛漂,就是好程序員與壞程序員的差別。

那么分析了產(chǎn)生并發(fā)沖突的原因以后检诗,就比較容易思考解決方案了匈仗。大體的思路有兩種:一種是將并發(fā)操作變?yōu)閱尉€操作,另一種是讓所有跨越時間的段的操作不去更改數(shù)據(jù)逢慌。

解決方案

案例1

我們現(xiàn)在來看一下分頁悠轩,或者上拉或者下拉刷新的解決方法。我們剛剛提出的2種的解決思路攻泼,哪一種比較合適呢火架?對于發(fā)布數(shù)據(jù)和瀏覽數(shù)據(jù),比如微博忙菠,我們有可能把這種并發(fā)操作變?yōu)閱尉€操作嗎何鸡?好像不太容易。

那么我們能夠走得路就剩下第二條牛欢,也就是跨時間段的過程中不要改變數(shù)據(jù)骡男,我們剛剛產(chǎn)生的bug到底是什么數(shù)據(jù)改變導(dǎo)致了bug“茫回顧下我們的代碼實(shí)現(xiàn)的本質(zhì)隔盛,就容易找到其中的緣由了犹菱。

通常我們在實(shí)現(xiàn)分頁的時候,首頁看到的是最新的數(shù)據(jù)骚亿,那么從數(shù)據(jù)庫中取數(shù)據(jù)的sel語句是select * from news order by desc limt 0已亥,10,這樣取到最新的數(shù)據(jù)来屠,如果點(diǎn)擊下一頁虑椎,查詢語句不變,只是分頁條目不在是第0-9俱笛,而是第10-19條捆姜,如果在這個過程中有新的數(shù)據(jù)插入,我們會發(fā)現(xiàn)有一個東西變了迎膜,就是原有數(shù)據(jù)在數(shù)據(jù)庫的排序序號變了泥技,如果我新發(fā)布一條數(shù)據(jù),原來的第一條最新的新聞就會變成第二條磕仅,原來的第10條會變成第11條珊豹。這就是一個時間段內(nèi)的操作過程中有數(shù)據(jù)發(fā)生了改變。

既然我們無法把這樣的并發(fā)操作變成單線操作榕订,我們可以選擇不讓數(shù)據(jù)發(fā)生改變店茶,這樣并發(fā)bug就可以得到很好的解決了。

跨時間段的讓數(shù)據(jù)不改變不好走劫恒,那我們可以選擇第一種思路贩幻,讓并發(fā)操作變?yōu)閱尉€操作,之前提到的加鎖是解決方案之一两嘴,但是對用戶體驗(yàn)不好性能很差丛楚,基本上無法再互聯(lián)網(wǎng)項(xiàng)目中使用。如果不能加鎖憔辫,那么常用的解決方案是什么趣些?

我們可以用隊列。如果我們將所有的用戶請求進(jìn)行排隊贰您,有一個服務(wù)來訂閱這個隊列坏平,那么不管有多少用戶訪問,最終到服務(wù)器端枉圃,處理服務(wù)的就只有一個進(jìn)程。這樣就實(shí)現(xiàn)了一個由并發(fā)操作轉(zhuǎn)換成單線操作庐冯。

mmexport1472089890891.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孽亲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子展父,更是在濱河造成了極大的恐慌返劲,老刑警劉巖玲昧,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異篮绿,居然都是意外死亡孵延,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門亲配,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尘应,“玉大人,你說我怎么就攤上這事吼虎∪郑” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵思灰,是天一觀的道長玷犹。 經(jīng)常有香客問我,道長洒疚,這世上最難降的妖魔是什么歹颓? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮油湖,結(jié)果婚禮上巍扛,老公的妹妹穿的比我還像新娘。我一直安慰自己肺魁,他們只是感情好电湘,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹅经,像睡著了一般寂呛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瘾晃,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天贷痪,我揣著相機(jī)與錄音,去河邊找鬼蹦误。 笑死劫拢,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的强胰。 我是一名探鬼主播舱沧,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼偶洋!你這毒婦竟也來了熟吏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牵寺,沒想到半個月后悍引,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡帽氓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年趣斤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黎休。...
    茶點(diǎn)故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡浓领,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奋渔,到底是詐尸還是另有隱情镊逝,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布嫉鲸,位于F島的核電站撑蒜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏玄渗。R本人自食惡果不足惜座菠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望藤树。 院中可真熱鬧浴滴,春花似錦、人聲如沸岁钓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屡限。三九已至品嚣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钧大,已是汗流浹背翰撑。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啊央,地道東北人眶诈。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像瓜饥,于是被迫代替她去往敵國和親逝撬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容