一、緩存輔助模式:根據需要將數據從數據存儲加載到緩存中,以提高讀取性能和響應速度。
1、查緩存,不存在則查庫,并更新緩存:應用程序首先嘗試從緩存中獲取所需數據,如果緩存中不存在,則從數據庫中獲取并更新緩存。這種方式可以提高讀取性能和命中率。
2、直接維護一塊全量數據,與數據庫同步:應用程序在內存中直接維護一份全量數據的副本,并盡量與數據庫保持同步。這種方式適用于數據變動較小的情況,并且可以實現幾乎100%的命中率。如果數據量較小且可以容納在進程內,還能避免跨進程通信的開銷。
在性能優(yōu)化方面,可以更細致地考慮以下問題:
- 在內存中維護復雜數據結構的優(yōu)化:由于內存中的數據結構是直接維護的,而不是通過查詢數據庫獲得的,因此搜索效率可以比數據庫快幾百倍。這對于數據量不大但關系復雜的數據非常有益。
- 數據更新策略的優(yōu)化:在應用程序啟動時,將數據完全加載到內存中,并根據一些策略進行數據更新。更新策略可以包括定時更新和異步更新等方式,不同的數據可以設定不同的更新頻率。
- 數據過期和更新的優(yōu)化:可以為不同的數據設置不同的過期時間,在數據過期后,可以通過請求觸發(fā)主動更新或使用回調方式被動更新。這樣可以確保數據的及時更新,同時避免不必要的數據處理開銷。
- 數據修改時的同步更新:在進行數據修改后,需要及時同步更新緩存和數據庫中的數據,以保持數據的一致性。這種同步機制需要謹慎設計,確保數據的正確性和可靠性。
二、命令和查詢責任分離模式:通過使用不同的接口來分離讀取數據和更新數據的操作,從而提高系統的可維護性和靈活性。
CQRS(Command Query Responsibility Segregation)是一種架構模式,其核心思想是將讀取和寫入操作分離為兩個不同的數據模型。這種方式可以讓讀取和寫入具有完全不同的數據結構,減少彼此之間的干擾,并降低權限控制的復雜度。同時,CQRS也可以在程序內部實現兩套命令模型,分別處理讀取和寫入操作,進行針對性的優(yōu)化和定制。總之,CQRS模式通過將讀取和寫入操作分離,提供了一種靈活和可優(yōu)化的架構方式,使系統在性能、可維護性和權限控制等方面都得到了改善。
現在一般的做法是類似于上圖的做法,為讀寫配置兩套獨立的數據源,并且和事件溯源的方式結合起來做。我們來說說讀寫兩套模型在存儲上分離這個事情,在《相輔相成的存儲五件套》一文中我們的架構圖其實就有這方面的意思。對于讀寫這兩個事情,我們完全可以不用一套數據源,為讀建立專門的物化視圖,可以針對讀進行優(yōu)化,避免在讀的時候做很多Join的工作,可以把性能做到極致(后面會有物化視圖模式的介紹)。事件溯源+CQRS+物化視圖三者一般會結合起來使用。
三、事件溯源模式:使用只追加的存儲方式記錄對領域中數據所做操作的完整系列事件,以便可以重現和審計系統狀態(tài)的變化。
事件溯源(Event Sourcing)是一種軟件開發(fā)模式,它通過記錄和存儲事件序列來代替?zhèn)鹘y的數據狀態(tài)存儲方式。在傳統的CRUD(Create, Read, Update, Delete)模式中,我們會直接對數據進行更新操作,但這樣做可能會導致并發(fā)性和數據丟失等問題。而通過事件溯源模式,我們將重點關注數據的變動事件,而不是當前狀態(tài),具有以下特點:
1. 事件不可變性:事件一旦被創(chuàng)建就不能更改,只能追加新的事件。這確保了數據的完整性和準確性。
2. 事件追蹤和審計:通過記錄完整的事件序列,我們可以追蹤和審計數據的變化歷史。這對于故障排查、審計要求以及法規(guī)合規(guī)性非常重要。
3. 高性能和可伸縮性:事件溯源模式的數據寫入操作只是簡單地將新事件追加到事件流中,這通常比傳統的數據更新操作更高效。此外,由于事件可以并行處理,它具有較好的可伸縮性。
4. 事件驅動的開發(fā):事件溯源模式使得系統可以基于事件進行外部處理,通過訂閱和處理事件來達到不同的業(yè)務目的。這使得系統具有較低的耦合度和更好的靈活性。
5. 保留原始信息:事件溯源記錄了每個事件的完整信息,沒有信息損耗。這使得我們可以隨時訪問歷史數據,并支持回滾操作。
事件溯源模式在某些業(yè)務場景下比傳統的數據狀態(tài)存儲更適用,特別是當業(yè)務更加關注數據的意圖、審計、回滾和歷史功能,并且希望避免數據更新沖突、具有較高的性能以及接受數據的最終一致性時。此外,在事件驅動的系統中使用事件溯源模式更加自然,因為它反映了現實世界中物體之間通過事件相互影響的方式。
四、物化視圖模式:根據需要生成預填充的視圖,以優(yōu)化某些查詢操作,當數據在一個或多個數據存儲中的格式不適合時。
用數據存儲的時候往往會更多考慮存儲而不是讀取。我們使用各種數據庫范式來設計數據庫,在讀取數據的時候我們需要做大量的關聯查詢以輸出符合需要的查詢結果。這個時候性能往往會成為瓶頸,物化視圖是一種空間換時間的做法。與其在查詢的時候做關聯,倒不如提前保存一份面向于查詢和輸出的數據格式。因此,物化視圖適合下面的場景:
經過復雜的計算才能查詢出數據
背后存儲可能會有不穩(wěn)定的情況
需要連接多個不同類型的存儲才能查詢到結果
但是因為需要考慮到物化視圖計算保存的開銷,所以也不太適合于數據變化太頻繁的情況,因為數據加工需要時間,所以不適合需要數據強一致性的場景。實現上一般是基于消息監(jiān)聽做額外維護一套物化視圖的數據源和主流程解耦?;萜盏腣ertica是一款高性能的列式分析數據庫,它的一個特性就是物化視圖,通過事先提供SQL語句直接緩存面向于統計的查詢結果,極大程度提高了性能,也是空間換時間的思想。
五、基于隊列的負載均衡模式:使用隊列作為任務和服務之間的緩沖區(qū),以平滑處理間歇性的重負載,確保系統的穩(wěn)定性和可擴展性。
引入消息隊列并不會直接提高處理能力,但它可以降低系統的耦合性,使得每個組件都能獨立具有彈性。對于無法立即承受負荷的部分,可以利用消息隊列進行緩沖。然而,需要注意的是,消息隊列并不意味著無限制的存儲能力,它也有一定的容量限制。
在使用消息隊列時,需要考慮處理速度和入隊速度之間的比例關系。一般來說,我們需要事先評估系統的需求,并確保處理能力(即每秒處理的事務數 TPS)至少是最高峰值入隊能力(即每秒入隊的事務數 TPS)的兩倍以上。這樣做可以確保系統保持一定的余量,即使業(yè)務邏輯有所修改,處理能力下降了30%,系統仍能夠承受一定的壓力。
換句話說,消息隊列可以作為一個緩沖層,通過調節(jié)處理和入隊速度的比例來確保系統的穩(wěn)定性和彈性。但在設計和配置消息隊列時,需要根據實際情況進行評估和調整,以滿足系統的需求。
六、優(yōu)先級隊列模式:確定發(fā)送到服務的請求的優(yōu)先級,使具有較高優(yōu)先級的請求可以更快地被接收和處理,確保高優(yōu)先級任務的及時完成。
優(yōu)先級隊列與FIFO隊列不同,它允許消息具有不同的處理優(yōu)先級。在實際實現中,有兩種方式可以實現優(yōu)先級隊列:消息優(yōu)先級方式和不同處理池方式。
在消息優(yōu)先級方式中,隊列會實時根據消息的優(yōu)先級進行位置重排,始終將優(yōu)先級較高的消息優(yōu)先處理。而在不同處理池方式中,我們可以針對不同優(yōu)先級的消息配置專門的處理池,以提供更多的處理資源和優(yōu)質的硬件設備。這樣做可以確保高優(yōu)先級消息具有更高的處理能力。在選擇方案和實施時,需要考慮是否需要絕對按照優(yōu)先級處理消息,或者只是相對優(yōu)先處理即可。如果需要絕對優(yōu)先,除了消息位置重排外,還需要實現搶占處理的機制。
另外,如果采用第二種多池方式來處理消息,有可能發(fā)生低優(yōu)先級消息的處理時間比高優(yōu)先級消息更快的情況(如果兩者的業(yè)務邏輯完全不同)。在實現中,RabbitMQ 3.5以上版本支持了消息優(yōu)先級,使用的是第一種方式,在消息堆積緩沖時進行消息重排,消費端可以優(yōu)先處理優(yōu)先級高的消息。但在消費速度大于生產速度的情況下,無法實現高優(yōu)先級消息的優(yōu)先處理。
此外,對于隊列中的消息,還有一種情況需要特別考慮,即長時間停留在隊列中的消息應被視為低優(yōu)先級或死信消息來處理。最好是有專門的消費者來處理這類消息,以避免影響整個隊列的處理。我們也應注意到,在實踐中會遇到由于被廢棄消息阻塞而導致完全失去處理能力的事故。
七、限流模式:控制應用程序、個人租戶或整個服務實例所消耗的資源量,以防止過度負載和資源的浪費,確保系統的平穩(wěn)運行和公平分配資源。
在進行壓力測試時,我們會觀察到系統吞吐量隨著壓力增加而增加,同時響應時間保持在可控范圍內(1秒以內)。然而,當壓力突破一定邊界后,響應時間會突然變得不可控,系統吞吐量下降,并最終導致系統崩潰。每個系統都有其負載的邊界,超過這個邊界,系統將無法滿足服務級別協議(SLA),從而導致用戶無 ** 常使用該服務。
由于系統擴展通常不是短時間內能夠實現的,所以最快的手段是限流,通過限制流量來保護當前系統,防止其突破邊界并徹底崩潰。在處理大量業(yè)務的系統中,對于關鍵服務甚至入口級別進行限流是必要的,沒有其他選擇。例如,在淘寶雙11的凌晨0點,我們也會發(fā)現一定比例的下單請求被限流。
常見的限流算法包括以下幾種:
1. 計數器算法:最簡單的算法,對資源使用進行計數,達到一定計數后拒絕服務。
2. 令牌桶算法:以固定速率往一個桶中放入令牌,桶中最多存放n個令牌,當桶滿時丟棄多余的令牌。在處理請求時需要獲取令牌,無法獲取則拒絕請求。
3. 漏桶算法:一個固定容量的漏洞,按照一定的速率流出水滴(任務),可以以任意速度流入水滴(任務),漏桶滿了則溢出丟棄。
令牌桶算法限制平均流入速率,并允許一定的突發(fā)請求;而漏桶算法限制常量的流出速率,用于平滑流入的速度。在實現上,常用的開源類庫中都有相關的實現,例如Google的Guava庫提供了RateLimiter,它就是基于令牌桶算法實現的。
在進行限流時,需要快速執(zhí)行,任何超過流量控制的請求都不能被放行,否則就失去了意義。同時,限流應該提前執(zhí)行,在系統能力達到80%時最好開始限流,這樣可以減小風險??梢韵蚩蛻舳朔祷靥囟ǖ南蘖骺刂棋e誤代碼,讓用戶知道這不是錯誤,而是限流,可以稍后再嘗試。此外,在監(jiān)控圖上,我們應該敏感地觀察限流曲線,限流后的曲線會突然失去增長的梯度,變得平穩(wěn)。如果監(jiān)控圖的時間范圍太短,可能會誤判這是正常請求量。文章來源:http://www.zghlxwxcb.cn/news/detail-802139.html
限流可以在邊緣節(jié)點上進行。以秒殺場景為例,如果每秒有100萬個請求,將這100萬個請求全部發(fā)送到應用服務器毫無意義。我們可以在邊緣節(jié)點(CDN)甚至客戶端上進行簡單的邊緣計算,讓這100萬個請求按命中注定的方式隨機放棄其中99.9%,只留下1000個請求進入我們的業(yè)務服務。這樣,1000個每秒的TPS一般來說是可處理的。因此,在參與秒殺等活動時,系統會在極短的時間內告知您活動已結束,說明您已被選中,無法進入后端系統參與秒殺。文章來源地址http://www.zghlxwxcb.cn/news/detail-802139.html
關注公眾號:領取架構師面試資料
到了這里,關于架構09- 理解架構的模式3-性能和可擴展性的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!