最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SQL性能優(yōu)化之壓測404的根因追查與解決方案

 更新時間:2026年03月25日 08:29:30   作者:G探險者  
測試人員對任務(wù)列表查詢接口進行并發(fā)壓測時,出現(xiàn)大量 404 響應(yīng)錯誤,經(jīng)初步排查,這些 404 并非業(yè)務(wù)邏輯主動返回,而是接口響應(yīng)超時后由網(wǎng)關(guān)/Nginx 拋出的超時錯誤,下面我們就來看看如何優(yōu)化這一問題吧

今天聊一聊sql優(yōu)化的一則案例分析。

適用數(shù)據(jù)庫:達夢(DM8)/ MySQL

場景:任務(wù)列表查詢接口 work_hour_task 壓力測試

一、背景

測試人員對任務(wù)列表查詢接口進行并發(fā)壓測時,出現(xiàn)大量 404 響應(yīng)錯誤。經(jīng)初步排查,這些 404 并非業(yè)務(wù)邏輯主動返回,而是接口響應(yīng)超時后由網(wǎng)關(guān)/Nginx 拋出的超時錯誤。

觸發(fā)鏈路如下:

SQL 慢查詢(全表掃描 + N+1 子查詢 + filesort)
  → 接口響應(yīng)時間飆升
  → 壓測并發(fā)請求大量堆積
  → 網(wǎng)關(guān)請求排隊超時
  → 返回 404

二、問題 SQL

原始 SQL(MySQL 版本)

EXPLAIN SELECT
    t.id, t.tenant_id, t.task_name, t.is_project_task, t.team_code, t.project_code,
    t.task_type, t.task_sub_type, t.responsible_person_code, t.work_hours, t.estimate_work_hours,
    t.task_desc, t.process, t.plan_start_date, t.plan_end_date, t.task_source,
    t.actual_start_time, t.actual_end_time, t.related_team_code, t.rel_id,
    t.STATUS, t.ext_info, t.version, t.deleted,
    t.created_by, t.created_time, t.updated_by, t.updated_time
FROM work_hour_task t
WHERE t.deleted = 0
  AND (
    t.team_code IN ('webDev')
    OR EXISTS (
      SELECT 1 FROM work_hour_task_related_team tr
      WHERE tr.task_rel_id = t.rel_id
        AND tr.deleted = 0
        AND tr.team_code IN ('webDev')
        AND tr.TENANT_ID = 'hkbank'
    )
  )
  AND t.TENANT_ID = 'hkbank'
ORDER BY t.created_time DESC
LIMIT 15

達夢版本結(jié)構(gòu)基本一致,WHERE 條件中額外使用了 FIND_IN_SET 函數(shù)代替 EXISTS 子查詢。

三、執(zhí)行計劃解讀

MySQL 版本執(zhí)行計劃

idselect_typetypekeyrowsExtra
1PRIMARYALLNULL1233Using where; Using filesort
2DEPENDENT SUBQUERYeq_refuk_task_team_del1Using index condition; Using where

達夢版本執(zhí)行計劃

節(jié)點類型描述
NSET2 → PRJT2 → SORT3結(jié)果集 → 投影 → 排序存在額外排序開銷
UNION FOR OR2OR 條件拆成兩路掃描兩路各自回表,代價翻倍
BLKUP2 × 2BOOKMARK LOOKUP兩路均存在回表
SSEK2 × 2二級索引 seek其中一路因 FIND_IN_SET 索引失效

四、三大核心問題

問題一:全表掃描(type = ALL)

主表 t 有 3 個候選索引(idx_task_team、idx_task_page_core、idx_task_count_core),但優(yōu)化器最終 key = NULL,全部放棄,被迫全表掃描 1233 行

根本原因: WHERE 條件中對列使用了函數(shù)(FIND_IN_SET)或存在關(guān)聯(lián)子查詢(EXISTS),優(yōu)化器無法利用索引進行范圍掃描。

問題二:DEPENDENT SUBQUERY(N+1 問題)

EXISTS 子查詢的 select_typeDEPENDENT SUBQUERY,意味著它依賴外層主表的每一行逐行觸發(fā)執(zhí)行

主表掃描 1233 行 × 子查詢執(zhí)行 1233 次 = I/O 實際放大 1233 倍

子查詢雖然單次走了唯一索引(eq_ref,rows=1),但積累后總代價極高,這是典型的 N+1 問題。

問題三:Using filesort(額外排序)

ORDER BY t.created_time DESC 無法利用現(xiàn)有索引完成排序,數(shù)據(jù)庫須在內(nèi)存或磁盤中對全量結(jié)果集做額外排序。高并發(fā)壓測時,排序操作大量占用 CPU 和內(nèi)存,進一步拖慢響應(yīng)時間。

五、優(yōu)化方案

改寫思路:兩段式查詢

將原來「一條復(fù)雜 SQL 承包所有邏輯」的寫法,拆分為兩步:

  • 第一步(ID 收集層):輕量查詢先收集符合條件的 task id 列表,用 UNION ALL 分別處理兩種匹配條件,各自加 ORDER BY + LIMIT,合并后取 Top N。
  • 第二步(明細查詢層):用主鍵 IN (id1, id2, ...) 查詢完整字段,走主鍵索引,無子查詢,無 filesort。

這種「分步查詢」模式是處理 OR + 子查詢 + 排序分頁 組合場景的標(biāo)準(zhǔn)實踐,徹底解耦了「找哪些記錄」和「取這些記錄的字段」兩個問題。

優(yōu)化后 SQL(MySQL 版本)

-- 第二步:用主鍵 IN 查詢明細,消除子查詢和 filesort
SELECT
    id, tenant_id, task_name, is_project_task, team_code, project_code,
    task_type, task_sub_type, responsible_person_code, work_hours, estimate_work_hours,
    task_desc, process, plan_start_date, plan_end_date, task_source,
    actual_start_time, actual_end_time, related_team_code, rel_id,
    STATUS, ext_info, version, deleted,
    created_by, created_time, updated_by, updated_time
FROM work_hour_task
WHERE id IN (1942, 1941, 1940, 1939, 1938, 1937, 1936, 1935,
             1934, 1933, 1932, 1931, 1930, 1929, 1928)
  AND deleted = 0
  AND TENANT_ID = 'hkbank'

優(yōu)化后執(zhí)行計劃

idselect_typetypekeyrowsExtra
1SIMPLErangePRIMARY15Using where

達夢版本額外改寫

達夢原 SQL 中 FIND_IN_SET 將函數(shù)施加于列上導(dǎo)致索引失效,需在應(yīng)用層將參數(shù)預(yù)先拆分:

-- 改前(索引失效)
OR FIND_IN_SET(?, RELATED_TEAM_CODE) > 0
-- 改后(應(yīng)用層拆分參數(shù)后傳入,索引可正常使用)
OR t.related_team_code IN (?, ?, ?)

推薦覆蓋索引

-- MySQL
ALTER TABLE work_hour_task
ADD INDEX idx_covering (TENANT_ID, deleted, team_code, created_time DESC);

-- 達夢
CREATE INDEX idx_covering
ON work_hour_task (TENANT_ID, deleted, team_code, created_time DESC);

六、優(yōu)化前后對比

指標(biāo)優(yōu)化前優(yōu)化后
select_typePRIMARY + DEPENDENT SUBQUERYSIMPLE
type(掃描類型)ALL(全表掃描)?range(索引范圍掃描)?
key(命中索引)NULL(全部放棄)?PRIMARY(主鍵)?
rows(掃描行數(shù))1233 行 × 子查詢 1233 次 ?15 行 ?
ExtraUsing filesort ?Using where ?
行數(shù)降幅下降 98.8%

優(yōu)化前執(zhí)行路徑:全表掃描 1233 行 → 逐行觸發(fā)子查詢(× 1233 次)→ filesort 排序 → 取 Top 15

優(yōu)化后執(zhí)行路徑:主鍵 range 掃描 15 行 → 直接返回(無子查詢,無排序)

七、經(jīng)驗總結(jié)與規(guī)范建議

本次問題根因清單

#根因影響解決方式
1FIND_IN_SET / EXISTS 對列施函數(shù)索引全部失效,退化為全表掃描改為 IN (?) 或應(yīng)用層預(yù)處理
2DEPENDENT SUBQUERY(N+1)子查詢隨主表每行觸發(fā),I/O 放大 N 倍分步查詢或改寫為 JOIN
3Using filesort全量結(jié)果集額外排序,高并發(fā)時 CPU 飆升建包含 ORDER BY 字段的覆蓋索引
4缺少覆蓋索引索引命中后仍大量回表取字段建聯(lián)合覆蓋索引,字段順序:過濾列 + 排序列

SQL 開發(fā)規(guī)范建議

禁止在 WHERE 條件的列上直接使用函數(shù)FIND_IN_SET、DATE()、YEAR() 等),改為在參數(shù)側(cè)做處理,保持列的"裸露"。

慎用 EXISTS / IN 關(guān)聯(lián)子查詢,考慮改寫為 JOIN 或分步查詢,避免產(chǎn)生 DEPENDENT SUBQUERY。

分頁列表接口推薦「兩段式查詢」:先查 id 列表(輕查詢,走索引),再用主鍵 IN 查完整字段,兩步走比一步復(fù)雜查詢更可控。

新建索引需覆蓋 WHERE 過濾字段 + ORDER BY 字段,減少回表和 filesort,字段順序按選擇性從高到低排列。

上線前必須通過 EXPLAIN 驗證執(zhí)行計劃,重點關(guān)注:

  • type 不得為 ALL
  • Extra 不得出現(xiàn) Using filesort / Using temporary

壓測出現(xiàn)大量非業(yè)務(wù) 404 時,優(yōu)先排查接口響應(yīng)時間和數(shù)據(jù)庫慢查詢?nèi)罩?,而非只看?yīng)用層錯誤日志。

執(zhí)行計劃關(guān)鍵字速查

字段危險值(需優(yōu)化)目標(biāo)值
typeALL(全表)range / ref / eq_ref / const
keyNULL(未用索引)命中具體索引名
rows遠大于實際返回行數(shù)接近實際返回行數(shù)
ExtraUsing filesort / Using temporaryUsing index(覆蓋索引最佳)
select_typeDEPENDENT SUBQUERYSIMPLE / PRIMARY

總結(jié)

這次優(yōu)化的核心收獲是:慢不一定在業(yè)務(wù)代碼里,404 也不一定是路由問題。當(dāng)壓測出現(xiàn)大量超時類 404 時,第一步應(yīng)該打開慢查詢?nèi)罩?,?EXPLAIN 拿出來看。

記住三個關(guān)鍵詞:全表掃描、N+1、filesort。這三者任意一個在高并發(fā)下都足以拖垮接口,三個疊加則必然超時。

優(yōu)化的本質(zhì)不是"加索引"這么簡單,而是要理解優(yōu)化器的決策邏輯——讓 WHERE 條件能走索引,讓子查詢不隨主表行數(shù)膨脹,讓 ORDER BY 不產(chǎn)生額外排序,三點都滿足,性能自然就上去了。

到此這篇關(guān)于SQL性能優(yōu)化之壓測404的根因追查與解決方案的文章就介紹到這了,更多相關(guān)SQL壓測404錯誤排查與解決內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MySQL性能優(yōu)化之索引優(yōu)化與查詢優(yōu)化

    MySQL性能優(yōu)化之索引優(yōu)化與查詢優(yōu)化

    在數(shù)據(jù)庫優(yōu)化中,索引優(yōu)化和查詢優(yōu)化是兩個非常重要的方面,通過合理地使用索引,可以顯著提高查詢效率,這篇文章主要介紹了MySQL性能優(yōu)化之索引優(yōu)化與查詢優(yōu)化的相關(guān)資料,需要的朋友可以參考下
    2025-12-12
  • MySQL實現(xiàn)清空分區(qū)表單個分區(qū)數(shù)據(jù)

    MySQL實現(xiàn)清空分區(qū)表單個分區(qū)數(shù)據(jù)

    這篇文章主要介紹了MySQL實現(xiàn)清空分區(qū)表單個分區(qū)數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 簡單了解mysql方言dialect

    簡單了解mysql方言dialect

    這篇文章主要介紹了簡單了解數(shù)據(jù)庫方言dialect,數(shù)據(jù)庫方言也是如此,MySQL 是一種方言,Oracle 也是一種方言,MSSQL 也是一種方言,他們之間在遵循 SQL 規(guī)范的前提下,都有各自的擴展特性,需要的朋友可以參考下
    2019-07-07
  • MySQL中臨時表的基本創(chuàng)建與使用教程

    MySQL中臨時表的基本創(chuàng)建與使用教程

    這篇文章主要介紹了MySQL中臨時表的基本創(chuàng)建與使用教程,注意臨時表中數(shù)據(jù)的清空問題,需要的朋友可以參考下
    2015-12-12
  • mysql error 1071: 創(chuàng)建唯一索引時字段長度限制的問題

    mysql error 1071: 創(chuàng)建唯一索引時字段長度限制的問題

    這篇文章主要介紹了mysql error 1071: 創(chuàng)建唯一索引時字段長度限制的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 一文詳解MySQL的并發(fā)控制

    一文詳解MySQL的并發(fā)控制

    無論何時只要有多個查詢需要在同一時刻修改數(shù)據(jù),都會產(chǎn)生并發(fā)控制問題,MySQL可以在兩個層面進行并發(fā)控制,服務(wù)器層和存儲引擎層,下面這篇文章主要給大家介紹了關(guān)于MySQL并發(fā)控制的相關(guān)資料,需要的朋友可以參考下
    2023-05-05
  • MySQL如何生成自增的流水號

    MySQL如何生成自增的流水號

    這篇文章主要介紹了MySQL如何生成自增的流水號問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • MySQL使用ReplicationConnection導(dǎo)致連接失效解決

    MySQL使用ReplicationConnection導(dǎo)致連接失效解決

    這篇文章主要為大家介紹了MySQL使用ReplicationConnection導(dǎo)致連接失效問題分析解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • mysql主鍵id的生成方式(自增、唯一不規(guī)則)

    mysql主鍵id的生成方式(自增、唯一不規(guī)則)

    本文主要介紹了mysql主鍵id的生成方式,主要包括兩種生成方式,文中通過代碼示例介紹的非常詳細,感興趣的可以了解一下
    2021-09-09
  • MYSQL定時清除備份數(shù)據(jù)的具體操作

    MYSQL定時清除備份數(shù)據(jù)的具體操作

    這篇文章主要給大家介紹了關(guān)于MYSQL定時清除備份數(shù)據(jù)的具體操作,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用MYSQL具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06

最新評論

峨眉山市| 新野县| 龙海市| 灵丘县| 三台县| 云和县| 二连浩特市| 邵阳市| 台前县| 莒南县| 大关县| 安福县| 宁武县| 林周县| 鲁甸县| 松溪县| 甘谷县| 醴陵市| 胶州市| 嫩江县| 林周县| 肥西县| 洛隆县| 涟源市| 汽车| 沾化县| 东乡族自治县| 宣化县| 南昌县| 安国市| 红河县| 军事| 宿州市| 全州县| 墨竹工卡县| 栖霞市| 天峨县| 且末县| 长海县| 德兴市| 周口市|