You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot API压测时SQL查询变慢问题排查求助

问题排查:Spring Boot + PostgreSQL压测时查询响应时间飙升

我正在对基于Spring Boot + Spring Data JPA、连接PostgreSQL数据库的应用做压测。其中一个查询指定时间段样本详情的REST API,单请求响应约500ms,但压测时响应时间大幅上升。

数据查询Repository代码

@Query(value = """
        select
          to2.sample_number as sampleNumber,
          ta.profile_id    as fromAgentProfileId,
          ta.full_name     as fromAgentName,
          ta2.profile_id   as toAgentProfileId,
          ta2.full_name    as toAgentName,
          to2.sample_type   as sampleTypeId,
          tp.id            as brandId,
          tp.name          as brandName,
          tod.price        as price,
          to2.created_date as createdDate
        FROM tbl_sample_detail tod
          JOIN tbl_sample to2 ON tod.sample_id = to2.id
          JOIN tbl_user ta ON to2.from_user_id = ta.id
          JOIN tbl_user ta2 ON to2.to_user_id = ta2.id
          JOIN tbl_brand tp on tod.brand_id = tp.id
        WHERE
          (ta.profile_id = :profileId OR ta2.profile_id = :profileId)
          AND (-1 = :userId OR to2.from_user_id = :userId OR to2.to_user_id = :userId)
          AND (-1 IN :sampleTypeList OR to2.sample_type IN :sampleTypeList)
          AND (-1 IN :brandIds OR tod.brand_id IN :brandIds)
          AND (-1 IN :brandCategoryIds OR tp.brand_category_id IN :brandCategoryIds)
          AND to2.created_date between :fromTime and :toTime
        ORDER BY to2.created_date DESC
        """, nativeQuery = true, countQuery = """
        SELECT COUNT(*)
        FROM tbl_sample_detail tod
          JOIN tbl_sample to2 ON tod.sample_id = to2.id
          JOIN tbl_user ta ON to2.from_user_id = ta.id
          JOIN tbl_user ta2 ON to2.to_user_id = ta2.id
          JOIN tbl_brand tp on tod.brand_id = tp.id
        WHERE
          (ta.profile_id = :profileId OR ta2.profile_id = :profileId)
          AND (-1 = :userId OR to2.from_user_id = :userId OR to2.to_user_id = :userId)
          AND (-1 IN :sampleTypeList OR to2.sample_type IN :sampleTypeList)
          AND (-1 IN :brandIds OR tod.brand_id IN :brandIds)
          AND (-1 IN :brandCategoryIds OR tp.brand_category_id IN :brandCategoryIds)
          AND to2.created_date between :fromTime and :toTime
        """)
Page<SampleHistoryView> getSampleDetailHistory(
        @Param("userId") long userId, @Param("profileId") long profileId,
        @Param("sampleTypeList") List<Integer> sampleTypeList, @Param("brandIds") List<Short> brandIds,
        @Param("brandCategoryIds") List<Short> brandCategoryIds, @Param("fromTime") LocalDateTime fromTime,
        @Param("toTime") LocalDateTime toTime, Pageable page);

压测发现的问题

压测时通过以下查询发现count语句耗时3-4秒:

SELECT pid,client_addr,(now() - xact_start) AS TIME,state,query FROM  pg_stat_activity WHERE state IN ('active') ORDER BY TIME DESC;

数据库实例为db.t3.xlarge,存储500GB。

执行计划详情

Limit  (cost=1.59..431.88 rows=20 width=113) (actual time=83.950..83.993 rows=20 loops=1)
      ->  Nested Loop  (cost=1.59..67427.27 rows=3134 width=113) (actual time=83.949..83.990 rows=20 loops=1)
            ->  Nested Loop  (cost=1.29..67341.70 rows=3134 width=97) (actual time=83.937..83.962 rows=20 loops=1)
                  ->  Nested Loop  (cost=0.87..66607.30 rows=357 width=69) (actual time=83.925..83.936 rows=4 loops=1)
                        Join Filter: ((ta.profile_id = 4) OR (ta2.profile_id = 4))
                        Rows Removed by Join Filter: 34665
                        ->  Nested Loop  (cost=0.58..31828.79 rows=108554 width=56) (actual time=0.028..35.022 rows=34669 loops=1)
                              ->  Index Scan Backward using idx_06_25_tbl_sample_created_date on tbl_sample to2  (cost=0.29..29111.03 rows=108554 width=43) (actual time=0.015..17.248 rows=34669 loops=1)
                                    Index Cond: ((created_date >= '2023-06-17 19:30:12'::timestamp without time zone) AND (created_date <= '2025-07-18 19:30:12'::timestamp without time zone))
                              ->  Memoize  (cost=0.29..0.32 rows=1 width=29) (actual time=0.000..0.000 rows=1 loops=34669)
                                    Cache Key: to2.from_user_id
                                    Cache Mode: logical
                                    Hits: 34664  Misses: 5  Evictions: 0  Overflows: 0  Memory Usage: 1kB
                                    ->  Index Scan using tbl_user_pkey on tbl_user ta  (cost=0.28..0.31 rows=1 width=29) (actual time=0.003..0.003 rows=1 loops=5)
                                          Index Cond: (id = to2.from_user_id)
                        ->  Index Scan using tbl_user_pkey on tbl_user ta2  (cost=0.28..0.31 rows=1 width=29) (actual time=0.001..0.001 rows=1 loops=34669)
                              Index Cond: (id = to2.to_user_id)
                  ->  Index Scan using idx_sample_detail_sample on tbl_sample_detail tod  (cost=0.42..1.73 rows=33 width=44) (actual time=0.004..0.005 rows=5 loops=4)
                        Index Cond: (sample_id = to2.id)
            ->  Memoize  (cost=0.30..0.34 rows=1 width=18) (actual time=0.001..0.001 rows=1 loops=20)
                  Cache Key: tod.brand_id
                  Cache Mode: logical
                  Hits: 15  Misses: 5  Evictions: 0  Overflows: 0  Memory Usage: 1kB
                  ->  Index Scan using tbl_brand_pkey on tbl_brand tp  (cost=0.29..0.33 rows=1 width=18) (actual time=0.002..0.002 rows=1 loops=5)
                        Index Cond: (id = tod.brand_id)
    Planning Time: 1.426 ms
    Execution Time: 84.059 ms

UPDATE:开启track_io_timing = on后的执行计划

Limit  (cost=1.59..431.88 rows=20 width=113) (actual time=92.846..92.890 rows=20 loops=1)
  Buffers: shared hit=129868
  ->  Nested Loop  (cost=1.59..67427.27 rows=3134 width=113) (actual time=92.845..92.886 rows=20 loops=1)
        Buffers: shared hit=129868
        ->  Nested Loop  (cost=1.29..67341.70 rows=3134 width=97) (actual time=92.834..92.859 rows=20 loops=1)
              Buffers: shared hit=129853
              ->  Nested Loop  (cost=0.87..66607.30 rows=357 width=69) (actual time=92.821..92.832 rows=4 loops=1)
                    Join Filter: ((ta.profile_id = 4) OR (ta2.profile_id = 4))
                    Rows Removed by Join Filter: 34665
                    Buffers: shared hit=129837
                    ->  Nested Loop  (cost=0.58..31828.79 rows=108554 width=56) (actual time=0.019..38.105 rows=34669 loops=1)
                          Buffers: shared hit=25830
                          ->  Index Scan Backward using idx_06_25_tbl_sample_created_date on tbl_sample to2  (cost=0.29..29111.03 rows=108554 width=43) (actual time=0.009..16.956 rows=34669 loops=1)
                                Index Cond: ((created_date >= '2023-06-17 19:30:12'::timestamp without time zone) AND (created_date <= '2025-07-18 19:30:12'::timestamp without time zone))
                                Buffers: shared hit=25815
                          ->  Memoize  (cost=0.29..0.32 rows=1 width=29) (actual time=0.000..0.000 rows=1 loops=34669)
                                Cache Key: to2.from_user_id
                                Cache Mode: logical
                                Hits: 34664  Misses: 5  Evictions: 0  Overflows: 0  Memory Usage: 1kB
                                Buffers: shared hit=15
                                ->  Index Scan using tbl_user_pkey on tbl_user ta  (cost=0.28..0.31 rows=1 width=29) (actual time=0.002..0.002 rows=1 loops=5)
                                      Index Cond: (id = to2.from_user_id)
                                      Buffers: shared hit=15
                    ->  Index Scan using tbl_user_pkey on tbl_user ta2  (cost=0.28..0.31 rows=1 width=29) (actual time=0.001..0.001 rows=1 loops=34669)
                          Index Cond: (id = to2.to_user_id)
                          Buffers: shared hit=104007
              ->  Index Scan using idx_sample_detail_sample on tbl_sample_detail tod  (cost=0.42..1.73 rows=33 width=44) (actual time=0.004..0.005 rows=5 loops=4)
                    Index Cond: (sample_id = to2.id)
                    Buffers: shared hit=16
        ->  Memoize  (cost=0.30..0.34 rows=1 width=18) (actual time=0.001..0.001 rows=1 loops=20)
              Cache Key: tod.brand_id
              Cache Mode: logical
              Hits: 15  Misses: 5  Evictions: 0  Overflows: 0  Memory Usage: 1kB
              Buffers: shared hit=15
              ->  Index Scan using tbl_brand_pkey on tbl_brand tp  (cost=0.29..0.33 rows=1 width=18) (actual time=0.002..0.002 rows=1 loops=5)
                    Index Cond: (id = tod.brand_id)
                    Buffers: shared hit=15
Planning:
  Buffers: shared hit=86
Planning Time: 1.337 ms
Execution Time: 92.950 ms

SECOND UPDATE:压测时CPU使用率达到100%

CPU使用率截图

恳请协助排查该问题的原因。


内容的提问来源于stack exchange,提问作者iuhettiarachchi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:05:56