正如我們在前一節中看到的,查詢規劃器需要估計查詢所檢索的列數,以便做出良好的查詢計畫選擇。本節快速介紹系統用於這些估計的統計資訊。
統計資訊的一個組成部分是每個表格和索引中的總條目數,以及每個表格和索引佔用的磁碟區塊數。此資訊保存在 pg_class
表格的 reltuples
和 relpages
欄位中。我們可以透過類似於以下查詢來查看它
SELECT relname, relkind, reltuples, relpages FROM pg_class WHERE relname LIKE 'tenk1%'; relname | relkind | reltuples | relpages ----------------------+---------+-----------+---------- tenk1 | r | 10000 | 345 tenk1_hundred | i | 10000 | 11 tenk1_thous_tenthous | i | 10000 | 30 tenk1_unique1 | i | 10000 | 30 tenk1_unique2 | i | 10000 | 30 (5 rows)
在這裡,我們可以看到 tenk1
包含 10000 列,其索引也包含 10000 列,但索引(毫不奇怪)比表格小得多。
出於效率考量,reltuples
和 relpages
不會即時更新,因此它們通常包含稍微過期的值。它們由 VACUUM
、ANALYZE
和一些 DDL 命令(例如 CREATE INDEX
)更新。未掃描整個表格的 VACUUM
或 ANALYZE
操作(通常情況下是這樣)將根據掃描的表格部分,以增量方式更新 reltuples
計數,從而產生近似值。在任何情況下,規劃器都會縮放在 pg_class
中找到的值,以符合目前的實際表格大小,從而獲得更接近的近似值。
由於 WHERE
子句會限制要檢查的列,因此大多數查詢僅檢索表格中的一小部分列。因此,規劃器需要估計 WHERE
子句的選擇性,也就是說,符合 WHERE
子句中每個條件的列的比例。用於此任務的資訊儲存在 pg_statistic
系統目錄中。pg_statistic
中的條目由 ANALYZE
和 VACUUM ANALYZE
命令更新,並且始終是近似值,即使是最近更新的也是如此。
與其直接查看 pg_statistic
,不如在手動檢查統計資訊時查看其檢視 pg_stats
會更好。pg_stats
的設計使其更易於讀取。此外,pg_stats
可供所有人讀取,而 pg_statistic
只能由超級使用者讀取。(這可以防止未授權的使用者從統計資訊中瞭解其他人的表格內容。pg_stats
檢視僅限於顯示目前使用者可以讀取的表格的列。)例如,我們可以執行
SELECT attname, inherited, n_distinct, array_to_string(most_common_vals, E'\n') as most_common_vals FROM pg_stats WHERE tablename = 'road'; attname | inherited | n_distinct | most_common_vals ---------+-----------+------------+------------------------------------ name | f | -0.5681108 | I- 580 Ramp+ | | | I- 880 Ramp+ | | | Sp Railroad + | | | I- 580 + | | | I- 680 Ramp+ | | | I- 80 Ramp+ | | | 14th St + | | | I- 880 + | | | Mac Arthur Blvd+ | | | Mission Blvd+ ... name | t | -0.5125 | I- 580 Ramp+ | | | I- 880 Ramp+ | | | I- 580 + | | | I- 680 Ramp+ | | | I- 80 Ramp+ | | | Sp Railroad + | | | I- 880 + | | | State Hwy 13 Ramp+ | | | I- 80 + | | | State Hwy 24 Ramp+ ... thepath | f | 0 | thepath | t | 0 | (4 rows)
請注意,同一欄位顯示了兩列,一列對應於從 road
表格開始的完整繼承階層 (inherited
=t
),另一列僅包括 road
表格本身 (inherited
=f
)。(為了簡潔起見,我們僅顯示 name
欄位的前十個最常見值。)
ANALYZE
儲存在 pg_statistic
中的資訊量,特別是每個欄位的 most_common_vals
和 histogram_bounds
陣列中的最大條目數,可以使用 ALTER TABLE SET STATISTICS
命令按欄位設定,也可以透過設定 default_statistics_target 設定變數全域設定。目前的預設限制為 100 個條目。提高限制可能會使規劃器能夠做出更準確的估計,尤其是對於資料分佈不規則的欄位,但代價是在 pg_statistic
中佔用更多空間,並花費稍微更多的時間來計算估計值。相反,對於具有簡單資料分佈的欄位,較低的限制可能就足夠了。
有關規劃器使用統計資訊的更多詳細資訊,請參閱第 68 章。
常見的情況是,由於查詢子句中使用的多個欄位相關聯,因此執行錯誤的查詢計畫會導致查詢速度變慢。規劃器通常假設多個條件彼此獨立,當欄位值相關時,此假設不成立。由於一般統計資訊的每個欄位特性,因此無法擷取有關跨欄位關聯的任何知識。但是,PostgreSQL 能夠計算多變數統計資訊,它可以擷取此類資訊。
因為可能的欄位組合數量非常龐大,自動計算多變數統計資料是不切實際的。取而代之,可以建立擴充統計物件,更常被稱為統計物件,來指示伺服器取得感興趣的欄位集合的統計資料。
統計物件是使用 CREATE STATISTICS
指令建立的。建立此類物件僅僅是建立一個目錄項目,表達對統計資料的興趣。實際的資料收集是由 ANALYZE
執行的 (手動指令或背景自動分析)。收集到的值可以在 pg_statistic_ext_data
目錄中檢查。
ANALYZE
基於與計算常規單欄位統計資料相同的資料表列樣本來計算擴充統計資料。由於透過增加資料表或其任何欄位的統計目標 (如前一節所述) 來增加樣本大小,因此更大的統計目標通常會產生更準確的擴充統計資料,以及花費更多時間來計算它們。
以下小節描述目前支援的擴充統計資料種類。
最簡單的擴充統計資料追蹤函數相依性,這是資料庫正規化形式定義中使用的一個概念。如果知道 a
的值就足以確定 b
的值,也就是說,沒有兩個列具有相同的 a
值但 b
值不同,我們就說欄位 b
在函數上相依於欄位 a
。在完全正規化的資料庫中,函數相依性應該只存在於主鍵和超鍵上。然而,在實務中,許多資料集由於各種原因並未完全正規化;為了效能而有意的反正規化是一個常見的例子。即使在完全正規化的資料庫中,某些欄位之間也可能存在部分關聯性,可以用部分函數相依性來表示。
函數相依性的存在直接影響某些查詢中估計值的準確性。如果查詢包含獨立欄位和相依欄位的條件,則相依欄位的條件不會進一步減少結果大小;但是,如果不知道函數相依性,查詢規劃器會假設這些條件是獨立的,導致低估結果大小。
為了告知規劃器函數相依性,ANALYZE
可以收集跨欄位相依性的測量資料。評估所有欄位集合之間的相依程度會非常昂貴,因此資料收集僅限於出現在使用 dependencies
選項定義的統計物件中的那些欄位組。建議僅針對強烈相關的欄位組建立 dependencies
統計資料,以避免在 ANALYZE
和後續的查詢規劃中產生不必要的額外負擔。
以下是收集函數相依性統計資料的範例
CREATE STATISTICS stts (dependencies) ON city, zip FROM zipcodes; ANALYZE zipcodes; SELECT stxname, stxkeys, stxddependencies FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid) WHERE stxname = 'stts'; stxname | stxkeys | stxddependencies ---------+---------+------------------------------------------ stts | 1 5 | {"1 => 5": 1.000000, "5 => 1": 0.423130} (1 row)
由此可見,欄位 1 (郵遞區號) 完全決定欄位 5 (城市),因此係數為 1.0,而城市僅在大約 42% 的時間決定郵遞區號,這表示許多城市 (58%) 由多個郵遞區號表示。
當計算涉及函數相依欄位的查詢的選擇性時,規劃器會使用相依係數調整每個條件的選擇性估計,以避免產生低估值。
函數相依性目前僅適用於考慮將欄位與常數值進行比較的簡單等式條件,以及具有常數值的 IN
子句。它們不用於改善比較兩個欄位的等式條件或將欄位與運算式比較的估計值,也不用於範圍子句、LIKE
或任何其他類型的條件。
在使用函數相依性進行估計時,規劃器假設涉及欄位的條件是相容的,因此是多餘的。如果它們不相容,則正確的估計值為零列,但沒有考慮到這種可能性。例如,給定一個像這樣的查詢
SELECT * FROM zipcodes WHERE city = 'San Francisco' AND zip = '94105';
規劃器會忽略 city
子句,認為它不會改變選擇性,這是正確的。但是,它會對
SELECT * FROM zipcodes WHERE city = 'San Francisco' AND zip = '90210';
做出相同的假設,即使實際上沒有任何列滿足此查詢。但是,函數相依性統計資料沒有提供足夠的資訊來得出這個結論。
在許多實際情況下,通常會滿足此假設;例如,應用程式中可能有一個 GUI,只允許選擇相容的城市和郵遞區號值以在查詢中使用。但如果不是這種情況,函數相依性可能不是一個可行的選項。
單欄位統計資料儲存每個欄位中不同的值數量。當規劃器只有單欄位統計資料時,合併多個欄位時 (例如,對於 GROUP BY a, b
) 的不同值數量估計通常是錯誤的,導致它選擇錯誤的計畫。
為了改善此類估計,ANALYZE
可以收集欄位組的 n-distinct 統計資料。與之前一樣,對每個可能的欄位分組執行此操作是不切實際的,因此只針對出現在使用 ndistinct
選項定義的統計物件中的那些欄位組收集資料。將為所列欄位集合中兩個或多個欄位的每個可能的組合收集資料。
繼續之前的範例,郵遞區號資料表中的 n-distinct 計數可能如下所示
CREATE STATISTICS stts2 (ndistinct) ON city, state, zip FROM zipcodes; ANALYZE zipcodes; SELECT stxkeys AS k, stxdndistinct AS nd FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid) WHERE stxname = 'stts2'; -[ RECORD 1 ]-------------------------------------------------------- k | 1 2 5 nd | {"1, 2": 33178, "1, 5": 33178, "2, 5": 27435, "1, 2, 5": 33178} (1 row)
這表示有三個欄位組合具有 33178 個不同的值:郵遞區號和州;郵遞區號和城市;以及郵遞區號、城市和州 (它們都相等的事實是預期的,因為郵遞區號本身在此資料表中是唯一的)。另一方面,城市和州的組合只有 27435 個不同的值。
建議僅在實際用於分組的欄位組合上建立 ndistinct
統計物件,以及對於組數量估計錯誤導致錯誤計畫的情況。否則,ANALYZE
週期只會被浪費。
另一種為每個欄位儲存的統計資訊是「最常出現值列表」(most-common value lists,MCV)。 這種方式可以針對個別欄位提供非常精確的估計,但對於包含多個欄位條件的查詢,可能會導致顯著的估計錯誤。
為了改善這類估計,ANALYZE
可以收集欄位組合的 MCV 列表。與函數相依性和 n-distinct 係數類似,針對每個可能的欄位組合執行此操作是不切實際的。 在這種情況下更是如此,因為 MCV 列表(與函數相依性和 n-distinct 係數不同)確實會儲存常見的欄位值。 因此,只會為使用 mcv
選項定義的統計物件中一起出現的欄位群組收集資料。
延續先前的範例,ZIP code 表格的 MCV 列表可能如下所示(與較簡單的統計資訊類型不同,檢視 MCV 內容需要一個函式):
CREATE STATISTICS stts3 (mcv) ON city, state FROM zipcodes; ANALYZE zipcodes; SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid), pg_mcv_list_items(stxdmcv) m WHERE stxname = 'stts3'; index | values | nulls | frequency | base_frequency -------+------------------------+-------+-----------+---------------- 0 | {Washington, DC} | {f,f} | 0.003467 | 2.7e-05 1 | {Apo, AE} | {f,f} | 0.003067 | 1.9e-05 2 | {Houston, TX} | {f,f} | 0.002167 | 0.000133 3 | {El Paso, TX} | {f,f} | 0.002 | 0.000113 4 | {New York, NY} | {f,f} | 0.001967 | 0.000114 5 | {Atlanta, GA} | {f,f} | 0.001633 | 3.3e-05 6 | {Sacramento, CA} | {f,f} | 0.001433 | 7.8e-05 7 | {Miami, FL} | {f,f} | 0.0014 | 6e-05 8 | {Dallas, TX} | {f,f} | 0.001367 | 8.8e-05 9 | {Chicago, IL} | {f,f} | 0.001333 | 5.1e-05 ... (99 rows)
這表示城市和州的最常見組合是 DC 的 Washington,其實際頻率(在樣本中)約為 0.35%。 該組合的基本頻率(根據簡單的每個欄位頻率計算)僅為 0.0027%,導致低估兩個數量級。
建議建立MCV統計物件僅適用於實際上一起用於條件中的欄位組合,並且由於群組數量的錯誤估計導致產生不良的執行計畫。 否則,ANALYZE
和計畫週期只會被浪費。
如果您在文件中看到任何不正確、與您使用特定功能的經驗不符或需要進一步說明的內容,請使用此表單來報告文件問題。