新聞中心
說在前面
我曾不止一次聽說過這句話:
成都創(chuàng)新互聯(lián)是專業(yè)的海豐網(wǎng)站建設(shè)公司,海豐接單;提供成都網(wǎng)站制作、成都網(wǎng)站建設(shè),網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行海豐網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
“十個女人無法在一個月內(nèi)生出孩子”
我明白這句話的意思,用來形容我們的開發(fā)工作需要循序漸進,沒有辦法簡單的增加人員就能加快研發(fā)速度。
這句話也經(jīng)常被用于反駁產(chǎn)品經(jīng)理或者老板,試圖讓他們明白我們內(nèi)心所表達的觀點,老實說我也說過這樣的話,當時還覺得挺有道理,現(xiàn)在想來可能有些一廂情愿了。
沒錯,在現(xiàn)實世界中,當然不可能在一個月內(nèi)生出孩子,但我們畢竟是做產(chǎn)品寫代碼的,而不是真的要去生孩子,所以這種說法未免有點偷換概念。
我并不是較真,如果只是想讓產(chǎn)品經(jīng)理明白我們所要表達的觀點,我們完全可以用其他的比喻,如實反饋存在的困難與問題即可。
言歸正傳,這句話與本文有什么關(guān)系呢?本文想要就“并發(fā)”所帶來的問題進行探討,相信看完后你會對此有一個感覺。
與我之前寫的幾篇文章一樣,并發(fā)一詞在本文中所表達的意思是:
“在分布式環(huán)境下,超過一個線程同時對同一個狀態(tài)進行訪問和變更所導(dǎo)致的一致性問題和可用性問題”
問題的根源:狀態(tài)
我無法給出一個百分比數(shù)據(jù)用以說明到底有多少后端應(yīng)用程序在使用數(shù)據(jù)庫,但我想國內(nèi)涉及到增刪查改之類的各種“管理系統(tǒng)”應(yīng)該不在少數(shù)。
說到底,增刪改查是落地,而怎么落地則取決于業(yè)務(wù)的需要,也就是說,業(yè)務(wù)規(guī)則以及流程表達了我們的邏輯,但終究離不開柴米油鹽(增刪改查)。
那么什么是狀態(tài)?
它可以是文件,也可以是數(shù)據(jù)庫,可以是一個變量,也可以是緩存,它代表了計算的結(jié)果或者依賴(中間結(jié)果),由于它是可變的,并且可以被超過1個以上的程序同時訪問或者修改。
所以由此產(chǎn)生了兩個問題:
- 一致性:確保業(yè)務(wù)代碼的邏輯符合設(shè)計期望。即:我們?nèi)绾伪WC在并發(fā)的時候確保狀態(tài)始終處于我們預(yù)期?
- 可用性:確保系統(tǒng)可以滿足伸縮需求,但同時,也必須要滿足一致性。即:在保持一致性的同時,如何提高系統(tǒng)的負載能力?
一致性要求是必須的,無法滿足一致性的情況要么是業(yè)務(wù)邏輯本身有問題,要么就是我們在編碼過程中出現(xiàn)了BUG,而如果是我們的編碼出現(xiàn)問題,很明顯就不符合驗收標準。
可用性要求則取決于運營的實際情況,隨著系統(tǒng)使用規(guī)模的上升,我們需要保證系統(tǒng)始終處于用的狀態(tài)下,因為業(yè)務(wù)方不希望服務(wù)被中斷或者超時。
我來描述一下完整的邏輯:
- 由于我們需要確保系統(tǒng)的設(shè)計與編碼符合業(yè)務(wù)的流程與規(guī)則,所以我們需要保證一致性。
- 由于我們需要為更多的用戶提供服務(wù),所以我們需要提高系統(tǒng)的可用性。
- 由于單臺服務(wù)器所提供的硬件能力已經(jīng)達到極限,所以我們不得不使用多臺服務(wù)器,形成集群同時服務(wù)業(yè)務(wù)請求。
- 由于集群后多個服務(wù)器可能會訪問和修改同一個狀態(tài)(狀態(tài)一致性問題的產(chǎn)生原因),所以我們必須使用協(xié)調(diào)多個服務(wù)修改狀態(tài)的機制(鎖、事務(wù),問題的解決方案)。
接下來,我會分別就狀態(tài)的一致性和可用性進行討論。
問題:一致性
在上一節(jié)中,我們說狀態(tài)一致證明我們最終落地的數(shù)據(jù)是正確的,符合業(yè)務(wù)邏輯的。不一致是由于我們對業(yè)務(wù)的理解或者編碼出現(xiàn)了BUG,而BUG是我們必須要解決的。
這里有兩個層面的問題,我們分別來看一下。
業(yè)務(wù)層面
如果在業(yè)務(wù)邏輯的層面上本身就存在悖論,存在漏洞,經(jīng)驗豐富的開發(fā)人員會在進行系統(tǒng)設(shè)計或者編碼的時候就能察覺出來,道理很簡單,因為無法被實現(xiàn),不管怎么樣都會存在BUG,而這種BUG是我們技術(shù)人員無法解決的,我們不能夠去猜業(yè)務(wù)方到底想要什么,因為這極有可能不符合他們的期望,最后仍然有可能會導(dǎo)致返工,造成成本的上升。
就像我們拿本文開頭的那個例子去懟產(chǎn)品經(jīng)理一樣,很多時候是由于我們不善表達,沒有清楚表達出我們的疑惑,從而造成尷尬的場面。
溝通和管理是困難的,眾口難調(diào)。如何跟業(yè)務(wù)人員進行高效的溝通一直以來都是一個難題,但我們必須要清楚一點的是:
“只有我們充分洞悉和理解所要實現(xiàn)的業(yè)務(wù)領(lǐng)域,才能夠使我們更加輕松和增強信心,**因為只有這樣,我們才能夠選擇最適合的技術(shù)和模型幫助我們靈活的完成任務(wù)?!?*
我的意思并不是讓咱們都成為該領(lǐng)域的專家,因為術(shù)業(yè)有專攻,分工配合才是重中之重,如果我們沒有跟業(yè)務(wù)需求方達成緊密的一致,就有可能造成浪費。
有關(guān)這一塊的內(nèi)容,建議大家看一本書,叫做《領(lǐng)域驅(qū)動設(shè)計》,英文名是《Domain Driven Design》,簡稱DDD。
技術(shù)層面
技術(shù)層面出現(xiàn)不一致的問題一般有兩種情況:
- 我們沒有理解業(yè)務(wù)的原始需求。
- 我們完全理解業(yè)務(wù)的需求,但是由于編碼造成了BUG,出現(xiàn)了意外的不一致。
如果是第一種,這個沒什么好說的,返回業(yè)務(wù)層面與業(yè)務(wù)專家進行真誠層面的溝通,獲取真正的需求。
如果是第二種,那么我們首先應(yīng)該找出不一致的原因,分析不一致的原因有助于我們加深理解和避免此類問題。
如果是由于小失誤造成的問題我們直接修復(fù)即可,這其實很常見,我們寫的代碼或多或少都要經(jīng)過測試與修復(fù)。
如果是由于并發(fā)競爭造成的問題,那我們就需要用到相關(guān)的解決方案了,最常見的是使用數(shù)據(jù)庫事務(wù)來保證狀態(tài)落地的時候不會產(chǎn)生不一致,因為不一致會導(dǎo)致事務(wù)的回滾。
還有就是使用鎖來限制資源的訪問以及修改,這些都是很常見的技術(shù),鑒于本文的重點是想說明產(chǎn)生這些問題的原因,所以不會這些解決方案進行詳細的講述,有興趣的朋友可以翻看一下我之前的文章或者查閱相關(guān)資料文檔。
問題:可用性
可用性往往決定了系統(tǒng)架構(gòu)的實現(xiàn)方式,可用性導(dǎo)致了我們最終將不得不使用分布式集群來應(yīng)對大規(guī)模的訪問需求。
可以說,產(chǎn)生并發(fā)問題的直接原因就是可用性,因為它讓我們對狀態(tài)的管理變得十分復(fù)雜。
如果沒有可用性要求,最簡單的我們甚至可能都不需要數(shù)據(jù)庫,但現(xiàn)實中,對于一款成功的產(chǎn)品,我們不可能告訴我們的老板咱們無法實現(xiàn)對不對?
“量變導(dǎo)致質(zhì)變,當可用性要求越來越高,系統(tǒng)規(guī)模越來越大,即便是再簡單的增刪改查都將不會再簡單”
如何在提升可用性的同時還能保證狀態(tài)的一致性?這真的不是技術(shù)能夠解決的。
先冷靜一下,我的意思是,由于網(wǎng)絡(luò)分區(qū)的存在,狀態(tài)的強一致會導(dǎo)致可用性降低,而可用性的提高又會造成分區(qū)狀態(tài)的不一致,從而降低了一致性。
這就是著名CAP定理[1],我們要么取CP,降低系統(tǒng)的可用性,要么取AP,降低狀態(tài)的一致性。
那么我們有沒有什么辦法來達到一個比較好的平衡呢?
答案是當然可以,但是,就如我一開始所說的,這并不是技術(shù)一個人就能夠解決的問題。
事實已經(jīng)很明顯了,我們不可能取CP來降低系統(tǒng)的可用性,那樣就沒得玩了,所以我們只能夠選擇AP。
“在業(yè)務(wù)可以允許范圍內(nèi),設(shè)計一種最終一致的中間流程步驟,來提高系統(tǒng)的可用性,同時,又得以讓業(yè)務(wù)可以正常不受影響,處于預(yù)期的運轉(zhuǎn)。”
因此基于BASE理論[2][3]的最終一致更貼近于現(xiàn)實與業(yè)務(wù),CAP定理只是證明和告訴我們,哪些事情行不通,但是BASE理論告訴我們,上有政策下有對策,使用柔性事務(wù),反脆弱的系統(tǒng)才能讓我們更加的靈活。
所以我們要怎么做呢?我們要告訴產(chǎn)品經(jīng)理,系統(tǒng)的可用性瓶頸必須要更改業(yè)務(wù)的流程才能得以實現(xiàn)。
我們要學(xué)會表達,告訴產(chǎn)品經(jīng)理,這不是能不能做的問題,或者我能不能行的問題。
而是計算機科學(xué)目前的發(fā)展水平就是如此,也會存在極限,我們必須要學(xué)會相互適應(yīng),才能保證健康的發(fā)展。
那么事務(wù)怎么辦?我該使用分布式事務(wù)嗎?
盡量避免使用分布式事務(wù),這是由衷的建議,事實證明分布式事務(wù)不僅不會提高性能,反而會拖垮高可用場景的系統(tǒng)。
如果你遇到了這個問題,那么說明狀態(tài)分區(qū)隔離以及事務(wù)場景有可能存在不合理的地方。
如果你確實有這種需求,那么盡量避免使用分布式事務(wù),或者將分布式改為本地事務(wù),也就是說不要將它們放到不同的地方進行事務(wù)處理。
如果這樣也不行的話,那就必須讓事務(wù)這個概念被明確包含進業(yè)務(wù)范圍,作為一個獨立的實體存在,不要讓它隱藏在技術(shù)細節(jié)中。
這樣的話,我們就能夠?qū)@個定義明確的事務(wù)“句柄”或者“鉤子”進行補償或者回滾等最終一致冪等操作。
這也是我一直強調(diào)表達的,不要僅僅從技術(shù)層面來看待分布式事務(wù),它可能是一個潛在的業(yè)務(wù)需求,存在生命周期的潛在概念。
就像我們對接支付寶一樣,根據(jù)訂單號來確保冪等,返回明確的信息給支付寶表明處理成功或失敗,實現(xiàn)最終一致的交易處理。
并行就一定快于串行?
這個問題要從兩個方面來看,如果并行運行的程序沒有相互依賴,沒有狀態(tài)、資源競爭,那么水平拓展是非常容易的。
比如MapReduce,將大規(guī)模的數(shù)據(jù)分發(fā)并行處理,最后并歸計算結(jié)果,速度肯定快于串行執(zhí)行。
相反,如果狀態(tài)特征在進行并歸的時候,前后依賴就產(chǎn)生了耦合,并行處理導(dǎo)致的切換加載開銷就變得沒有意義。
什么意思呢?
比如將一個個事件進行存儲,因為狀態(tài)本質(zhì)上來講就是就是一系列事件施加計算后的快照而已(詳見https://www.cnblogs.com/xingxueliao/p/11561263.html#event_and_state)
所以如果我們想要計算某一個時間點上的快照,就需要從頭將事件播放到指定的位置上,事件對狀態(tài)的影響是嚴格按照先后順序來的。
因為事件導(dǎo)致的狀態(tài)結(jié)果是前后依賴的關(guān)系,因此并行運算并不會得到什么幫助,反而會因為切換導(dǎo)致無必要的狀態(tài)加載以及卸載開銷。
這種情況下,串行處理是唯一的方式,我們應(yīng)該在這個上下文中,對輸入的事件流進行持久批量的計算,而且不用考慮并發(fā)所帶來的一致性問題。
因此,盲目的使用多線程或者多實例集群不僅不會讓可用性得以提升,反倒因為競爭的關(guān)系導(dǎo)致可用性被降低。
對數(shù)據(jù)特征進行分析,如何隔離狀態(tài)才是我們需要關(guān)注的重點,因為只有將沒有依賴關(guān)系的狀態(tài)隔離后我們才有可能提高整個系統(tǒng)的可用性。
最后
這就完了?好像你沒有提到任何關(guān)于如何解決這些問題“具體方案”。
沒有具體的方案,因為這超出了本文的范疇,本文只是簡單的講述問題產(chǎn)生的原因,試圖以比較清晰的一個視角來發(fā)現(xiàn)和看待問題是為什么存在的。
新聞標題:并發(fā)到底帶來了什么問題?
標題路徑:http://fisionsoft.com.cn/article/ppsgos.html