互聯網架構的我的主觀見解

如下內容僅做爲我的主觀見解,不表明業界,僅供參考php

架構是一個系統的總體骨架的簡稱。理論上能夠說全部運行的系統都有本身的架構,不一樣的是數據處理方式、工具和目的。一個理想的架構須要表現出最大的擴展性、最好的安全性、最佳的運行性能、最簡的維護工做、最快的升級特色、最合適的支撐團隊、最低的運營成本和最優的發展目標。爲了實現這些要求無數團隊與我的在無私貢獻豐厚的成果,但還遠遠不夠,由於業務要求與市場不斷在升級變化,與之要求的系統也須要不變的升級與變化。不少時候系統在隨着變化而面臨重構,更甚的會在不一樣的階段重構一次或幾回。但有些並不是架構全責,好比:團隊重組、語言變動、業務轉向、硬件調整、成本管控等。java

不能否認大公司頗有錢能夠擁有你不敢相像的團隊,能夠買你可能永遠都接觸不到的硬件或系統,去完成他們定義的高端架構標準,但他們在處理架構時經歷了什麼、成功到什麼節點、能適應到什麼節點、還有多少優化空間你能斷言否?這不該該是架構的標杆,這只是有錢的標杆。我認爲架構的初衷不該該有綁架的動機,它須要咱們拿出本身擅長的能力不斷去學別人所長補本身所短,權衡的思考如今的要求與狀況、未來的要求與狀況,再儘量的去選擇一套合適的方案,一步一步迭代與完善。node

目前尚未一個絕對的權威的架構標準,不過業界都有同一個目標:把數據庫、業務邏輯的處理量與併發量減小到可承受的範圍內,並預留必定的擴展和伸縮空間。選擇合適的方案是決策人能力的體現,不少人喜歡跟風選擇去填補本身的眼界或嘗試慾望,在使用先後沒有充分的瞭解與分析,致使面臨問題時手忙腳亂,甚至有些問題一直沒法解決。跟風是順應潮流是擴大生存空間,但也須要充分的準備學習才能更好的生存下去。python

一個以數據庫爲核心的互聯網系統中,架構的首要重點天然是數據庫的規劃;而後業務邏輯規劃;最後對外服務規劃。除非服務器長期的壓力小到只須要要單臺服務器就知足了,那架構就不須要專門去思考與研究,甚至都不須要專門考慮數據的原始性。若是服務器壓力在上升架構的重要性就必定會擺在面前,而架構的重點就是合理安排各環節之間的通訊法則。mysql

數據庫規劃

數據庫是整個系統的數據倉庫,是提供數據保障的數據管理獨立系統,但它在多節點的狀況下處理方式有很大的變化。最主要的緣由是數據庫依賴索引來查詢數據(而索引只能對單表,多表索引會影響拆數據的目的),數據拆分後索引也一同拆分(只有這樣才能保證索引樹更小,查詢更快)。爲儘量少的對系統提供服務的影響須要在業務層增長一層數據庫節點索引(或中間件)關聯到不一樣的數據庫節點上快速選擇完成數據操做。linux

目前作法有nginx

  1. 使用強大的中間件(好比mycat、ShardingSphere等),它們會自動處理分表,分片等工做,讓你的架構不須要再考慮太多數據庫的問題。但會受中間件的功能限制,某些更定製化的特殊需求可能須要在業務上進行復雜處理(好比熟悉的數據庫不支持,更靈活的查詢和複雜的查詢不支持或性能不佳,不過中間件通常有團隊在維護,只要市場有較大剛需算法上可行很快會增長對應的功能),固然也能夠外加其它數據庫系統來輔助完成。
  2. 對必用搜索字段作HASH業務索引分片到不一樣的節點表。當只有一個必用字段時問題不大,若是出現多個必用字段時會複雜的多,要麼遍歷主要必用字段節點羣(業務上處理複雜性能稍差,如:分頁須要額外計算拼裝);要麼給次要的必用搜索字段建立各自的節點羣,這個節點羣的數據列包含主要必用字段,和輔助搜索字段(須要額外佔用部分空間,業務處理相對簡單性能稍好,如分頁只須要獲取主分片ID集再查詢主要必用字段節點羣的數據再合併即爲分頁數據體),也能夠包含全部數據(額外空間佔用量大,直接返回分頁數據體),但更新時都須要同時更新全部對應的節點羣。這種作法更自由,業務適應能力強,對數據二次拆分或合併難度大,也能夠藉助其它非關係型數據庫來輔助搜索數據。
  3. 使用商業收費數據庫集羣系統,就如同使用中間件同樣,但須要在不一樣的量級下額外支付不一樣的費用。
  4. 按業務某些不怎麼互通的區域拆分經過大量主從複製來分壓,好比按地區拆分,按業務線拆分,這種作法會限制較多的查詢功能,系統的支撐量有限也不能無限擴展。
  5. 依賴大量的主從複製,最原始的方式,簡單粗暴,系統的支撐量很是有限而且也不能無限擴展,須要足夠強的硬件支撐,當到極限時必需調整。

不一樣的作法適應不一樣的環境、背景、條件。相同的是集羣服務器必需知足一些基本要求,好比:redis

  1. 各節點的數據能夠互通且及時性高
    若是數據互通性很差,及時性不高就會出現數據延時讀髒,嚴重影響系統正常功能。高效的互通性與及時性須要保證各節點的壓力一直在可承受範圍內。好比主從複製分流,若是複製過於依賴某些寫入量大的節點時會影響這些節點的寫速度的效率發揮,讓一些從節點作提供複製功能給其它節點,減小主節點的複製壓力(固然這樣會形成相對的複製延時,須要控制好複製的層次,視讀寫量級而定)算法

  2. 各節點的數據處理量要相對均勻
    若是各節點數據處理量極度不均勻壓力就可能會失衡,容易形成部分節點壓力過大而罷工。數據處理量要均勻就須要在分表分片時分散數據集中點,系統查詢時分散到各只讀節點上。在節點的數據量上通常須要選擇一個惟一或索引層夠高的字段來分片,好比:手機號取前綴或其它段、主鍵ID能夠取模、取整、取尾數等,儘量能讓各節點的數據量相對均勻便可;對於系統查詢量須要業務代碼儘量能雨露均沾的訪問各節點,而不是集中在某些節點,通常可使用權值計算、隨機碰撞、訪問順序、時序,詢壓等。

3.支持數據鎖最好支持一致性事務
數據鎖能夠保證數據的一致性,在併發環境中尤爲重要,只有鎖定數據才能保證各進程上下文中處理的數據不會被破壞。值得慶幸的是大部分數據庫都提供了數據鎖,但在集羣中上鎖可能會出如今不一樣的節點上,若是上鎖的順序混亂那麼就可能形成死鎖。
事務處理能夠快速讓一批數據的修改生效(提交)或失效(回滾),同時事務提供事務隔離與事務鎖保證了數據的一致性,簡化程序處理流程,避免程序回滾數據與數據加鎖等操做。關係型數據大部分都支持標準事務,非關係型數據庫基本沒有或很簡單的事務,但都提供了鎖。在集羣中同一個業務事務處理可能會出如今多個節點上,而在事務提交時可能會出現某些節點提交失敗,而其它節點提交成功的狀況,這會形成已經提交的數據沒法回滾,而未提交成功的數據回滾的問題。多個節點事務沒法保證事務的絕對一致性,有不少種特殊狀況會致使事務只在部分節點上生效,好比:網絡異常、數據庫掛了、業務進程掛了、死鎖等。
要想在多節點上保證事務的可靠性就須要額外增長一個獨立一致性事務,以這個事務來決定各節點的事務成敗。獨立事務必需在其它各節點事務提交前提交而且記錄各節點的事務處理數據,獨立事務提交成功後,而後成功提交一個節點事務就刪除一個獨立事務對應的數據,當出現某個節點的事務沒法正常提交時可再經過其它的守護進程取出獨立事務內的數據寫到異常節點中,保證事務一致性。若是在尚未來的及刪除獨立事務內的節點數據時就掛了則須要守護進程自行判斷數據是否已經修改爲功(插入或刪除的數據經過惟一特徵來判斷,修改經過修改前惟一特徵數據源增長限制條件)。這個獨立事務可以使用消息隊列,先進先出,當某個節點事務提交失敗時再去填補下;固然也可使用數據庫事務。準備一個提交一致性事務(記錄各節點事務修改數據體,提交後就表示事務完成,而後逐個提交各節點事務,成功一個刪除一個提交一致性事務對應的節點數據體或者所有提交完後統一刪除),一個確認一致性事務(這個事務可使用最後提交的節點事務來擔任,這個節點的事務最早開啓前加鎖,最後提交解鎖,若是提交中異常則交給其它守護進程處理)。另外守護進程須要堵塞其它業務進程事務處理,防止業務事務內處理待守護進程修改的數據,守護進程使用不當可能會影響系統的性能。守護進程可使用定時隊列服務、寫到業務進程中或者二者均有,這徹底取決於架構的選用。
理論上只要擔任的一致性事務或鎖能正常提供服務就能夠保證各節點的數據的一致性。sql

  1. 各節點迭代、更新、升級、伸縮可以快速同步進行
    業務發展會影響各階段的人力與財力投入,也不可能一開始就使用很是大的集羣來應對很小的業務,系統初期會預估1~5年的數據量,而後根據當前的財力來選擇合適的集羣,再做對應的分表分片處理。隨着業務的變化頗有可能須要再次升級或降級,若是集羣升降級很是複雜並伴有不確認性那將會給系統的升降級來帶不可估計的影響。一個好的架構須要具有快速的升降級處理條件,以應對業務的變化。而升降級中最困難的莫過於分表分片的二次拆分與合併,業務代碼的調整。這也是爲何如今不少項目使用了數據庫中間件的緣由,中間件把這些操做封裝好對外的就如同操做一個數據庫,使得架構升降級時不須要調整業務代碼,惟一的遺憾是中間件並不能像操做一個數據庫那麼的任性,由於他就是一個數據庫節點路由器,操做不當會給系統帶來隱患。固然若是在業務代碼中來完成中間件的功能也是能夠的,不過須要額外增長很是多的算法邏輯,好處是你的系統你做主,只要能力達到就以完成中間件的全部工做而且還能夠更好的定製調整。
    若是單從升降級處理上講,分表與分片須要準備一個處理工具把這些數據進行復制轉移,當數據複製轉移完成後再切換到業務處理,若是切換失敗則退回切換。中間須要存在一點切換停服時間,一來保證數據在切換中不會再變化,二來保證數據切換失敗時退回前數據也不會變化。業務代碼上的切換須要同數據切換同步,最好各業務節點裏有兩套代碼,一套是原來的分表分片處理業務,一套是如今的分表分片處理業務,若是數據切換失敗能夠快速退回。

  2. 節點羣須要高可用容許少許服務器異常時依然能夠正常提供服務
    高可用是如今比較熱門的話題,當用戶體量足夠多時系統異常機率會多起來,異常時間越長損失就越大,因此讓系統在最短的時間內恢復正常止損是高可用的價值所在。高可用涉及到系統的出入口(負載均衡器)、業務服務器、數據服務器等各節點和環節。一般業務服務器會更容易點,由於業務代碼相同且部署多個,部分異常時負載均衡器能夠直接剔除,正常後再自動加入。系統的出入口與數據服務器通常是增長備用節點來替換,經過守護進程定時去核實主節點可用性,當主節點不可用時自動切換到備用節點上。系統的出入口能夠經過修改域名解析IP,或修改機房網絡等方式。數據服務器只須要修改鏈接地址或地址解析便可。但它們在切換以前最好保證異常的節點死透了或強制關閉,假死容易帶來數據的混亂。

  3. 合理查詢不受限制
    集羣原本的目的是解決數據量的問題,但數據被切分後查詢會變得更麻煩,若是一條查詢涉及到多個節點則須要遍歷這些節點,節點越多這條查詢越慢,即便增長緩存也會由於第一次查詢超時堵塞整個業務線。不一樣類型的數據庫單表數據量不同對查詢的限制或要求也不同,目前主要有關係與非關係型數據庫,還有部分數據庫在這二者之間(不過大部分能夠定義爲非關係型數據庫)。關係型數據庫可使用關聯查詢減小數據的冗餘量簡化查詢流程,會犧牲數據的容納量與查詢速度;非關係型數據庫提高數據查詢速度拆分查詢流程,不能使用關係查詢須要增長冗餘數據來減小查詢次數與複雜性。能夠說數據拆分後會把數據非關係化,爲了完成更多的查詢要求就須要增長更多輔助冗餘數據完成,固然這些冗餘數據不必定要存放在相同的數據庫中,能夠是其它的數據庫或緩存。

業務邏輯規劃

業務邏輯是須要定製開發,基本沒有太多的第二選擇,即便使用了開源代碼也須要二次開發。但在集羣數據庫系統中業務的處理須要格外當心,稍有不慎就會形成巨大的隱患甚至影響系統正常運行。

業務拆分
不少架構或決策人員願意把直接操做數據的邏輯單獨拆分出來組成微服務或低層服務提供給更外層的業務邏輯處理,讓更專業的人員處理低層數據操做,其餘人員處理外層業務邏輯操做,儘量避免數據操做不當的災難。這種作法好處比較明顯,好比:系統更穩定可靠、外層處理人員不須要再考慮數據一致性等相關問題開發更快、獨立服務提供更多應用場景、各層級更專注容易升級與維護等,缺點是:低層服務處理複雜、須要額外佔用空間、增長系統總體負載與成本等。當業務很大時仍是建議這種佈局,由於開發人員的能力責任不同,天然結果也會不同,數據處理不當帶來的災難每每影響很是大,越是核心容易出錯的地方越應該交給有能力的人去完成。拆分合理也須要綜合評估,拆分不合理會把邏輯處理量偏向一邊影響週期和擴展性。

開發語言
開發語言只是一個實際系統業務功能實現的工具,沒有太大的貴賤之分,適應團隊纔是王道。合理的架構不僅是節省成本,有的時候還決定了項目的成敗。而不少人卻把架構的失敗歸結到開發語言自己及框架和使用的各類服務工具;好比:像php、nodejs、python等弱類型語言安全性差、功能少、速度慢不能開發大型系統,而應該使用Java、go等高級別強類型語言,系統框架也應該使用流行強大的;數據庫系統應該使用mongodb、postgresql、oracle、非關係數據庫而不是使用mysql等等。但我認爲架構的失敗是決策人的全責與能力的體現,好的工具用的好才能發揮好,軟件的世界裏沒有絕對好的東西,只有用的好的東西。強類型語言也有漏洞、多線程併發等特定高級功能並無多少系統大量運用、運行速度也不必定絕對性的快;還有全部的數據庫單表支撐都是有限的,數據庫受硬件限制沒法作到無限量,若是能夠今天也不須要討論分表分片搞集羣。但有一點能夠說的是強類型語言學習成本高難度稍大,天然能生成下來的開發人員能力與素質相對偏高;而弱類型語言學習成本低簡單,不少低能力與低素質的人混雜在內拉低了總體的能力水平,致使弱類型語言成了弱雞的代名詞。

每一個語言有本身的定位與特色,但目前弱類型語言都是由C或C++之類的更低級的強類型語言開發的出來的,爲保證弱類型的特色會犧牲一些空間與性能來避免強類型語言的開發複雜性。但不表明性能就必定比高級別強類型語言差,一個系統運行的速度徹底取決於實際CPU執行的指令時長和IO等待時長,只有最優的代碼才能保證速度最優,惋惜的是在業務代碼中最優每每只是表象,系統中通常會使用框架及組件而真正使用到的功能可能還不能20%大部的功能是無用的損耗,無形中增長了系統的負載。在很是簡單的功能對比弱類型語言基本不具有性能等優點,但面對龐大的功能時弱類型語言開發速度快,代碼體量小,誰優誰劣還真很差說。當系統處理量足夠大時,性能的體現纔會很是明顯,不過目前尚未哪一個系統會用多種語言開發多套進行性能對比。目前全部的對比都是在必定的基礎上對比,並非成熟的大型系統上對比,語法與底層的差別使得對比自己不具有不公平性,每一個語言都有本身的性能突出點和薄弱點,在性能突出點使用多的系統中天然性能更佳,相反性能更差。

至於業務代碼的安全性與開發語言自己是沒有直接關係的,沒有哪一個語言敢承諾開發出來的程序是絕對不會出現安全問題的。弱類型與強類型的語言在各自生存環境中有本身的的處理方式來避免安全問題,但不免會有疏忽,徹底在於開發人員自己的意識與看法。

我的認爲只要能力到了弱類型語言並不弱而強類型語言並無強到哪裏去,大部分複雜的功能與算法已經被寫到各服務與工具、語言解析器和框架中,即便有更高級的算法要求也會是另外的算法團隊來完成,而大家只須要去使用它(除非你能力足夠強能夠寫出任何其它語言能作的事)。全部開發語言都有本身的優勢與缺點,所以語言沒有最好只有最合適。若是你評估確認某種語言及框架是團隊最拿手的就不要再去冒然嘗試團隊的短板,除非你有萬全的把握應對。

對外服務規劃

對外服務有兩種,一種是系統內部各服務節點之間的對外服務(不對公網開放),好比數據庫、微服務等;一種是對公網和內網開放的服務,好比對外API、網站展現、數據輸出等。不官哪一種形式都須要保證服務的正常有序。
高可用性
若是服務出現異常應該如何保證對外服務還能正常呢?高可用性是全部系統都會面對的問題,軟件與硬件存在許多的不肯定性致使系統異常,在出現異常時若是去及時發現並處理便是高可用的主要目標。通常能夠增長監控系統,發現系統異常後及時報警和自動處理,減小導演帶來不可服務的損失。
安全
對外服務最怕的就是數據泄露、篡改、甚至形成系統不可服務。從理論上說,沒有絕對的安全,但須要有對應的監控和預防機制儘量止損。主要安全區域有:操做系統、組件服務、業務邏輯、業務組件框架、開發語言低層等,通常可以掌控的是業務邏輯安全,其它的安全問題基本上只能在配置、升級、補丁上來彌補。

業務邏輯安全。業務邏輯安全有不少,好比SQL注入、SHELL注入、跨站篡改、業務漏洞等。除了業務漏洞以外其它全部安全問題都是由業務邏輯不夠嚴謹形成的。若是須要很好的避免漏洞就得須要知道漏洞的特色,而且還要有更好的洞察能力去避免。對於業務漏洞核心的思想是不要相信全部用戶提供的數據,這些數據都須要通過驗證或加工處理安全後纔可使用;全部會接收用戶數據的進程儘量不要給過高的操做權限,好比在linux系統下不要在root帳號下啓動這些服務進程。業務漏洞通常是需求考慮不周形成的,好比:作活動只能每人抽獎一次,但在其它業務中又能夠贈送抽獎次數,致使抽獎數據不可控。

業務組件框架、開發語言低層安全。這部分安全問題不多,一旦出現影響會比較大,基本上只能升級對應的組件框架或開發語言,對於比較大的集羣來講是一個不小的工做(固然若是集羣管理工具強大另論),有的時候可能還須要修改比較多的業務代碼來彌補。做爲決策人應該須要考慮下這塊的安全問題,儘早安排部署對就的工具,不一樣的開發語言工具不盡相同這裏就很少說明,不過也能夠自行開發簡單的處理腳本進行遠程批量操做。

組件服務安全。服務組件只要不對外網開放,就能夠抵擋一大半安全問題,若是有對外開放的服務則須要注意相關配置,好比:端口號調整、使用加密協議通訊、限制鏈接的地址或其它標識、使用專用帳號啓動服務、複雜的受權密碼等。其次就是業務代碼或管理工具漏洞也可能會致使組件服務被暴露出來,甚至能夠任意控制,通常建議管理工具只在內網或專網使用,業務代碼提供的操做帳號必需限制權限,全部的受權密碼或帳號必定要儘量複雜防止暴力破解。

操做系統安全。操做系統安全大部分是系統組件與系統輔助管理工具帶來的,遇到了只能及時升級或打補丁。操做系統都會提供一塊兒輔助管理工具和基本的IO操做管理進程,這些是操做系統不可缺乏的。由於操做系統的複雜性,因此漏洞也難以免。但也不是全部的漏洞是系統帶來的,好比ssh若是配置使用不當會被利用甚至提權。通常建議集羣節點只能經過內網或專網(***或其它固定IP)通道管理,減小其它外網訪問的可能性,固然各節點依然須要增長二道防守,增長帳號與密碼強度與保密性,而且對外提供的鏈接帳號不使用最高權限帳號,只能經過這個帳號進入後再提權。

監控管理
集羣系統必定要有個監控管理系統,不然管理太多的服務器將是一個耗時易錯的工做。監控系統主要負責服務可用與安全預警並及時給出報警與修復;管理系統主要負責集羣升級、更新、調整、伸縮、安裝等工做。不管是集羣監控或管理都有不少成熟工具,按業務需求來選擇,若是你有足夠的能力或財力也能夠自行研發,畢竟本身作的東西能夠任意調整與定製。

實施

架構不是神祕黑盒子,非常一個有嚴格條理的各環節解決方案。當系統數據量足夠大時各類狀況都有可能發生,只有提早預知更多、考慮更多、準備更多才能保證系統更穩定,一樣也不是每種構架方案都適合任何業務場景。
通常架構時須要畫出架構圖,同時給出各環節流程圖以及問題與解決方案。沒有哪一個架構能一次性完美的解決全部當前問題和未來問題,業務在變化架構也須要不斷的調整或升級,見招拆招是一條走不完的路,不然架構一次後面就沒有技術團隊啥事了。

選擇

選擇數據庫。表層數據使用非關係型數據庫是緩解系統壓力的有效途徑,通常建議一種以上,好比redis和es,在高速環境下好比秒殺,隊列、分頁緩存可使用redis集羣,在全文搜索上使用es集羣。上層數據對事務要求很少,主要是在速度上要求更高,而非關係型數據庫就是爲速度而生的。落地底層數據庫可使用mysql、mongodb、postgresql、oracle等集羣,也可使用可持久化的非關係型數據庫集羣(固然非關係型數據在事務處理上會稍微複雜些)。若是業務有很是多複雜的關係網建議使用關係型數據庫(好比:分銷系統,電商平臺系統),若是業務關係網比較簡單建議使用mongodb或非關係型數據庫(好比:秒殺系統)。固然若是團隊能力OK只要能持久保存數據哪一種數據庫均可以。

開發語言。建議使用團隊拿手的語言,最比如較流行的好比:java、php等由於被使用的越多問題修改的就越多也越完善。嘗試新語言會付出不少的代價,除非你足夠牛。

數據庫集羣操做。若是業務要求並不苛刻,仍是建議使用開源的中間件,畢竟集羣操做複雜、管理也複雜,什麼都依靠團隊來完成會嚴重影響開發週期,並且容易踩坑。

表層非關係型數據同步。落地底層數據庫須要同步數據到表層非關係型數據庫上,才能讓表層數據庫提供最新數據,通常對內存型的非關係型數據可直接寫,好比寫redis集羣;不是內存型的非關係型數據庫使用隊列同步或三方工具,好比同步數據到es可使用列隊或阿里的canal同步工具等。

對外服務。對外服務相對比較固定,主要是負載均衡器、系統服務器、CDN,微服務等。負載均衡器建議使用雲負載均衡器,它們已經幫你完成負載均衡及高可用等功能。也能夠本身搭建,通常能夠選用varnish(HTTP加速器)、nginx(反向代理器)等。系統服務器通常須要配套開發語言的,好比java須要使用tomcat,php或python使用nginx或apache等。CDN目前都在使用商業的,本身部署成本大,並且須要按地區部署點節省跨區域的網絡擁堵。微服務與系統服務相似。

相關文章
相關標籤/搜索