網際網路三高架構之高併發和高效能的理解
網際網路三高架構:高併發、高效能、高可用,簡稱三高(3H)
網際網路應用系統開發肯定經常會看到高併發和高效能這兩個詞,可謂是耳熟能詳,而具體的含義和關係真的如你所想的,真正的理解了嗎?
先來看一個例子:
一個蓄水池,是1m*1m*1m=1立方米大小,有一個出水口,出水口每秒鐘流出0.1立方米,那麼這個蓄水池的併發量是1立方米,出水速度是0.1立方米/秒。
如果增加一個出水口,都是每秒鐘流出0.1立方米,那麼這個蓄水池的併發量沒變,但是出水速度變成了0.2立方米/秒。
同理,增大了出水口,蓄水池的出水速度也變快了。
上面我們很容易知道,併發量是一個容量的概念,效能就是出水速度,而且有下面這些結果。
- 增大蓄水池的長寬高,可以增加併發能力。
- 出水口如果擴大了出口大小,則可以提高出水的速度,也就是效能提高了。
- 增加出水口的數量,則是增加了並行處理的能力,同樣可以提高效能。
那麼對照我們計算機中,我們的系統中,是怎麼樣的結果呢?
- 增加伺服器的記憶體大小,可以增加併發量。因為記憶體增加了,就可以開更多的程序,更多的執行緒,也可以擴大任務佇列的大小。
- 提高cpu的主頻速度,優化程式,可以提高效能。cpu更快了,程式優化的更好了,處理單個任務的時間也就更短了。
- 增加多核甚至分散式伺服器數量,也可以提高效能,同時提高併發量。
如果只是效能提高了,併發量是否也能提高呢?
如果我們靜態的理解併發量,那它是不會提高的。
而我更願意動態的來理解併發量,即:單位時間內可以進來的最大數量。
那麼提高效能,是可以線性提高併發量的,因為單位時間內,進來的同時也有出去。
我們先來做一個假設:
單個程序(php fast-cgi)記憶體佔用10M
單個執行緒( java web)記憶體佔用2M
單個協程(go)記憶體佔用20K
佇列任務(nginx)記憶體佔用2K
我們下面來看看記憶體與併發量的關係。
記憶體量 | 程序數 | 執行緒數 | 協程 | 佇列任務 |
---|---|---|---|---|
1G | 100 | 500 | 50K | 500K |
2G | 200 | 1000 | 100K | 1000K |
4G | 400 | 2000 | 200K | 2000K |
8G | 800 | 4000 | 400K | 4000K |
從上面的結果中,我們可以很直觀的看出來,併發能力在不同的執行模式中的巨大區別。
多程序和多執行緒的模式,不僅是記憶體開銷巨大,而且在數量不斷增加的情況下,對CPU的壓力也是非常巨大,
這也是為什麼這類系統在併發量大的情況下會很不穩定,甚至宕機。
假設上邊計算出來的資料,都是靜態的容量,如果所有任務都不處理,那麼記憶體肯定都是會很快就被撐爆。
所以要達到更高的併發量,就需要有更快的處理速度,即做好效能優化。
下面,再來做一個假設。
我們現在有一臺伺服器,配置是8核16G記憶體。
如果我們的應用是計算密集型,純運算的系統,如:資料索引查詢、排序等操作。
而且還要假設,這個應用在多核並行運算時不存在鎖競爭的情況(只讀)。
QPS = 1000ms/單個請求耗時*8
① 如果單個請求(任務)耗時100ms,那麼我們可以計算出來:qps = (1000ms/100ms)*8核 = 80個/秒
② 如果我們優化處理的演算法,單個請求耗時降低到10ms,那麼:qps = (1000ms/10ms)*8核 = 800個/秒
③ 如果可以繼續優化,將單個請求耗時降低到1ms,那麼:qps = (1000ms/1ms)*8核 = 8000個/秒
上面的情況和優化的效果理解起來應該很容易,因為對伺服器資源的依賴更多是CPU的運算能力和數量。
在實際的網際網路應用中,系統更多是依賴mysql,redis,rest api或者微服務,屬於IO密集型。按照上面的計算方式,可能就不太準確了,因為cpu是有富餘的。
在IO阻塞的時候,開啟更多工的方式當然有上面多程序、多執行緒、多協程和佇列的方式來實現,而且也是有效且更好地利用伺服器資源的方法,可以達到更高的併發量,
畢竟我們把大部分的運算放到了應用外部的mysql,redis,rest api等服務。
到此為止,我們已經知道併發量、效能優化跟伺服器資源(伺服器數量,cpu,記憶體)的關係,也知道效能優化對併發量的影響。
解疑答惑
1 記憶體越多,併發量一定可以越大嗎?
答:大部分情況是的。這個問題,上面有提到過,對於多程序、多執行緒的模式,執行緒太多的時候,執行緒搶佔時間片,CPU切換上下文會越來越慢。影響系統性能。
對於協程、佇列的執行模式,這個問題會好很多,當然協程排程、佇列維護的開銷,肯定也是會增加,只是增加的開銷不至於對系統性能造成直線下降。
2 CPU越快,應用的效能一定越好嗎?
答:絕對的。只不過CPU和應用效能提升可能不成線性增長的關係,因為應用可能是IO密集型,應用效能還會受到IO阻塞的影響。
3 CPU越多,應用的效能一定越好嗎?
答:大部分情況是的。如果大量鎖存在,效能提升可能會大打折扣,因為並行能力會被鎖住,又變成單執行緒執行了,沒有最大的發揮多CPU的作用。
4 伺服器越多,併發量一定越大嗎?
答:絕對的。伺服器增加,CPU和記憶體資源相應也就越多,併發能力也就會增大,他們之間是線性相關。
5 伺服器越多,效能一定越好嗎?
答:大部分情況是的。但是單個伺服器的效率可能會是下降的,資料一致性問題、同步問題、鎖問題,這些都會導致單個伺服器的效率(CPU利用率)下降,所以不是線性相關。
關於CPU利用率:
如果只是考慮應用對CPU利用效率的話:單核=多核=多伺服器
單程序/單執行緒的系統對於伺服器資源的利用率更高。
到多核的系統中,就會因為鎖的問題,多工同步的問題,作業系統排程的問題,造成一定的資源浪費。而分散式系統中,這些浪費也會更嚴重。
6 怎樣更好的更有效的利用伺服器資源呢?
答:避免因為IO阻塞讓CPU閒置,導致CPU的浪費;
避免多執行緒間增加鎖來保證同步,導致並行系統序列化;
避免建立、銷燬、維護太多程序、執行緒,導致作業系統浪費資源在排程上;
避免分散式系統中多伺服器的關聯,比如:依賴同一個mysql,程式邏輯中使用分散式鎖,導致瓶頸在mysql,分散式又變成序列化運算。
上面說了要避免的地方,要具體怎麼來避免,到具體的業務場景就需要具體分析了。
而且有些時候,為了業務功能,或者其它方面的需求,比如:可用性、伸縮性、擴充套件性、安全性,不得不犧牲掉一部分效能。
最後,做一個總結:
併發量,是一個容量的概念,服務可以接受的最大任務數量,動態的看待它,還需要把效能考慮進去。
效能,是一個速度的概念,單位時間內可以處理的任務數量。
高併發和高效能是緊密相關的,提高應用的效能,是肯定可以提高系統的併發能力的。
應用效能優化的時候,對於計算密集型和IO密集型還是有很大差別,需要分開來考慮。
增加伺服器資源(CPU、記憶體、伺服器數量),絕大部分時候是可以提高應用的併發能力和效能
(前提是應用能夠支援多工平行計算,多伺服器分散式計算才行),但也是要避免其中的一些問題,才可以更好的更有效率的利用伺服器資源。