掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
譯者 | 劉汪洋

江陵網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)公司等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)自2013年創(chuàng)立以來到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
審校 | 重樓
在現(xiàn)代數(shù)字化環(huán)境下,單純構(gòu)建一個具備基本功能的系統(tǒng)已無法滿足更高的應(yīng)用需求。我們需要開發(fā)在高負(fù)載環(huán)境下能夠穩(wěn)定且高效擴(kuò)展的系統(tǒng)。
眾多開發(fā)者和架構(gòu)師的實(shí)踐證明,系統(tǒng)可擴(kuò)展性的提升往往伴隨著獨(dú)特的挑戰(zhàn)。即使是微小的效率問題,在放大到百萬倍的負(fù)載下,也可能導(dǎo)致系統(tǒng)陷入癱瘓。那么,怎樣才能確保你的應(yīng)用程序在任何負(fù)載下都能快速響應(yīng)呢?
本文將詳細(xì)介紹構(gòu)建可擴(kuò)展系統(tǒng)時的性能優(yōu)化策略。我們會探討一些適用于各種代碼庫的通用策略,無論是前端還是后端,也不論使用何種編程語言。這些策略不僅限于理論層面;它們已在全球一些最具挑戰(zhàn)性的技術(shù)環(huán)境中經(jīng)過實(shí)際應(yīng)用和驗(yàn)證。作為 Facebook 團(tuán)隊(duì)的一員,我親自參與了將這些優(yōu)化技術(shù)應(yīng)用于多個項(xiàng)目中,包括 Facebook 的輕量級廣告創(chuàng)建體驗(yàn)和 Meta 商務(wù)套件。
因此,無論你是在打造下一個大型社交網(wǎng)絡(luò)、企業(yè)級軟件套件,還是僅僅想要優(yōu)化個人項(xiàng)目,我們在此討論的策略都將成為你工具箱中的寶貴資產(chǎn)?,F(xiàn)在,讓我們開始探索吧。
預(yù)取是一種基于預(yù)測用戶行為的性能優(yōu)化技術(shù)。設(shè)想用戶正在與應(yīng)用程序交互,系統(tǒng)能夠預(yù)測用戶的下一步操作,并提前獲取相關(guān)數(shù)據(jù)。這種方法能夠創(chuàng)造一種無縫體驗(yàn):當(dāng)數(shù)據(jù)被需要時,它幾乎能夠即刻被獲取,從而使應(yīng)用程序顯得更加迅速和響應(yīng)靈敏。主動在需求出現(xiàn)之前獲取數(shù)據(jù)能夠顯著提升用戶體驗(yàn),但如果過度使用,可能會導(dǎo)致資源浪費(fèi),如帶寬、內(nèi)存甚至處理能力的浪費(fèi)。Facebook 在其需要依賴機(jī)器學(xué)習(xí)的復(fù)雜操作中大量使用預(yù)取,例如在“好友建議”功能中。
預(yù)取涉及在用戶明確表達(dá)需求之前,主動向服務(wù)器發(fā)送請求以檢索數(shù)據(jù)。盡管這看起來很有吸引力,但開發(fā)者必須確保在效率和資源使用之間取得平衡。
在實(shí)施預(yù)取之前,首先應(yīng)確保服務(wù)器響應(yīng)時間已經(jīng)得到優(yōu)化。后端代碼優(yōu)化可以通過以下方式實(shí)現(xiàn)更佳的服務(wù)器響應(yīng)時間:
預(yù)取的核心是對用戶下一步操作的預(yù)測。然而,預(yù)測有時可能不準(zhǔn)確。如果系統(tǒng)為用戶從未訪問的頁面或功能預(yù)獲取數(shù)據(jù),就會造成資源的浪費(fèi)。因此,開發(fā)者應(yīng)采用機(jī)制來評估用戶意圖,例如跟蹤用戶行為模式或檢查用戶的活躍參與度,以確保數(shù)據(jù)僅在有高概率被使用的情況下被獲取。
預(yù)取可以在任何編程語言或框架中實(shí)現(xiàn)。以 React 為例,來展示預(yù)取的實(shí)現(xiàn)方法。
考慮一個簡單的 React 組件。該組件一旦完成渲染,就會觸發(fā)一個 AJAX 調(diào)用來預(yù)先獲取數(shù)據(jù)。當(dāng)用戶點(diǎn)擊該組件中的按鈕時,第二個組件會使用這些預(yù)先獲取的數(shù)據(jù):
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function PrefetchComponent() {
const [data, setData] = useState(null);
const [showSecondComponent, setShowSecondComponent] = useState(false);
// 組件渲染完成后立即預(yù)取數(shù)據(jù)
useEffect(() => {
axios.get('https://api.example.com/data-to-prefetch')
.then(response => {
setData(response.data);
});
}, []);
return (
{showSecondComponent && }
);
}
function SecondComponent({ data }) {
// 在這個組件中使用預(yù)取的數(shù)據(jù)
return (
{data ? Here is the prefetched data: {data} : Loading...}
);
}
export default PrefetchComponent;在上述代碼示例中,PrefetchComponent組件在渲染之后立刻進(jìn)行數(shù)據(jù)獲取。當(dāng)用戶點(diǎn)擊按鈕時,SecondComponent組件會展示,使用的是之前預(yù)先獲取的數(shù)據(jù)。
在計(jì)算機(jī)科學(xué)中,“不要重復(fù)自己”原則是優(yōu)秀編碼習(xí)慣的核心。此原則也是性能優(yōu)化的有效手段,正是記憶化技術(shù)的基礎(chǔ)。記憶化建立在這樣一個觀點(diǎn)上:重復(fù)執(zhí)行某些操作可能會消耗大量資源,尤其是當(dāng)這些操作的結(jié)果不經(jīng)常發(fā)生變化時。那么,為什么要重復(fù)執(zhí)行已經(jīng)完成的工作呢?
記憶化通過緩存計(jì)算結(jié)果來提升應(yīng)用程序的性能。當(dāng)同一計(jì)算再次被請求時,系統(tǒng)會先檢查結(jié)果是否已在緩存中。如果已緩存,就直接從緩存中提取結(jié)果,省去了實(shí)際計(jì)算的步驟。從本質(zhì)上講,記憶化涉及到對之前結(jié)果的存儲(由此得名)。這對于計(jì)算成本高且經(jīng)常被同樣的輸入調(diào)用的函數(shù)來說尤為有效。這就好比一個學(xué)生解決了一個復(fù)雜的數(shù)學(xué)問題,并在書的邊緣記下了答案。如果未來的考試中出現(xiàn)了同樣的問題,學(xué)生可以簡單地查看書邊的筆記,而不必重新解決這個問題。
記憶化并非適用于所有情況。在某些場景下,記憶化可能會導(dǎo)致更多的內(nèi)存消耗。因此,正確識別何時使用這種技術(shù)至關(guān)重要:
在 React 中,我們可以利用 useCallback 和useMemo等鉤子來實(shí)現(xiàn)記憶化。讓我們來看一個簡單的例子:
import React, { useState, useCallback, useMemo } from 'react';
function ExpensiveOperationComponent() {
const [input, setInput] = useState(0);
const [count, setCount] = useState(0);
// 模擬一個計(jì)算開銷很大的操作
const expensiveOperation = useCallback((num) => {
console.log('Computing...');
// 模擬耗時長的計(jì)算
for(let i = 0; i < 1000000000; i++) {}
return num * num;
}, []);
const memoizedResult = useMemo(() => expensiveOperation(input), [input, expensiveOperation]);
return (
setInput(e.target.value)} />
Result of Expensive Operation: {memoizedResult}
Component re-render count: {count}
);
}
export default ExpensiveOperationComponent;在這個示例中,expensiveOperation函數(shù)模擬了一個計(jì)算密集型任務(wù)。我們使用useCallback鉤子來確保在每次組件渲染時,這個函數(shù)不會被重新定義。此外,useMemo鉤子被用來存儲expensiveOperation的結(jié)果,這樣,即使組件重新渲染,如果輸入沒有變化,就不會重復(fù)執(zhí)行這個計(jì)算。
并行數(shù)據(jù)獲取是指同時獲取多個數(shù)據(jù)集,而非逐個獲取。這就好比在超市結(jié)賬時,有多個收銀員同時服務(wù),而不僅僅是一個:顧客能更快得到服務(wù),排隊(duì)時間縮短,整體效率得到提升。在數(shù)據(jù)處理領(lǐng)域,鑒于很多數(shù)據(jù)集之間互不相關(guān),因此并行獲取能顯著加快頁面加載速度,尤其適用于檢索復(fù)雜數(shù)據(jù)所需時間較長的場景。
在 PHP 中,隨著現(xiàn)代擴(kuò)展和工具的發(fā)展,實(shí)現(xiàn)并行處理變得更為簡便。以下是一個使用concurrent {}代碼塊的基本示例:
fetchDataA(),
"b" => fetchDataB(),
};
echo $result["a"]; // Outputs: Data A
echo $result["b"]; // Outputs: Data B
?>在此示例中,fetchDataA 和 fetchDataB 分別代表兩個數(shù)據(jù)檢索函數(shù)。通過運(yùn)用concurrent {}代碼塊,這兩個函數(shù)可同時執(zhí)行,從而縮短了獲取這兩個數(shù)據(jù)集的總耗時。
延遲加載是一種設(shè)計(jì)模式,其核心思想是僅在真正需要時才加載數(shù)據(jù)或資源。與預(yù)先加載所有內(nèi)容不同,延遲加載只載入初始視圖所需的必要內(nèi)容,隨后根據(jù)需求加載額外資源。這類似于一家餐廳僅在顧客點(diǎn)特定菜品時才開始烹飪,而非預(yù)先準(zhǔn)備所有菜肴。例如,在網(wǎng)頁中,模態(tài)框的數(shù)據(jù)只有在用戶點(diǎn)擊按鈕打開模態(tài)框時才被加載。通過這種方式,可以將數(shù)據(jù)的獲取推遲到實(shí)際需要的時刻。
有效實(shí)現(xiàn)延遲加載的關(guān)鍵在于,要確保在數(shù)據(jù)獲取過程中向用戶提供清晰的反饋,以優(yōu)化用戶體驗(yàn)。常見的做法是在數(shù)據(jù)檢索時展示一個旋轉(zhuǎn)的加載動畫,這樣用戶就能明白他們的請求正在被處理,即便數(shù)據(jù)暫時還不可用。
以下是一個 React 組件中實(shí)現(xiàn)延遲加載的示例。此組件只在用戶點(diǎn)擊按鈕以查看模態(tài)框內(nèi)容時獲取數(shù)據(jù):
import React, { useState } from 'react';
function LazyLoadedModal() {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const fetchDataForModal = async () => {
setIsLoading(true);
// 模擬一次 AJAX 獲取數(shù)據(jù)的調(diào)用
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
setIsLoading(false);
setIsModalOpen(true);
};
return (
{isModalOpen && (
{isLoading ? (
Loading...
// 這里可以使用旋轉(zhuǎn)圈或加載動畫
) : (
{data}
)}
)}
);
}
export default LazyLoadedModal;在這個例子中,只有當(dāng)用戶點(diǎn)擊“打開模態(tài)框”按鈕后,才會開始獲取模態(tài)框的數(shù)據(jù)。在此之前,不會發(fā)起不必要的網(wǎng)絡(luò)請求。一旦開始獲取數(shù)據(jù),便會顯示加載信息(或旋轉(zhuǎn)器),以示用戶請求正在處理。
在當(dāng)今快速的數(shù)字時代,響應(yīng)時間的每一毫秒都十分重要。用戶尋求快速響應(yīng),而企業(yè)無法承受讓用戶等待的后果。性能優(yōu)化已成為提供優(yōu)質(zhì)數(shù)字體驗(yàn)的必要條件,而不僅僅是一種優(yōu)化。
通過預(yù)取、記憶化、并行獲取和延遲加載等技術(shù),開發(fā)者能有效提升應(yīng)用性能。雖然這些策略在應(yīng)用和方法上有所不同,但它們共同的目標(biāo)是確保應(yīng)用程序能夠盡可能高效和快速地運(yùn)行。
重要的一點(diǎn)是,不存在一勞永逸的解決方案或“銀彈”。每個應(yīng)用程序都有其獨(dú)特之處,性能優(yōu)化應(yīng)結(jié)合對應(yīng)用程序需求的深入理解、對用戶期望的認(rèn)識,以及正確技術(shù)的有效應(yīng)用。這是一個持續(xù)改進(jìn)和學(xué)習(xí)的過程。
劉汪洋,社區(qū)編輯,昵稱:明明如月,一個擁有 5 年開發(fā)經(jīng)驗(yàn)的某大廠高級 Java 工程師,擁有多個主流技術(shù)博客平臺博客專家稱號。
原文標(biāo)題:Performance Optimization Strategies in Highly Scalable Systems,作者:Hemanth Murali

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流