從 SQL 查詢優化技巧去看 h2 資料庫查詢原理

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/11/09/17819253.html
-Advertisement-
Play Games

本文目標是:瞭解查詢的核心原理,對比 SQL 查詢優化技巧在 h2database 中的落地實現。前提:為了貼近實際應用,本文 Code Insight 基於 BTree 存儲引擎。 ...


本文目標是:瞭解查詢的核心原理,對比 SQL 查詢優化技巧在 h2database 中的落地實現。

前提:為了貼近實際應用,本文 Code Insight 基於 BTree 存儲引擎。

數據查詢核心原理

資料庫實現查詢的原理:遍歷表/索引,判斷是否滿足where篩選條件,添加到結果集。簡單通用。

對於選擇表還是索引、如何遍歷關聯表、優先遍歷哪個表、怎樣提升遍歷的效率,這個就是資料庫查詢複雜的地方。

/**
 * 查詢命令實現查詢的主要過程
 * @see org.h2.command.dml.Select#queryFlat
 */
private void queryFlat(int columnCount, ResultTarget result, long limitRows) {
    // 遍歷單表 or 關聯表。topTableFilter 可以簡單理解為游標 cursor。
    while (topTableFilter.next()) {
        // 判斷是否符合 where 篩選條件
        if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
            Value[] row = new Value[columnCount];
            // 填充select 需要的 columns ①
            for (int i = 0; i < columnCount; i++) {
                Expression expr = expressions.get(i);
                row[i] = expr.getValue(session);
            }
            // 保存符合條件的數據,這個對應 resultSet
            result.addRow(row);
            // 沒有 sort 語句的情況下,達到 limitRows, 終止 table scan ②
            if ((sort == null || sortUsingIndex) && limitRows > 0 &&
                    result.getRowCount() >= limitRows) {
                break;
            }
        }
    }
}





Join 查詢核心原理

基於狀態機模式,實現多表嵌套迴圈遍歷。

使用的 Join 演算法是: Nested Loop Join。

狀態變遷:BEFORE_FIRST --> FOUND --> AFTER_LAST

/**
 * Check if there are more rows to read.
 * 遍歷的數據 row 記錄在當前 session 中,隨時隨地可以獲取
 *
 * @return true if there are
 * @see org.h2.table.TableFilter#next
 */
public boolean next() {
    // 遍歷結束,沒有符合的條件的 row
    if (state == AFTER_LAST) {
        return false;
    } else if (state == BEFORE_FIRST) {
        // cursor 遍歷初始化, 如果基於索引的游標,則可以提前鎖定數據範圍。③
        cursor.find(session, indexConditions);
        if (!cursor.isAlwaysFalse()) {
            // 如果包含 join 表,重置關聯表的狀態機。
            if (join != null) {
                join.reset();
            }
        }
    } else {
        // state == FOUND || NULL_ROW 的情況
        // 嵌套遍歷 join 關聯表。這是個遞歸調用關聯表的過程。
        if (join != null && join.next()) {
            return true;
        }
    }
    // 表/索引數據掃描,匹配filterCondition,直到找到符合的 row
    while (true) {
        if (cursor.isAlwaysFalse()) {
            state = AFTER_LAST;
        } else {
            if (cursor.next()) {
                currentSearchRow = cursor.getSearchRow();
                current = null;
                state = FOUND;
            } else {
                state = AFTER_LAST;
            }
        }
        // where 條件判斷
        if (!isOk(filterCondition)) {
            continue;
        }
        // 嵌套遍歷 join 關聯表。主表的每一行 row,需要遍歷關聯子表一次。④
        if (join != null) {
            join.reset();
            if (!join.next()) {
                continue;
            }
        }
        // check if it's ok
        if (state == NULL_ROW || joinConditionOk) {
            return true;
        }
    }
    state = AFTER_LAST;
    return false;
}





獲取查詢數據

從遍歷的 row 中,獲取 select 語句需要的 column 數據。

對應的 Cursor 實現是:org.h2.index.PageBtreeCursor

/**
 * 根據 columnId 獲取對應的值
 * @see org.h2.table.TableFilter#getValue
 */
public Value getValue(Column column) {
	if (current == null) {
		// 優先從當前遍歷的 row 獲取數據。
        // 如果是索引中的 row,不會包含所有的行,會有取不到的情況
		Value v = currentSearchRow.getValue(columnId);
		if (v != null) {
			return v;
		}
        // 如果沒有,再嘗試從原始表 row 存儲中獲取數據。⑤
        // 對應的實現: currentRow = index.getRow(session, currentSearchRow.getKey());
		current = cursor.get();
		if (current == null) {
			return ValueNull.INSTANCE;
		}
	}
	return current.getValue(columnId);
}





常用的 SQL 查詢優化技巧

分別對應上述源代碼註釋的數字角標。

①避免使用 SELECT *:只選擇需要的列

如果使用 select *, 即使使用了索引查詢。也需要取原數據行的所有數據(⑤)。會進行數據的二次讀取,也就是回表查詢。影響了性能。

②避免使用 ORDER BY, 儘量使用LIMIT

使用 LIMIT:如果只需要部分結果,可以使用 LIMIT 子句限制返回的行數,避免檢索整個結果集。

如上源代碼,如果沒有 Order By,有limit 限制情況下,可以中途結束表遍歷。

如果有 Order By 的情況下,肯定要執行完成整個掃描遍歷的過程,最終在 result 結果集中再一次進行排序計算。

③使用索引:確保表中的列上有適當的索引,以加快查詢速度。

如果使用索引,在初始化掃描階段,會給 cursor 一定的範圍,避免全表掃描。極大的縮小的查詢範圍。

④減少連接的表的數量:如果可能,儘量減少查詢中的表的數量。

無需多言,嵌套遞歸查詢,理論上是所有表的笛卡爾積。

使用覆蓋索引:一個查詢的所有列都包含在索引中。

這樣查詢可以只掃描索引而不需要回表。例如,如果你的查詢是 SELECT id, name FROM users WHERE age = 30,那麼在 age, id, name 上創建一個複合索引可以避免回表。

其他

Nested Loop Join

// 用偽代碼表示,可以更清晰理解上述 join 遍歷的過程
for (r in R) {
    for (s in S) {
        if (r satisfy condition s) {
            output <r, s>;
        }
    }
}





MySQL 中的Nested Loop Join

MySQL官方文檔中提到,MySQL只支持Nested Loop Join這一種join algorithm.

MySQL resolves all joins using a nested-loop join method.

This means that MySQL reads a row from the first table, and then finds a matching row in the second table, the third table, and so on.

作者:京東物流 楊攀

來源:京東雲開發者社區 自猿其說Tech 轉載請註明來源


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 問題 運行Springboot測試類時,查詢資料庫裡面數據顯示如下白網頁 程式報如下錯誤 解決方案 Spring Boot應用未能啟動的原因是它沒有找到合適的資料庫配置具體來說,它需要一個數據源(DataSource),但未能在你的配置中找出,也沒有找到任何嵌入式資料庫(H2, HSQL 或 Der ...
  • 哈嘍大家好,我是鹹魚 不知道有沒有小伙伴跟我一樣,剛開始學習 Python 的時候都聽說過 Python 是一種解釋型語言,因為它在運行的時候會逐行解釋並執行,而 C++ 這種是編譯型語言 不過我今天看到了一篇文章,作者提出 Python 其實也有編譯的過程,解釋器會先編譯再執行 不但如此,作者還認 ...
  • 目錄 Welcome to YARP - 1.認識YARP並搭建反向代理服務 Welcome to YARP - 2.配置功能 2.1 - 配置文件(Configuration Files) 2.2 - 配置提供者(Configuration Providers) 2.3 - 配置過濾器(Confi ...
  • 一:背景 1. 講故事 這段時間分析了幾個和網路故障有關的.NET程式之後,真的越來越體會到電腦基礎課的重要,比如 電腦網路 課,如果沒有對 tcpip協議 的深刻理解,解決這些問題真的很難,因為你只能在高層做黑盒測試,你無法看到 tcp 層面的握手和psh通訊。 這篇我們通過兩個小例子來理解一 ...
  • FTP 介紹 FTP是File Transfer Protocol的縮寫,譯為文件傳輸協議,是基於CS結構的應用層協議。其主要作用是在網路上的兩台電腦之間傳輸文件。 FTP傳輸層使用的是TCP協議,它有一個很大的特點是採用雙埠的工作模式,即客戶端和服務端的通信是通過兩個通道進行的 命令通道用於服 ...
  • MySQL總共支持四種連接方式訪問資料庫,如下表格所示,其中Linux平臺環境下,主要有兩種連接方式,一種是TCP/IP連接方式,另外一種是socket方式。Name pipe和Share memory是Windows平臺下獨有的連接方式。 那麼,MySQL資料庫中,我們能否查看會話具體使用的連接方 ...
  • 近日,由天翼雲科技有限公司雲網產品事業部天璣實驗室撰寫的《關於公有雲區分負載QoS感知的記憶體資源動態超分管理優化》(Thoth:Provisioning Overcommitted Memory Resource with Differentiated QoS in Public Clouds)論文... ...
  • 本文分享自華為雲社區《Proxy下的Prepare透傳,讓GaussDB(for MySQL)更穩固,性能更卓越》,作者: GaussDB 資料庫 。 1.引言 在很多業務場景下,資料庫應用程式處理大量相同的SQL語句——只需更改SQL語句中的文字或變數值。例如:使用相同的SQL模板進行WHERE查 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...