SQL語句執行時間太慢,有什么優化措施?以及衍生的相關問題

SQL語句執行時間太慢,有什么優化措施?

可以從四個方面進行:

第一個是查詢是否添加了索引

如果沒有的話,為查詢字段添加索引,

還有是否存在讓索引失效的場景,像是沒有遵循最左前綴,進行了一些類型轉化

第二點是SQL語句本身的優化,

1、如避免使用SELECT *,只查詢需要的字段

2、優化JOIN操作,避免笛卡爾積

如 SELECT * FROM a JOIN b(缺少 a.id = b.a_id),會產生 “笛卡爾積”(數據量 = 表 a 行數 × 表 b 行數),瞬間耗盡數據庫資源。 優化:JOIN 必須加關聯條件,且關聯字段需建索引(如 a.id 和 b.a_id)。

3、大表與大表直接 JOIN 若兩張表均有百萬級數據,直接 JOIN 會產生大量中間結果,耗時極長。

第三點是表結構設計優化

像是采用分庫分表的方式,解決數據量過大問題

  • 水平分表(按行拆分):將一張表按規則拆分為多張表,每張表結構相同,數據不同。常見規則:
    • 時間范圍:orders_2023orders_2024(按年份拆分);
    • 哈希:user_0~user_31(按?user_id % 32?拆分)。工具:Sharding-JDBC、MyCat。
  • 垂直分庫(按業務拆分):將一個數據庫按業務模塊拆分為多個數據庫,如電商系統拆分為?user_db(用戶)、order_db(訂單)、product_db(商品),避免單庫壓力過大。

第四點是架構優化

1、像是使用redis提前存儲數據,減輕數據庫的請求壓力,避免每次查詢都訪問數據庫。

2、采用讀寫分離的方式,將 “讀操作”(如查詢)路由到從庫,“寫操作”(如插入、更新)路由到主庫,避免主庫讀壓力過大。

衍生出的問題:

1、為什么添加索引后,SQL的執行時間就變快了吶?

?首先我們要了解索引這個概念,如果將數據庫比作一本,那么索引就相當于是這本書的目錄,而如果沒有目錄的話,當查找某個內容的話,你只能一頁一頁查找,,數據量越大,翻頁時間越長;

當添加了目錄后,你就可以精準的定位到某個對應,解決無效的翻頁時間。

索引的核心原理就是將“全表掃描”轉化為“精準定位”

數據庫表的原始數據(行數據)存儲在磁盤上,默認是 “無序” 的(除非按主鍵排序)。當沒有索引時,查詢數據(如?WHERE user_id = 123)需要做以下操作:

  1. 從磁盤讀取表的第一行數據,檢查?user_id?是否等于 123;
  2. 不等于則繼續讀第二行、第三行…… 直到遍歷完所有行(全表掃描);
  3. 若表有 100 萬行數據,最壞情況需要讀取 100 萬次磁盤 —— 而磁盤 IO 是數據庫性能的 “最大瓶頸”(磁盤讀寫速度比內存慢 1000 倍以上)。

添加索引后,情況完全不同:索引會單獨創建一個 “有序的索引結構”,把 “查詢條件字段(如?user_id)” 和 “行數據的磁盤地址” 關聯起來,并且按?user_id?排序。此時查詢?user_id = 123?的流程變成:

  1. 去索引結構中查找?user_id = 123—— 由于索引是有序的,可通過 “二分查找”(類似查字典)快速定位,只需 3~4 次磁盤 IO(100 萬數據的二分查找次數僅約 20 次,遠少于全表掃描的 100 萬次);
  2. 從索引中獲取對應行數據的磁盤地址;
  3. 直接根據地址讀取目標行數據,無需遍歷其他行。

底層邏輯

索引的數據結構是B + 樹索引

B+樹作為索引的存儲結構。選擇B+樹的原因包括:

  • 節點可以有更多子節點,路徑更短;
  • 磁盤讀寫代價更低,非葉子節點只存儲鍵值和指針,葉子節點存儲數據;
  • B+樹適合范圍查詢和掃描,因為葉子節點形成了一個雙向鏈表。

2、如何分析這條執行很慢的SQL語句?

采用explain命令,分析這條SQL的執行情況。通過keykey_len可以檢查是否命中了索引,如果已經添加了索引,也可以判斷索引是否有效。通過type字段可以查看SQL是否有優化空間,比如是否存在全索引掃描或全表掃描。通過extra建議可以判斷是否出現回表情況,如果出現,可以嘗試添加索引或修改返回字段來優化。

3、索引失效的場景

  • 沒有遵循最左前綴原則。
  • 使用了模糊查詢且%號在前面。
  • 在索引字段上進行了運算或類型轉換。
  • 使用了復合索引但在中間使用了范圍查詢,導致右邊的條件索引失效。

**擴展:**最左前綴原則

索引失效的最左前綴原則是針對聯合索引(多字段索引)的一條核心規則, 簡單來說:在聯合索引中,查詢條件必須從索引的第一個字段開始匹配,且中間不能跳過任何字段,否則跳過的字段及之后的字段無法使用索引,導致索引失效或部分失效。 底層原理:

聯合索引在底層(如 B + 樹)的存儲是 “先按第一個字段排序,第一個字段相同的再按第二個字段排序,以此類推”

如對對(a, b, c)?建立聯合索引

  1. 先按?a?升序排列;
  2. 當?a?相等時,按?b?升序排列;
  3. 當?a?和?b?都相等時,按?c?升序排列。

4、讀寫分離模式下如何保證主從數據一致性

原因:由于主庫數據同步到從庫存在延遲(如網絡傳輸、SQL 執行耗時),可能導致 “主庫寫入數據后,從庫讀取不到最新數據” 的問題。

解決方式:

  • 配置合適刷盤策越
  • 減少binlog的日志量,避免大事務,拆分為事務。
  • 寫讀后延遲等待,比如寫操作后,線程休眠一段時間,再讀從庫
  • 增加重試機制,:讀從庫時若獲取到舊數據(可通過版本號或時間戳判斷),重試幾次(如 3 次,每次間隔 50ms),直到獲取最新數據或超時后讀主庫。
  • 對于強一致要求的數據,像是金融-支付,可以讀主庫,弱一致性的數據,像是電商商品展示,日志查詢,允許一定的延遲,可以讀從庫。

5、如何保證緩存和數據庫的數據一致性,(如,一次大量的請求到來,如何添加緩存?)

核心原則:先操作數據庫,然后再是緩存

方案一:

最常用的方案,適合大多數業務場景(最終一致性),流程如下:

1. 讀操作

  • 先查緩存:命中則直接返回;
  • 緩存未命中:查數據庫,將結果寫入緩存,再返回。

2. 寫操作

  • 先更新數據庫;
  • 再刪除緩存(而非更新緩存)。

為什么刪除緩存,而不是更新? 主要是避免 “緩存更新邏輯與數據庫更新邏輯不一致” 導致的錯誤(如數據庫有觸發器 / 事務,緩存更新可能漏處理);

方案二:

相對于方案一做出一點改變: 更新數據庫后主動更新緩存

需要注意的點是 必須在數據庫事務內更新緩存,確保數據庫與緩存操作 “同成功同失敗”。

方案三:

延遲雙刪 在高并發場景下,可能出現 “數據庫已更新,但緩存刪除請求因網絡延遲未執行” 的情況,導致舊數據殘留。

操作原理

  • 第一次刪除:盡可能在數據庫更新前清除舊緩存;
  • 第二次刪除:針對 “數據庫更新后,緩存刪除請求失敗” 或 “有其他線程在數據庫更新期間寫入了舊數據到緩存” 的場景,再次清理。

方案四:

基于 binlog 的異步更新緩存(高可用場景)

通過監聽數據庫 binlog(如 MySQL 的 binlog),異步更新緩存,適合讀寫分離、高并發場景:

  1. 流程
    • 數據庫更新后,binlog 記錄數據變更;
    • 監聽組件解析 binlog,獲取變更數據;
    • 緩存更新服務根據變更數據,異步更新或刪除緩存。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/96360.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/96360.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/96360.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

QtConcurrent應用解析

目錄 對比傳統線程 1. QtConcurrent::run() —— 異步運行函數 2.QtConcurrent::mapped() —— 并行轉換 3. QtConcurrent::filter() —— 并行過濾 4. QtConcurrent::run() QFutureWatcher —— UI 異步更新 5.線程池配置 QtConcurrent 是 Qt 框架提供的一個 高級并發編…

大疆圖傳十公里原理:無人機圖傳技術解析

大疆圖傳系統的核心在于把發射端的能量、機載接收的靈敏度、以及環境中的衰減因素,進行科學的預算與動態的修正。簡單的說,就是通過精準的鏈路預算來確保在最壞環境下仍有可用的信號空間。發射功率、天線增益、空中與地面的路徑損耗、接收端的噪聲底線等…

jmeter 帶函數壓測腳本

包含時間戳獲取、md5值計算、隨機字符串獲取<?xml version"1.0" encoding"UTF-8"?> <jmeterTestPlan version"1.2" properties"5.0" jmeter"5.6.3"><hashTree><TestPlan guiclass"TestPlanGui&…

鴻蒙app日志存儲

app的pid獲取 import process from @ohos.process;@Entry @Component struct MainAbility {aboutToAppear(): void {console.log(this.TAG,"pid: "+process.pid)}} 獲取本應用日志 在Android中可以使用logcat --pid xxxx 獲取特定進程xxxx的打印日志 在鴻蒙中也有…

02.【Linux系統編程】Linux權限(root超級用戶和普通用戶、創建普通用戶、sudo短暫提權、權限概念、權限修改、粘滯位)

目錄 1. root超級用戶和普通用戶 2. 創建普通用戶、密碼設置、切換用戶 3. sudo短暫提權&#xff08;給普通用戶添加sudo權限&#xff09; 4. 權限 4.1 是什么 4.2 為什么有權限&#xff1f;&#xff08;權限 角色 目標屬性&#xff09; 4.2.1 角色 4.2.2 目標屬性 …

阿里云可觀測 2025 年 8 月產品動態

本月可觀測熱文回顧 文章一覽&#xff1a; 零代碼改造&#xff01;LoongSuite AI 采集套件觀測實戰 性能瓶頸定位更快更準&#xff1a;ARMS 持續剖析能力升級解析 不只是告警&#xff1a;用阿里云可觀測 MCP 實現 AK 高效安全審計 金蝶云?星辰基于 SLS 構建穩定高效可觀測…

綠蟲零碳助手:通過電費推算用電量,確認光伏裝機規模

在光伏項目開發前期&#xff0c;精準掌握用電需求與合理確定裝機規模是關鍵環節。前者決定光伏系統需滿足的用電基數&#xff0c;后者影響項目投資成本與發電收益匹配度。通過電費數據推算實際用電量&#xff0c;再結合專業工具計算光伏裝機參數&#xff0c;可有效降低項目規劃…

融智學:構建AI時代學術的新范式

融智學&#xff1a;構建AI時代學術新范式摘要&#xff1a;鄒曉輝提出的融智學為現代學術體系困境提供系統性解決方案&#xff0c;通過"問題與價值驅動"的新范式取代傳統"發表驅動"模式。該體系包含三大核心&#xff1a;哲學基礎&#xff08;唯文主義、信息…

【JavaEE初階】-- JVM

文章目錄1. JVM運行流程2. Java運行時數據區2.1 方法區&#xff08;內存共享&#xff09;2.2 堆&#xff08;內存共享&#xff09;2.3 Java虛擬機棧&#xff08;線程私有&#xff09;2.4 本地方法棧&#xff08;線程私有&#xff09;2.5 程序計數器&#xff08;線程私有&#x…

第十四屆藍橋杯青少組C++選拔賽[2023.1.15]第二部分編程題(4 、移動石子)

參考程序1&#xff1a;#include <bits/stdc.h> using namespace std; int main() {int N;cin >> N;vector<int> stones(N);int sum 0;for (int i 0; i < N; i) {cin >> stones[i];sum stones[i];}int target sum / N; // 每個籃子的平均值int a…

Spring Boot 的注解是如何生效的

在 Spring 中&#xff0c;Configuration、ComponentScan、Bean、Import 等注解的掃描、解析和 BeanDefinition 注冊是一個分層處理的過程。下面我們以 Configuration 類為例&#xff0c;結合代碼流程詳細說明其從掃描到注冊的完整邏輯。 1. 整體流程概覽 以下是核心步驟的流程圖…

Django REST Framework響應類Response詳解

概述 Response 類是一個智能的 HTTP 響應類&#xff0c;能夠根據客戶端請求的內容類型&#xff08;Content-Type&#xff09;自動將數據渲染成合適的格式&#xff08;JSON、XML、HTML等&#xff09;。 基本用法 from rest_framework.response import Response# 最簡單的用法 de…

# 小程序 Web 登錄流程完整解析

登錄流程完整小白解析&#xff08;小程序 & Web&#xff09; 在開發中&#xff0c;登錄是每個系統最基礎的功能。為了讓小白也能理解&#xff0c;我們用通俗類比和流程講解 小程序登錄、Web 登錄、Token 刷新、安全存儲等整個過程。1?? 小程序登錄流程&#xff08;小白理…

安裝vcenter6.7 第二階段安裝很慢 或卡在50%

DNS、FQDN配置的問題采用VCSA安裝vCenter時&#xff0c;第一步安裝還算順利&#xff0c;第二步就會安裝失敗&#xff0c;而且還特別慢&#xff0c;這是因為部署時需要DNS服務器&#xff0c;下面就是不采用DNS服務器的部署方案。第一步&#xff1a;正常安裝&#xff0c;DNS就寫本…

第十六屆藍橋杯軟件賽 C 組省賽 C++ 題解

大家好&#xff0c;今天是 2025 年 9 月 11 日&#xff0c;我來給大家寫一篇關于第十六屆藍橋杯軟件賽 C 組省賽的C 題解&#xff0c;希望對大家有所幫助&#xff01;&#xff01;&#xff01; 創作不易&#xff0c;別忘了一鍵三連 題目一&#xff1a;數位倍數 題目鏈接&…

項目幫助文檔的實現

項目幫助文檔的實現 代碼如下&#xff1a; #ifndef __M_HELPER_H__ #define __M_HELPER_H__ #include <iostream> #include <fstream> #include <string> #include <vector> #include <sqlite3.h> #include <random> #include <sstream…

python逆向-逆向pyinstaller打包的exe程序反編譯獲取源代碼

python逆向-逆向pyinstaller打包的exe程序反編譯獲取源代碼 Pyinstaller pyinstaller 是一個用于將 Python 程序打包成獨立可執行文件的工具&#xff0c;能夠在沒有 Python 解釋器的情況下運行。 Python 腳本轉換為 Windows、macOS 和 Linux 操作系統上的可執行文件。 把Python…

【SQL】-- sql having 和 where 的 區別

HAVING 和 WHERE 都是用來篩選數據的&#xff0c;但它們的應用場景有所不同。WHERE&#xff1a;用于篩選行數據&#xff0c;通常在 FROM 子句之后執行。它在分組操作 (GROUP BY) 之前應用&#xff0c;用來篩選出符合條件的記錄。示例&#xff1a;SELECT name, age FROM employe…

MySQL,SQL Server,PostgreSQL三種數據庫各自的優缺點,分別適用哪些場景

MySQL的優缺點及適用場景優點開源免費&#xff0c;社區版可商用&#xff0c;成本低。輕量級&#xff0c;安裝配置簡單&#xff0c;適合中小型項目。讀寫性能優異&#xff0c;尤其在OLTP&#xff08;在線事務處理&#xff09;場景下表現突出。支持主從復制、分片等擴展方案&…

Java 類加載機制雙親委派與自定義類加載器

我們來深入解析 Java 類加載機制。這是理解 Java 應用如何運行、如何實現插件化、以及解決一些依賴沖突問題的關鍵。一、核心概念&#xff1a;類加載過程一個類型&#xff08;包括類和接口&#xff09;從被加載到虛擬機內存開始&#xff0c;到卸載出內存為止&#xff0c;它的整…