文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

高度可扩展系统中的性能优化策略

2024-11-30 04:21

关注

审校 | 重楼

在现代数字化环境下,单纯构建一个具备基本功能的系统已无法满足更高的应用需求。我们需要开发在高负载环境下能够稳定且高效扩展的系统。

众多开发者和架构师的实践证明,系统可扩展性的提升往往伴随着独特的挑战。即使是微小的效率问题,在放大到百万倍的负载下,也可能导致系统陷入瘫痪。那么,怎样才能确保你的应用程序在任何负载下都能快速响应呢?

本文将详细介绍构建可扩展系统时的性能优化策略。我们会探讨一些适用于各种代码库的通用策略,无论是前端还是后端,也不论使用何种编程语言。这些策略不仅限于理论层面;它们已在全球一些最具挑战性的技术环境中经过实际应用和验证。作为 Facebook 团队的一员,我亲自参与了将这些优化技术应用于多个项目中,包括 Facebook 的轻量级广告创建体验和 Meta 商务套件。

因此,无论你是在打造下一个大型社交网络、企业级软件套件,还是仅仅想要优化个人项目,我们在此讨论的策略都将成为你工具箱中的宝贵资产。现在,让我们开始探索吧。

预取

预取是一种基于预测用户行为的性能优化技术。设想用户正在与应用程序交互,系统能够预测用户的下一步操作,并提前获取相关数据。这种方法能够创造一种无缝体验:当数据被需要时,它几乎能够即刻被获取,从而使应用程序显得更加迅速和响应灵敏。主动在需求出现之前获取数据能够显著提升用户体验,但如果过度使用,可能会导致资源浪费,如带宽、内存甚至处理能力的浪费。Facebook 在其需要依赖机器学习的复杂操作中大量使用预取,例如在“好友建议”功能中。

何时进行预取?

预取涉及在用户明确表达需求之前,主动向服务器发送请求以检索数据。尽管这看起来很有吸引力,但开发者必须确保在效率和资源使用之间取得平衡。

A.优化服务器响应时间(后端代码优化)

在实施预取之前,首先应确保服务器响应时间已经得到优化。后端代码优化可以通过以下方式实现更佳的服务器响应时间:

B.确认用户意图

预取的核心是对用户下一步操作的预测。然而,预测有时可能不准确。如果系统为用户从未访问的页面或功能预获取数据,就会造成资源的浪费。因此,开发者应采用机制来评估用户意图,例如跟踪用户行为模式或检查用户的活跃参与度,以确保数据仅在有高概率被使用的情况下被获取。

如何实现预取

预取可以在任何编程语言或框架中实现。以 React 为例,来展示预取的实现方法。

考虑一个简单的 React 组件。该组件一旦完成渲染,就会触发一个 AJAX 调用来预先获取数据。当用户点击该组件中的按钮时,第二个组件会使用这些预先获取的数据:

import React, { useState, useEffect } from 'react';
import axios from 'axios';

function PrefetchComponent() {
  const [data, setData] = useState(null);
  const [showSecondComponent, setShowSecondComponent] = useState(false);
  // 组件渲染完成后立即预取数据
  useEffect(() => {
    axios.get('https://api.example.com/data-to-prefetch')
      .then(response => {
        setData(response.data);
      });
  }, []);
  return (
    
{showSecondComponent && }
); } function SecondComponent({ data }) { // 在这个组件中使用预取的数据 return (
{data ?
Here is the prefetched data: {data}
:
Loading...
}
); } export default PrefetchComponent;

在上述代码示例中,PrefetchComponent组件在渲染之后立刻进行数据获取。当用户点击按钮时,SecondComponent组件会展示,使用的是之前预先获取的数据。

记忆化

在计算机科学中,“不要重复自己”原则是优秀编码习惯的核心。此原则也是性能优化的有效手段,正是记忆化技术的基础。记忆化建立在这样一个观点上:重复执行某些操作可能会消耗大量资源,尤其是当这些操作的结果不经常发生变化时。那么,为什么要重复执行已经完成的工作呢?

记忆化通过缓存计算结果来提升应用程序的性能。当同一计算再次被请求时,系统会先检查结果是否已在缓存中。如果已缓存,就直接从缓存中提取结果,省去了实际计算的步骤。从本质上讲,记忆化涉及到对之前结果的存储(由此得名)。这对于计算成本高且经常被同样的输入调用的函数来说尤为有效。这就好比一个学生解决了一个复杂的数学问题,并在书的边缘记下了答案。如果未来的考试中出现了同样的问题,学生可以简单地查看书边的笔记,而不必重新解决这个问题。

何时使用记忆化?

记忆化并非适用于所有情况。在某些场景下,记忆化可能会导致更多的内存消耗。因此,正确识别何时使用这种技术至关重要:

如何实现记忆化

在 React 中,我们可以利用 useCallback 和useMemo等钩子来实现记忆化。让我们来看一个简单的例子:

import React, { useState, useCallback, useMemo } from 'react';

function ExpensiveOperationComponent() {
    const [input, setInput] = useState(0);
    const [count, setCount] = useState(0);
    // 模拟一个计算开销很大的操作
    const expensiveOperation = useCallback((num) => {
        console.log('Computing...');
        // 模拟耗时长的计算
        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函数模拟了一个计算密集型任务。我们使用useCallback钩子来确保在每次组件渲染时,这个函数不会被重新定义。此外,useMemo钩子被用来存储expensiveOperation的结果,这样,即使组件重新渲染,如果输入没有变化,就不会重复执行这个计算。

并行获取

并行数据获取是指同时获取多个数据集,而非逐个获取。这就好比在超市结账时,有多个收银员同时服务,而不仅仅是一个:顾客能更快得到服务,排队时间缩短,整体效率得到提升。在数据处理领域,鉴于很多数据集之间互不相关,因此并行获取能显著加快页面加载速度,尤其适用于检索复杂数据所需时间较长的场景。

何时使用并行获取?

如何使用并行获取

在 PHP 中,随着现代扩展和工具的发展,实现并行处理变得更为简便。以下是一个使用concurrent {}代码块的基本示例:

 fetchDataA(),
    "b" => fetchDataB(),
};

echo $result["a"];  // Outputs: Data A
echo $result["b"];  // Outputs: Data B
?>

在此示例中,fetchDataA 和 fetchDataB 分别代表两个数据检索函数。通过运用concurrent {}代码块,这两个函数可同时执行,从而缩短了获取这两个数据集的总耗时。

延迟加载

延迟加载是一种设计模式,其核心思想是仅在真正需要时才加载数据或资源。与预先加载所有内容不同,延迟加载只载入初始视图所需的必要内容,随后根据需求加载额外资源。这类似于一家餐厅仅在顾客点特定菜品时才开始烹饪,而非预先准备所有菜肴。例如,在网页中,模态框的数据只有在用户点击按钮打开模态框时才被加载。通过这种方式,可以将数据的获取推迟到实际需要的时刻。

如何实现延迟加载

有效实现延迟加载的关键在于,要确保在数据获取过程中向用户提供清晰的反馈,以优化用户体验。常见的做法是在数据检索时展示一个旋转的加载动画,这样用户就能明白他们的请求正在被处理,即便数据暂时还不可用。

React 中的延迟加载示例

以下是一个 React 组件中实现延迟加载的示例。此组件只在用户点击按钮以查看模态框内容时获取数据:

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 获取数据的调用
        const response = await fetch('https://api.example.com/data');
        const result = await response.json();

        setData(result);
        setIsLoading(false);
        setIsModalOpen(true);
    };

    return (
        
{isModalOpen && (
{isLoading ? (

Loading...

// 这里可以使用旋转圈或加载动画 ) : (

{data}

)}
)}
); } export default LazyLoadedModal;

在这个例子中,只有当用户点击“打开模态框”按钮后,才会开始获取模态框的数据。在此之前,不会发起不必要的网络请求。一旦开始获取数据,便会显示加载信息(或旋转器),以示用户请求正在处理。

结论

在当今快速的数字时代,响应时间的每一毫秒都十分重要。用户寻求快速响应,而企业无法承受让用户等待的后果。性能优化已成为提供优质数字体验的必要条件,而不仅仅是一种优化。

通过预取、记忆化、并行获取和延迟加载等技术,开发者能有效提升应用性能。虽然这些策略在应用和方法上有所不同,但它们共同的目标是确保应用程序能够尽可能高效和快速地运行。

重要的一点是,不存在一劳永逸的解决方案或“银弹”。每个应用程序都有其独特之处,性能优化应结合对应用程序需求的深入理解、对用户期望的认识,以及正确技术的有效应用。这是一个持续改进和学习的过程。

译者介绍

刘汪洋,51CTO社区编辑,昵称:明明如月,一个拥有 5 年开发经验的某大厂高级 Java 工程师,拥有多个主流技术博客平台博客专家称号。

原文Performance Optimization Strategies in Highly Scalable Systems,作者:Hemanth Murali

来源:51CTO内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯