小巧、快速、可靠。
選擇其中三項。
分析 (ANALYZE)

1. 概述

analyze-stmt

ANALYZE schema-name . table-or-index-name schema-name index-or-table-name

ANALYZE 命令會收集關於資料表和索引的統計資訊,並將收集到的資訊儲存在資料庫的內部資料表中,查詢最佳化器可以在其中存取這些資訊,並使用它們來做出更好的查詢規劃選擇。如果沒有提供任何參數,則會分析主資料庫和所有附加的資料庫。如果提供綱要名稱作為參數,則會分析該資料庫中的所有資料表和索引。如果參數是資料表名稱,則只會分析該資料表及其相關聯的索引。如果參數是索引名稱,則只會分析該索引。

2. 建議的使用模式

使用 ANALYZE 並非必要。然而,如果應用程式執行具有許多可能查詢計劃的複雜查詢,則在執行 ANALYZE 後,查詢規劃器將能夠更好地選擇最佳計劃。這可以顯著提升某些查詢的效能。

接下來的子章節將依偏好順序描述兩種關於何時以及如何執行 ANALYZE 的建議方法。

2.1. 定期執行「PRAGMA optimize」

PRAGMA optimize 命令會在需要時自動執行 ANALYZE。建議用法

  1. 具有短期資料庫連線的應用程式應在關閉每個資料庫連線之前執行一次「PRAGMA optimize;」。

  2. 使用長期資料庫連線的應用程式應在第一次開啟連線時執行「PRAGMA optimize=0x10002;」,然後也應定期執行「PRAGMA optimize;」,例如每天一次,或者如果資料庫快速發展,則可以更頻繁地執行。

  3. 所有應用程式都應在綱要變更後執行「PRAGMA optimize;」,尤其是在一個或多個 CREATE INDEX 陳述式之後。

PRAGMA optimize 命令通常不會執行任何操作,但如果對資料庫的個別資料表執行一個或多個 ANALYZE 子命令對查詢規劃器有用,它偶爾會執行這些命令。自 SQLite 版本 3.46.0 (2024-05-23) 起,「PRAGMA optimize」命令會自動限制 ANALYZE 子命令的範圍,以便即使在龐大的資料庫上,整個「PRAGMA optimize」命令也能快速完成。無需使用 PRAGMA analysis_limit。這是今後執行 ANALYZE 的建議方法。

PRAGMA optimize 命令通常只會考慮在先前由相同資料庫連線查詢過的資料表或在 sqlite_stat1 資料表中沒有項目的資料表上執行 ANALYZE。但是,如果將 0x10000 位元添加到參數中,PRAGMA optimize 將檢查所有資料表,以查看它們是否可以從 ANALYZE 中受益,而不僅僅是最近查詢過的資料表。當第一次開啟資料庫連線時,沒有查詢歷程記錄,這就是為什麼建議在新的資料庫連線上執行 PRAGMA optimize 時添加 0x10000 位元的原因。

請參閱下面的 自動執行 ANALYZE大型資料庫的近似 ANALYZE 章節以取得更多資訊。

2.2. ANALYZE 的固定結果

執行 ANALYZE 指令可能會導致 SQLite 為後續查詢選擇不同的查詢計畫。這幾乎總是一件好事,因為在 ANALYZE 之後選擇的查詢計畫幾乎在所有情況下都比 ANALYZE 之前的查詢計畫更好。這就是 ANALYZE 的重點。但沒有證據證明執行 ANALYZE 總是有益的。可以建構一些特殊情況,在這些情況下,執行 ANALYZE 可能會導致某些後續查詢執行速度變慢。

一些開發人員希望,一旦應用程式的設計完成,SQLite 將始終選擇與開發和測試期間相同的查詢計畫。然後,如果將數百萬份應用程式交付給客戶,開發人員可以確保所有這些數百萬份應用程式都執行相同的查詢計畫,而不管個別客戶在其特定資料庫中插入了哪些資料。這有助於重現來自現場的效能問題投訴。

為了達到這個目的,切勿在應用程式中執行完整的 ANALYZE 指令或 "PRAGMA optimize" 指令。相反,僅在開發期間,使用命令列介面或類似工具,在大小和內容與實際資料庫相似的測試資料庫上手動執行 ANALYZE。然後使用如下腳本擷取這次 ANALYZE 的結果:

.mode list
SELECT 
  'ANALYZE sqlite_schema;' ||
  'DELETE FROM sqlite_stat1;' ||
  'INSERT INTO sqlite_stat1(tbl,idx,stat)VALUES' ||
  (SELECT group_concat(format('(%Q,%Q,%Q)',tbl,idx,stat),',')
    FROM sqlite_stat1) ||
  ';ANALYZE sqlite_schema;';

在應用程式部署的實例中建立新的資料庫實例時,或者在長時間執行的應用程式中,每次啟動應用程式時,執行上述腳本產生的指令。這將完全按照開發和測試期間的狀態填充 sqlite_stat1 表格,並確保在實際環境中選擇的查詢計畫與在實驗室測試中選擇的查詢計畫相同。可以將上述腳本產生的字串複製/貼上到名為 "zStat1Init" 的靜態字串常數中,然後執行:

sqlite3_exec(db, zStat1Init, 0, 0, 0);

還可以根據執行腳本的上下文,在字串常數的開頭添加 "BEGIN;",在結尾添加 "COMMIT;"。

有關更多資訊,請參閱查詢規劃器穩定性保證

3. 詳細資訊

預設實作將所有統計資訊儲存在一個名為 "sqlite_stat1" 的表格中。如果使用 SQLITE_ENABLE_STAT4 選項編譯 SQLite,則會收集額外的直方圖資料並儲存在 sqlite_stat4 中。較舊版本的 SQLite 在使用 SQLITE_ENABLE_STAT2SQLITE_ENABLE_STAT3 編譯時會使用 sqlite_stat2 表格或 sqlite_stat3 表格,但所有最新版本的 SQLite 都會忽略 sqlite_stat2 和 sqlite_stat3 表格。未來的增強功能可能會建立其他 內部表格,其名稱模式相同,只是最後一位數字大於「4」。所有這些表格統稱為「統計資訊表格」。

可以使用 SELECT 查詢統計資訊表格的內容,並可以使用 DELETEINSERTUPDATE 指令更改其內容。從 SQLite 3.7.9 版 (2011-11-01) 開始,DROP TABLE 指令適用於統計資訊表格。ALTER TABLE 指令不適用於統計資訊表格。更改統計資訊表格的內容時應格外小心,因為無效的內容可能會導致 SQLite 選擇低效的查詢計畫。一般來說,除了執行 ANALYZE 指令外,不應透過任何其他機制修改統計資訊表格的內容。有關更多資訊,請參閱「使用 SQLITE_STAT 表格手動控制查詢計畫」。

ANALYZE 收集的統計資訊不會隨著資料庫內容的變化而更新。如果資料庫的內容發生顯著變化,或者資料庫結構發生變化,則應考慮重新執行 ANALYZE 指令以更新統計資訊。

查詢規劃器在讀取結構描述時會將統計資料表的內容載入記憶體。因此,當應用程式直接更改統計資料表時,SQLite 不會立即注意到這些更改。應用程式可以透過執行 ANALYZE sqlite_schema 來強制查詢規劃器重新讀取統計資料表。

4. 自動執行 ANALYZE

PRAGMA optimize 命令會根據需要自動對個別資料表執行 ANALYZE。建議應用程式在關閉每個資料庫連線之前呼叫 PRAGMA optimize 陳述式。或者,如果應用程式長時間保持單個資料庫連線開啟,則應在連線首次開啟時執行「PRAGMA optimize=0x10002」,並在此後定期(可能每天一次或甚至每小時一次)執行「PRAGMA optimize;」。

每個 SQLite 資料庫連線 都會記錄查詢規劃器何時能從精確的 ANALYZE 結果中受益。這些記錄保存在記憶體中,並在資料庫連線的生命週期內累積。PRAGMA optimize 命令會查看這些記錄,並且僅對那些新的或更新的 ANALYZE 資料可能有所助益的資料表執行 ANALYZE。在大多數情況下,PRAGMA optimize 不會執行 ANALYZE,但它偶爾會對從未分析過的資料表或自上次分析以來顯著增長的資料表執行 ANALYZE。

由於 PRAGMA optimize 的動作在一定程度上取決於先前在同一個資料庫連線上評估的查詢,因此建議將 PRAGMA optimize 延遲到資料庫連線關閉,以便有機會累積盡可能多的使用資訊。對於長時間保持開啟的資料庫連線,每隔幾小時或幾天設定計時器來執行 PRAGMA optimize 也是合理的。在資料庫連線開啟後立即執行 PRAGMA optimize 時,可以在位元遮罩引數中新增 0x10000 位元(使命令變成「PRAGMA optimize=0x10002」),這會導致檢查所有資料表,即使是目前連線期間未查詢過的資料表也會被檢查。

PRAGMA optimize 命令最初是在 SQLite 3.18.0 (2017-03-28) 中引入的,對於所有先前的 SQLite 版本,它都是無效操作。在 SQLite 3.46.0 (2024-05-23) 中,PRAGMA optimize 命令得到了顯著增強,本文檔中提供的建議基於這些增強功能。使用早期版本 SQLite 的應用程式應參考相應的文檔,以獲得有關如何最佳使用 PRAGMA optimize 的更好建議。

5. 大型資料庫的近似 ANALYZE

預設情況下,ANALYZE 會完整掃描每個索引。對於大型資料庫,這可能會很慢。因此,從 SQLite 3.32.0 (2020-05-22) 版本開始,可以使用 PRAGMA analysis_limit 命令來限制 ANALYZE 執行的掃描量,從而幫助 ANALYZE 即使在非常大的資料庫檔案上也能更快地執行。我們稱之為執行「近似 ANALYZE」。

analysis_limit pragma 的建議使用模式如下:

PRAGMA analysis_limit=1000;

這個語法指示 ANALYZE 命令像往常一樣開始對索引進行完整掃描。但是當已訪問的行數達到 1000(或此語法指定的任何其他限制)時,ANALYZE 命令將開始採取行動停止掃描。如果索引的最左欄在之前的 1000 步中至少更改過一次,則分析會立即停止。但是,如果最左欄始終相同,則 ANALYZE 會跳到最左欄不同的第一個條目,並在終止前再讀取 1000 行。

上一段中描述的分析限制效果的細節可能會在 SQLite 的未來版本中更改。但核心思想將保持不變。分析限制 N 將努力將每個索引中訪問的行數限制在約 N。

建議 N 的值介於 100 和 1000 之間。或者,要禁用分析限制,使 ANALYZE 對每個索引進行完整掃描,請將分析限制設定為 0。為了向後相容,分析限制的預設值為 0。

近似 ANALYZE 放置在 sqlite_stat1 表中的值與無限制分析計算的值不完全相同。但它們通常足夠接近。sqlite_stat1 表中的索引統計數據無論如何都是近似值,因此近似 ANALYZE 的結果與傳統完整掃描 ANALYZE 略有不同,幾乎沒有實際影響。可以建構一個病態案例,其中近似 ANALYZE 明顯不如完整掃描 ANALYZE,但在現實世界的問題中,這種情況很少見。

一個好的經驗法則是,在執行「ANALYZE」之前,始終將「PRAGMA analysis_limit=N」設定為 N 介於 100 和 1000 之間。過去,在執行「PRAGMA optimize」之前也建議這樣做,但從 3.46.0 版 (2024-05-23) 開始,這會自動發生。使用 PRAGMA analysis_limit 時,結果的精確度不如以前,但它們足夠精確,而且計算結果的速度要快得多,這意味著開發人員更有可能計算它們。近似 ANALYZE 比完全不執行 ANALYZE 要好。

5.1. 近似 ANALYZE 的限制

sqlite_stat4 表中的內容無法透過非完整掃描來計算。因此,如果指定了非零分析限制,則不會計算 sqlite_stat4 表。

此頁面最後修改時間:2024-05-05 15:23:53 UTC