偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

查詢飛起!SpringBoot樹形結(jié)構(gòu)從3秒飆到30毫秒的“神級加速”實錄

開發(fā) 前端
從遞歸查詢到高性能樹構(gòu)建,我們完成了 從 O(n2) 到 O(n) 的質(zhì)變。? 這一優(yōu)化讓首頁分類樹的響應時間從 3 秒降到 30 毫秒,真正達到了用戶“無感知”的速度,系統(tǒng)穩(wěn)定性和可維護性也得到極大提升。

在實際業(yè)務中,分類樹(Category Tree)往往是電商、內(nèi)容管理、權限體系等系統(tǒng)的核心數(shù)據(jù)結(jié)構(gòu)。隨著業(yè)務規(guī)模不斷擴張,原本毫無壓力的樹形查詢,突然變成了系統(tǒng)性能的瓶頸。

我所在的項目在首頁加載分類樹時,最初需要 3–5 秒 才能渲染完成,用戶大量投訴“卡頓、慢”,運維監(jiān)控顯示 高峰期數(shù)據(jù)庫連接池被耗盡,甚至引發(fā)系統(tǒng)崩潰。開發(fā)團隊雖然嘗試過各種優(yōu)化,但基本都屬于“打補丁”,沒能解決根本問題。

最終我們通過一次性查詢 + 高效內(nèi)存構(gòu)建樹的方式,將性能從 3 秒直接壓縮到 30 毫秒,實現(xiàn)了 100 倍提速。本文將完整還原這個過程,幫助你在遇到類似問題時快速找到正確的解決方案。

性能瓶頸:遞歸查詢的災難

傳統(tǒng)做法通常是遞歸調(diào)用 getChildren(),每次訪問數(shù)據(jù)庫查詢子節(jié)點,看似簡潔,實則暗藏殺機。

public List<Category> getCategoryTree() {
    List<Category> roots = categoryMapper.getRootCategories(); // 1次查詢
    for (Category root : roots) {
        loadChildren(root); // 每個節(jié)點觸發(fā)遞歸查詢
    }
    return roots;
}


private void loadChildren(Category parent) {
    List<Category> children = categoryMapper.getByParentId(parent.getId()); // N次查詢
    parent.setChildren(children);
    for (Category child : children) {
        loadChildren(child);
    }
}

問題分析:

  • 1 萬節(jié)點 = 1 萬次 SQL
  • 單次查詢平均 2ms → 總耗時 20 秒
  • 數(shù)據(jù)庫連接池被瞬間榨干
  • 內(nèi)存中反復創(chuàng)建對象,GC 壓力暴增

結(jié)果就是:首頁分類樹幾乎不可用

性能測試對比

我們對比了不同規(guī)模下的執(zhí)行耗時:

節(jié)點數(shù)

遞歸查詢方案

優(yōu)化后方案

提升倍數(shù)

1,000

800ms

15ms

53x

5,000

2.8s

25ms

112x

10,000

5.2s

30ms

173x

50,000

超時

45ms

1000x+

根本解決思路:一次查詢 + O(n) 算法

核心理念

  • 數(shù)據(jù)庫只查一次 → 獲取所有節(jié)點
  • HashMap 建索引 → O(1) 查找父子關系
  • 單次遍歷構(gòu)建樹 → O(n) 時間復雜度
  • 結(jié)果緩存 → 避免重復構(gòu)建

數(shù)據(jù)庫表設計(鄰接表 + level 字段)

CREATE TABLE category (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    parent_id BIGINT,
    level INT NOT NULL DEFAULT 0,
    sort_order INT DEFAULT 0,
    status TINYINT DEFAULT 1,
    create_time DATETIME,
    update_time DATETIME,
    INDEX idx_parent_id (parent_id),
    INDEX idx_level (level),
    INDEX idx_status (status)
);

設計要點:

  • level:支持批量層級查詢和排序優(yōu)化
  • (parent_id, sort_order) 復合索引 → 高效排序
  • status:軟刪除、狀態(tài)過濾

復雜度對比

維度

遞歸方案

優(yōu)化后方案

提升

時間復雜度

O(n2)

O(n)

線性提升

空間復雜度

O(h)(遞歸棧深度)

O(n)(HashMap)

更可控

數(shù)據(jù)庫查詢

O(n)

O(1)

極大減少

網(wǎng)絡 I/O

n 次

1 次

n 倍減少

優(yōu)化后的算法實現(xiàn)

通用樹構(gòu)建器

@Component
public class TreeBuilder {


    public <T extends TreeNode<T>> List<T> buildTree(List<T> nodes, Object rootValue) {
        if (CollectionUtils.isEmpty(nodes)) {
            return new ArrayList<>();
        }


        // 1. 建立快速索引 O(n)
        Map<Object, T> nodeMap = nodes.stream()
            .collect(Collectors.toMap(TreeNode::getId, node -> node));


        List<T> rootNodes = new ArrayList<>();


        // 2. 單次遍歷構(gòu)建父子關系 O(n)
        for (T node : nodes) {
            Object parentId = node.getParentId();
            if (Objects.equals(parentId, rootValue)) {
                rootNodes.add(node);
            } else {
                T parent = nodeMap.get(parentId);
                if (parent != null) {
                    parent.addChild(node);
                }
            }
        }


        // 3. 可選:遞歸排序 O(n log n)
        sortTree(rootNodes);


        return rootNodes;
    }


    private <T extends TreeNode<T>> void sortTree(List<T> nodes) {
        if (CollectionUtils.isEmpty(nodes)) return;
        nodes.sort(Comparator.comparing(TreeNode::getSortOrder,
            Comparator.nullsLast(Comparator.naturalOrder())));
        for (T node : nodes) {
            if (node.hasChildren()) sortTree(node.getChildren());
        }
    }
}
樹節(jié)點基類
public interface TreeNode<T> {
    Object getId();
    Object getParentId();
    Integer getSortOrder();
    List<T> getChildren();
    void setChildren(List<T> children);


    default void addChild(T child) {
        if (getChildren() == null) setChildren(new ArrayList<>());
        getChildren().add(child);
    }


    default boolean hasChildren() {
        return getChildren() != null && !getChildren().isEmpty();
    }
}

業(yè)務實現(xiàn)

實體類

package com.icoderoad.module.category;


import com.icoderoad.core.tree.TreeNode;
import lombok.Data;


import java.util.List;


@Data
public class Category implements TreeNode<Category> {
    private Long id;
    private String name;
    private Long parentId;
    private Integer level;
    private Integer sortOrder;
    private Integer status;
    private List<Category> children;


    @Override
    public Object getId() {
        return id;
    }


    @Override
    public Object getParentId() {
        return parentId;
    }


    @Override
    public Integer getSortOrder() {
        return sortOrder;
    }
}

Mapper

package com.icoderoad.module.category;


import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;


import java.util.List;


@Mapper
public interface CategoryMapper {


    @Select("SELECT id, name, parent_id AS parentId, level, sort_order AS sortOrder, status " +
            "FROM category WHERE status = 1 ORDER BY sort_order ASC")
    List<Category> findAllActive();
}

Service 接口

package com.icoderoad.module.category;


import java.util.List;


public interface CategoryService {
    List<Category> getCategoryTree();
}

 Service 實現(xiàn)類

package com.icoderoad.module.category;


import com.icoderoad.core.tree.TreeBuilder;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;


import java.util.List;


@Service
@RequiredArgsConstructor
public class CategoryServiceImpl implements CategoryService {


    private final CategoryMapper categoryMapper;
    private final TreeBuilder treeBuilder;


    @Override
    @Cacheable(value = "categoryTree", key = "'all'")
    public List<Category> getCategoryTree() {
        List<Category> all = categoryMapper.findAllActive();
        return treeBuilder.buildTree(all, 0L); // 假設根節(jié)點 parentId=0
    }
}

Controller

package com.icoderoad.module.category;


import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


import java.util.List;


@RestController
@RequiredArgsConstructor
public class CategoryController {


    private final CategoryService categoryService;


    @GetMapping("/categories/tree")
    public List<Category> getCategoryTree() {
        return categoryService.getCategoryTree();
    }
}

緩存優(yōu)化

我們采用 多級緩存(Caffeine + Redis),既能保證高并發(fā)下的讀性能,又能兼顧分布式環(huán)境中的一致性。

  • L1:Caffeine(本地內(nèi)存,10 分鐘過期)
  • L2:Redis(分布式,2 小時過期)
  • 啟動時預熱,定時刷新,避免冷啟動雪崩

監(jiān)控與保障

  • 使用 Micrometer 統(tǒng)計樹構(gòu)建耗時
  • 結(jié)合 Prometheus + Grafana 做性能監(jiān)控
  • 高峰期壓力測試驗證可承載 5 萬節(jié)點,毫秒級響應

結(jié)論

從遞歸查詢到高性能樹構(gòu)建,我們完成了 從 O(n2) 到 O(n) 的質(zhì)變。 這一優(yōu)化讓首頁分類樹的響應時間從 3 秒降到 30 毫秒,真正達到了用戶“無感知”的速度,系統(tǒng)穩(wěn)定性和可維護性也得到極大提升。

如果你正面臨類似的 N+1 查詢困境,建議優(yōu)先考慮:

  1. 一次查詢 + 內(nèi)存建樹,徹底消滅遞歸 SQL
  2. 合理索引 + Level 字段,讓樹形結(jié)構(gòu)查詢更高效
  3. 多級緩存策略,提升并發(fā)訪問下的可用性
  4. 監(jiān)控與預警,持續(xù)觀察系統(tǒng)性能變化

性能優(yōu)化沒有銀彈,但思路正確,就能實現(xiàn)百倍提升。

責任編輯:武曉燕 來源: 路條編程
相關推薦

2025-06-27 09:05:47

2013-11-11 11:17:45

AngularJS性能優(yōu)化

2025-06-25 09:30:14

2024-11-08 15:08:17

2021-04-27 06:20:25

MySQL集群優(yōu)化

2017-12-25 11:15:06

JavaArray數(shù)組

2022-06-15 11:27:15

開源代碼項目

2019-08-21 14:35:18

壓縮文件優(yōu)化過程Java

2024-04-10 08:00:00

PostgresNoSQL

2020-11-12 18:51:43

Java編程語言

2025-09-15 03:00:00

2025-03-24 08:51:16

2012-03-11 15:27:57

微軟

2017-05-31 13:58:05

戴爾宕機服務器

2021-05-19 15:35:19

數(shù)據(jù)庫工具技術

2015-03-20 10:01:50

Android StuGradle

2019-01-30 09:34:56

ElasticSearLogstashKibana

2024-11-01 12:10:57

2017-08-11 14:28:02

58同城推薦系統(tǒng)

2018-12-21 10:39:31

華為
點贊
收藏

51CTO技術棧公眾號