【編者按】在 UPYUN Open Talk 廈門場,美拍架構(gòu)平臺負責人洪小軍從計數(shù)器服務開始,深入地分享了大中型SNS系統(tǒng)設計經(jīng)驗。在系統(tǒng)設計的基本原則一節(jié)他表示,系統(tǒng)設計必須以一致性、可用性、可擴展性為目標。在初期,機構(gòu)需要做的是快速搭建系統(tǒng)上線;而隨著業(yè)務規(guī)模的增長,機構(gòu)必須深入打造系統(tǒng)的可擴展性;到項目發(fā)展到中后期,開發(fā)人員往往需要處理各種各樣的問題,這個時候可用性則成了必須關(guān)注的重點。
以下為原文
一、 業(yè)務系統(tǒng)架構(gòu)演化
談到業(yè)務系統(tǒng)的架構(gòu),首先要說計數(shù)器服務,雖然看起來是個小功能,如果將計數(shù)器整個歷程和深層次的東西掌握清楚,會發(fā)現(xiàn)很多東西是相通的。通過簡單的業(yè)務,挖掘背后的技術(shù),是一個很好的學習過程。
計數(shù)器服務
SNS最重要的就是社交關(guān)系,以及用戶信息。在美拍你可以看到美拍評論,有評論數(shù)、點贊數(shù),微博有評論數(shù)、轉(zhuǎn)發(fā)數(shù)、點贊數(shù),這些都需要通過計數(shù)器來完成。
1. 實現(xiàn)計數(shù)器最粗暴的方式:數(shù)據(jù)庫直接count
一些產(chǎn)品在開始上線的時候,會用非常粗暴的方式——在數(shù)據(jù)庫中把數(shù)據(jù)count出來。計數(shù)器服務在SNS系統(tǒng)中屬于TOP級別的訪問大戶,甚至是TOP1,這個簡單粗暴的方式自然會面臨一些問題:因為沒有獨立的計數(shù)器,每次都需要從數(shù)據(jù)庫count,會出現(xiàn)大量慢查詢、慢請求問題。
2. 大部分公司常用的方案:獨立的計數(shù)器表
上面粗暴的計數(shù)器方式,沒有獨立的計數(shù)器,每次都要從數(shù)據(jù)庫count。而今大部分公司或系統(tǒng)會獨立維護計數(shù)器,建立計數(shù)器表,包括ID表和純計數(shù)表,每次變更都需要update,與此同時前面還有一個memacached,用來扛大量熱的請求。這個方案在前期沒有什么問題,但發(fā)。展到一段階段,會出現(xiàn)很多接口響應時間過長的問題。
回到SNS系統(tǒng)的特點,數(shù)據(jù)庫中會有大量的update/select操作。比如美拍,某一個明星發(fā)了一條信息,瞬間會有很多評論、點贊,計數(shù)表會頻繁的update。另外一種情況是,大量的信息是沒有被評論和轉(zhuǎn)發(fā)。SNS系統(tǒng)只要存放這些評論和轉(zhuǎn)發(fā)數(shù)等于0的數(shù)據(jù),就會有大量的select,這就需要緩存的容量足夠大,將所有的數(shù)據(jù)裝進去。
SNS系統(tǒng)還有一個特點,就是長尾特性很明顯,數(shù)據(jù)分布上有一條很長的尾巴。比如最近三個月占90%,比如第4、5、6個月,占比是很均衡的,這就是長尾的特點,這對緩存提出很大的挑戰(zhàn)。假設你可能要保證緩存命中率90%,可能要花5G內(nèi)存,如果到95%,就需要10G,如果要到99%,可能就要100G或200G,這就對容量提出很高的要求。 可進行的優(yōu)化:
數(shù)據(jù)庫合并寫入,即程序段合并寫入請求,有起到一定效果,但是不會太明顯。
緩存金擴容盡可能存放更多數(shù)據(jù),但由于SNS系統(tǒng)的長尾效應還是很難達到非常高的命中率。
3. 一定規(guī)模公司采用的方案:Redis內(nèi)存存儲
二、 架設在內(nèi)存基礎上的架構(gòu)
內(nèi)存緩存,從請求的訪問策略來看,從緩存讀取,cache不到,再從mysql讀??;命中率取決于它的容量。如果是內(nèi)存存儲,命中率是100%。對于容量要求,很多地方會說存儲需要的容量比緩存大很多,但在具體的產(chǎn)品中并不完全是這樣。比如,在計數(shù)器服務中,采用存儲的方式,50%的美拍是沒有評論的,存儲只需要存50%有被評論過的就足夠了。但是采用內(nèi)存緩存的方式,是沒有這個策略的,可能需要存很多沒有被評論過的信息。
1. Redis 內(nèi)存存儲
Redis是全內(nèi)存存儲,在SNS系統(tǒng)計數(shù)器中只需存存放所有計數(shù)器>0的數(shù)據(jù) ,具有高速的讀取性能,保證接口的響應時間可控,這也是大多數(shù)公司采用的方案,也比較可控。
2. 長尾數(shù)據(jù)
隨著時間增長,會出現(xiàn)大量的長尾數(shù)據(jù),尤其是SNS系統(tǒng),就像微博發(fā)展4、5年前的數(shù)據(jù)特別多,全部寫入內(nèi)存成本太高。長尾訪問,不需要用內(nèi)存這么高的成本去支撐。
長尾問題解決方案:
做冷熱數(shù)據(jù)分離,將最近個月的數(shù)據(jù)放在內(nèi)存,更早的宕到SSD上,成本比較低,這是一個折中方案;
通過修改redis,實現(xiàn)更緊湊的存儲。
三、 FEED服務
FEED業(yè)務指的是,好友的消息列表,美拍上的好友動態(tài),微博就是首頁的列表。FEED常用推、拉兩種模式。
推模式:
推模式寫入消息的成本會比較高,相當于發(fā)一條美拍需要推給所有關(guān)注我美拍的人。推的數(shù)量取決于有多少人關(guān)注我。相反獲取信息的成本非常低,只需簡單的SELECT就可以取出來,推模式是基于空間去換時間的策略。
在這種情況下,需要考慮3個問題:
容量是否會成為瓶頸;
推送量大的情況下,會出現(xiàn)延遲問題;
加關(guān)注/減關(guān)注這類變更通知的成本非常高。
拉模式:
拉模式是和推模式相反的方式,輕量寫入。獲取消息,先將關(guān)注的列表取出來,再將關(guān)注的每個人反的美拍列表取出來,最終合并。這是用時間換空間,拉取需要很多計算,會影響到接口的響應時間,需要大量的計算,需要獲取大量的資源,這樣會造成需求的帶寬增多。
因此,在好友多、粉絲少的情況下,推的模式最好。好友少和粉絲多的情況,比較適合拉的方式。
現(xiàn)在的SNS系統(tǒng)大多都是好友多,粉絲又多。在這種情況下,一些業(yè)界公司采用了推拉結(jié)合的方法,比如Twitter。普通用戶(粉絲數(shù)少的用戶)采用推的方式,大V采用拉的方式。
在發(fā)布信息的時候會通過粉絲數(shù)及其他策略包括是否在線等狀況做推拉的判斷。微博,公開性的微博都是基于拉的方式,私密性的功能,都是基于推的方式。
FEED服務中的重點:
FEED服務中數(shù)據(jù)庫和緩存是兩個很重要的點。
在業(yè)務快速發(fā)展的階段,需要頻繁的增加新的產(chǎn)品屬性,每個屬性都需要在線變更,這樣會對在線用戶產(chǎn)生影響,同時也很增加成本。
常用的解決方法:索引和內(nèi)容分離,各司其職。索引保證高效的查找,所有的查找都在索引里完成。索引可能會有不同級別、維度的索引。內(nèi)容使用KV結(jié)構(gòu)(V為二進制數(shù)據(jù)),便于數(shù)據(jù)變更。在這里內(nèi)容需要盡可能緊湊的存儲。
數(shù)據(jù)庫定位
數(shù)據(jù)庫只是作為存儲,不應該有任何邏輯,因為系統(tǒng)追求的是高性能的讀取訪問。最多有V條件,不應有任何業(yè)務邏輯,業(yè)務邏輯應該放到程序端處理。
數(shù)據(jù)庫面臨很大的寫壓力,常用的是分庫分表的方式,但會出現(xiàn)兩個問題,大部分數(shù)據(jù)庫都有自身的IP,跟其他的表關(guān)聯(lián),分庫分表就不能用智能IP,可能會造成IP沖突。
分庫分表策略
最常見的就是,每個庫里都有N張表,基于HASF分表。但到一定階段會出現(xiàn)一些問題:
特殊用戶查詢性能很高,這種用戶可能有幾百萬甚至上千萬的記錄,一旦穿透到數(shù)據(jù)庫,性能會很糟糕。
到一定規(guī)模后,會考慮進行硬件的升級,如果將所有的歷史數(shù)據(jù)存放SSD,很浪費。
為解決這個問題,很多公司采用了基于時間分片的方案。最好的方法是建立一個索引表,在索引表里存放每個月用戶有多少條數(shù)據(jù)。
四、 緩存:雪崩
說道緩存,先補充一個雪崩的概念。
假設系統(tǒng)有4,5個節(jié)點,每個緩存節(jié)點命中率是90%,也就是每個節(jié)點要承擔接近20%的訪問量,如果這個時候有1個節(jié)點壞了,這個請求會全部打到memacache,整個系統(tǒng)很可能就直接宕掉。這就是所謂的雪崩。
解決方案
1、在這個階段,程序端做兩節(jié)緩存,使得請求在某個節(jié)點壞掉了的時候,不會直接到memache,然后請求另外一個節(jié)點。
2、隨著業(yè)務增長,需要擴充。解決方法:
擴充節(jié)點到20個,但可能出現(xiàn)multiget hole問題,在這種情況下,導致性能下降。
搭建多集群。
五、 系統(tǒng)設計的基本原則
CAP原則:一致性、可用性、可擴展性
在SNS場景中,往往上面的三個點,只能選擇其中兩個,需要一定的折中。最主要的是擴展性和可用性。當然不是說一致性不重要,可以采用最終一致的方法做補充。
前期:快速搭建系統(tǒng)上線。
中期:重點解決可擴展性問題。通過硬件升級、單機性能優(yōu)化,提高單機性能,以及采用SACLE分布式支持,可以解決擴展性問題。
SNS系統(tǒng)高可擴展性需要關(guān)注以下幾個點:
支持高并發(fā)讀取,首先第一點就是支持大量級的接口調(diào)用。其次還需要保證很低的響應時間。
支持峰值的寫入。最重要的一點是前后端分離,前端處于永遠可寫的狀態(tài)。(用戶成功發(fā)布內(nèi)容后,顯示成功,實際并沒有完全成功,后面的事情交給后端去做)
做好分布式部署,包括一個機房內(nèi)部多集群,最好是有條件做到多機房。多機房部署,要保證每個機房之間有可靠的消息同步,要避免跨機房讀調(diào)用。
(多機房如何將主要資源同步:SYSQL可采用master方式,Redis有條件的可采用master/salve方式)
中后期:很多系統(tǒng)需要解決可用性問題。可用性問題比前面的問題都更加有挑戰(zhàn)。
避免單點故障。
保證響應時間。
作者簡介:洪小軍,美拍架構(gòu)平臺負責人。2014年底加入美圖,美圖架構(gòu)平臺部門負責人,負責美圖后端整體技術(shù)架構(gòu)設計工作,及其核心業(yè)務、基礎服務、平臺服務等的研發(fā)工作。