人人都是架構(gòu)師:讀寫分離,前臺后臺分離?
一、早期優(yōu)化策略回顧:三大分離
在互聯(lián)網(wǎng)發(fā)展的早期,為了快速解決系統(tǒng)性能瓶頸,業(yè)界總結(jié)出了一系列對架構(gòu)影響最小、但效果顯著的優(yōu)化方法,其中“三大分離”是核心思想:
- 動靜分離:將網(wǎng)站的靜態(tài)資源(如圖片、CSS、JavaScript)和動態(tài)內(nèi)容(需要服務(wù)器實時處理的數(shù)據(jù))分開部署和處理,通過專門的靜態(tài)資源服務(wù)器或CDN加速靜態(tài)內(nèi)容的訪問,減輕應(yīng)用服務(wù)器的壓力。
- 讀寫分離:將數(shù)據(jù)庫的讀操作和寫操作分離到不同的數(shù)據(jù)庫實例上,以提高數(shù)據(jù)庫的并發(fā)處理能力和整體性能。
- 熱點數(shù)據(jù)分離:將訪問頻率極高的數(shù)據(jù)(熱點數(shù)據(jù))從主數(shù)據(jù)庫中剝離出來,存儲到高速緩存(如Redis、Memcached)中,從而顯著提升數(shù)據(jù)讀取速度。
這“三大分離”理念為后續(xù)更復雜的分布式系統(tǒng)架構(gòu)奠定了基礎(chǔ)。
二、讀寫分離:數(shù)據(jù)庫性能提升的關(guān)鍵
讀寫分離是一種通過用數(shù)據(jù)庫分組來快速提升數(shù)據(jù)庫性能的優(yōu)化策略。其核心思想是:在大多數(shù)Web應(yīng)用中,讀操作的頻率遠高于寫操作。如果所有讀寫請求都集中在一個數(shù)據(jù)庫實例上,該實例很容易成為性能瓶頸。通過將讀寫操作分離到不同的數(shù)據(jù)庫實例上,可以有效分攤壓力,提高數(shù)據(jù)庫集群的整體吞吐量。
1. 讀寫分離的實現(xiàn)方式
- 主從復制(Master-Slave Replication):這是讀寫分離最常見的實現(xiàn)基礎(chǔ)。一個主數(shù)據(jù)庫(Master)負責所有的寫操作,并將數(shù)據(jù)同步(復制)到一個或多個從數(shù)據(jù)庫(Slave)。所有的讀操作則分發(fā)到這些從數(shù)據(jù)庫上。
- 路由層:在應(yīng)用層和數(shù)據(jù)庫層之間引入一個路由層(可以是代碼邏輯、數(shù)據(jù)庫中間件或代理),負責識別SQL語句是讀操作還是寫操作,并將其轉(zhuǎn)發(fā)到相應(yīng)的主庫或從庫。
應(yīng)用層實現(xiàn):在應(yīng)用程序代碼中判斷SQL類型,手動選擇連接到主庫或從庫。這種方式實現(xiàn)簡單,但代碼侵入性強,維護成本高。
數(shù)據(jù)庫中間件/代理:使用專門的數(shù)據(jù)庫中間件(如MyCAT、ShardingSphere、DRDS等)或代理層。這些中間件可以透明地實現(xiàn)讀寫分離,應(yīng)用程序無需感知底層數(shù)據(jù)庫的拓撲結(jié)構(gòu),只需連接到中間件即可。這是目前主流且推薦的實現(xiàn)方式。
2. 讀寫分離的優(yōu)勢
- 提升數(shù)據(jù)庫并發(fā)能力:讀操作不再阻塞寫操作,反之亦然,大大增加了數(shù)據(jù)庫集群的并發(fā)處理能力。
- 增強系統(tǒng)可用性:當主庫發(fā)生故障時,可以快速將某個從庫提升為新的主庫,保證服務(wù)的連續(xù)性。部分從庫故障不影響讀服務(wù)。
- 擴展性:可以通過增加從庫的數(shù)量來線性擴展讀服務(wù)的承載能力。
- 降低成本:從庫可以使用配置較低的服務(wù)器,降低硬件成本。
3. 讀寫分離的挑戰(zhàn)
- 數(shù)據(jù)同步延遲:主從之間的數(shù)據(jù)同步可能存在延遲,導致從庫讀取到的數(shù)據(jù)不是最新的。這需要應(yīng)用層在對實時性要求高的場景下進行特殊處理(如強制讀主庫)。
- 復雜性增加:引入主從架構(gòu)和路由層會增加系統(tǒng)的復雜性,需要額外的運維和監(jiān)控。
三、水平切分:提升數(shù)據(jù)庫存儲容量
除了讀寫分離,當單一數(shù)據(jù)庫的存儲容量或?qū)懭胄阅苓_到瓶頸時,水平切分(Sharding)成為必要的手段。水平切分通過用數(shù)據(jù)庫分片,提升數(shù)據(jù)庫存儲容量,這往往涉及復雜的系統(tǒng)改造。
1. 水平切分的概念
水平切分是將一個大表的數(shù)據(jù),按照某種規(guī)則(如用戶ID的哈希值、時間范圍等)分散存儲到多個獨立的數(shù)據(jù)庫實例或表中。每個數(shù)據(jù)庫實例或表只存儲整個數(shù)據(jù)集合的一部分。
2. 水平切分的優(yōu)勢
- 突破單機存儲限制:不再受限于單臺服務(wù)器的存儲容量。
- 提升并發(fā)處理能力:讀寫請求被分散到多個數(shù)據(jù)庫實例,提高了整體的并發(fā)處理能力。
- 隔離故障:單個分片故障不會影響整個系統(tǒng)。
3. 水平切分的挑戰(zhàn)
- 數(shù)據(jù)路由復雜:需要一個機制來確定某個數(shù)據(jù)應(yīng)該存儲在哪個分片上,以及從哪個分片讀取。
- 跨分片查詢:涉及到多個分片的查詢(如聚合查詢、聯(lián)表查詢)會變得非常復雜且效率低下。
- 事務(wù)管理:跨分片的分布式事務(wù)管理難度大。
- 數(shù)據(jù)遷移和擴容:后期進行數(shù)據(jù)遷移或增加分片時,操作復雜。
四、前后端分離:系統(tǒng)解耦與資源瓶頸消除
前后端分離是一種將Web應(yīng)用的用戶界面(前端)和業(yè)務(wù)邏輯(后端)完全獨立開發(fā)、部署和運行的架構(gòu)模式。其核心目標是系統(tǒng)解耦,消除底層資源瓶頸。
1. 前后端分離的實現(xiàn)方式
- 獨立項目:前端項目(HTML, CSS, JavaScript,通常使用Vue, React, Angular等框架)和后端項目(提供API接口,如Java Spring Boot, Node.js Express)作為兩個獨立的項目進行開發(fā)。
- API接口通信:前端通過HTTP/HTTPS協(xié)議調(diào)用后端提供的RESTful API或GraphQL接口來獲取和提交數(shù)據(jù)。
- 獨立部署:前端代碼通常部署在CDN或靜態(tài)Web服務(wù)器上,后端API服務(wù)則部署在應(yīng)用服務(wù)器集群中。
2. 前后端分離的優(yōu)勢
- 職責分離與專業(yè)化:前端和后端團隊可以專注于各自領(lǐng)域,提高開發(fā)效率和專業(yè)度。
- 并行開發(fā):前后端可以并行開發(fā),縮短項目周期。
- 技術(shù)棧獨立:前后端可以采用不同的技術(shù)棧,互不影響,方便技術(shù)升級和創(chuàng)新。
- 提升用戶體驗:前端可以獨立部署在CDN,靜態(tài)資源加載更快,提供更流暢的用戶體驗。
- 消除后端渲染瓶頸:后端專注于提供數(shù)據(jù)接口,不再承擔頁面渲染的壓力,減輕了服務(wù)器負擔。
- 多端適應(yīng):一套后端API可以服務(wù)于Web、移動App、小程序等多個前端應(yīng)用。
- 更好的擴展性:前端和后端可以獨立擴展,當某一端成為瓶頸時,可以單獨擴容。
3. 前后端分離的挑戰(zhàn)
- 聯(lián)調(diào)成本:前后端接口定義和聯(lián)調(diào)需要良好的協(xié)作和接口文檔。
- 部署復雜性:需要獨立部署前端和后端,增加了部署和運維的復雜性。
- SEO問題:對于完全由JavaScript渲染的單頁面應(yīng)用(SPA),搜索引擎爬蟲可能難以抓取內(nèi)容,需要配合SSR(服務(wù)端渲染)或預渲染技術(shù)解決。