簡(jiǎn)介 為了執(zhí)行查詢(xún)或 DML 語(yǔ)句(INSERT 、UPDATE 、DELETE ),DB2 必須創(chuàng)建一個(gè)訪問(wèn)計(jì)劃(access plan)。訪問(wèn)計(jì)劃定義按什么順序訪問(wèn)表,使用哪些索引,以及用何種連接(join)方法來(lái)關(guān)聯(lián)數(shù)據(jù)。好的訪問(wèn)計(jì)劃對(duì)于 SQL 語(yǔ)句的快速執(zhí)行至關(guān)重要。DB2 優(yōu)化器可以創(chuàng)建訪問(wèn)計(jì)劃。這是一種基于成本的優(yōu)化器,這意味著它是根據(jù)表和索引的相關(guān)統(tǒng)計(jì)信息來(lái)作出決策的。DB2 在生成統(tǒng)計(jì)信息時(shí),不但能提供基本統(tǒng)計(jì)信息,還允許創(chuàng)建所謂的分布統(tǒng)計(jì)信息。不但數(shù)據(jù)庫(kù)管理員要理解分布統(tǒng)計(jì)信息,而且應(yīng)用程序開(kāi)發(fā)人員也要理解分布統(tǒng)計(jì)信息。應(yīng)用程序開(kāi)發(fā)人員必須小心謹(jǐn)慎,因?yàn)樵谀承┣闆r下分布統(tǒng)計(jì)信息對(duì)于 DB2 優(yōu)化器來(lái)說(shuō)非常重要。主變量或參數(shù)標(biāo)記(在 Java 中為 java.sql.PreparedStatement )的使用可能會(huì)造成阻礙,使優(yōu)化器無(wú)法最大限度地利用分布統(tǒng)計(jì)信息。本文解釋什么是分布統(tǒng)計(jì)信息、分布統(tǒng)計(jì)信息在哪些情況下尤為重要,以及應(yīng)用程序開(kāi)發(fā)人員應(yīng)該考慮些什么,才能使 DB2 優(yōu)化器創(chuàng)建有效的訪問(wèn)計(jì)劃。
基本統(tǒng)計(jì)信息和分布統(tǒng)計(jì)信息 在研究分布統(tǒng)計(jì)信息之前,我們先來(lái)看看基本統(tǒng)計(jì)信息,只要執(zhí)行 RUNSTATS 即可收集這些表的相關(guān)統(tǒng)計(jì)信息。 表的相關(guān)統(tǒng)計(jì)信息: - 當(dāng)前使用的頁(yè)面數(shù)
- 包含記錄行的頁(yè)面數(shù)
- 溢出的行數(shù)
- 表中的行數(shù)(基數(shù))
- 對(duì)于 MDC 表,還有包含數(shù)據(jù)的塊(block)數(shù)
表中各列的相關(guān)統(tǒng)計(jì)信息: - 列的基數(shù)
- 列的平均長(zhǎng)度
- 列中第二大的值
- 列中第二小的值
- 列中 NULL 值的個(gè)數(shù)
通常,執(zhí)行 RUNSTATS 時(shí),不但可以收集到關(guān)于表的統(tǒng)計(jì)信息,而且還可以收集到相應(yīng)的索引的相關(guān)統(tǒng)計(jì)信息。要了解為索引而收集的統(tǒng)計(jì)信息,請(qǐng)參閱 DB2 Administration Guide: Performance - Statistical information that is collected。 觀察一個(gè)表的基本統(tǒng)計(jì)信息,您可以看到,DB2 優(yōu)化器知道一個(gè)表由多少行組成(表的基數(shù)),以及一個(gè)列包含多少個(gè)不同的值(列的基數(shù))。但是,還有一些信息是基本統(tǒng)計(jì)信息無(wú)法提供的。例如,基本統(tǒng)計(jì)信息不能告訴優(yōu)化器一個(gè)列中某些值出現(xiàn)的頻率。假設(shè)表 TABLE_X 有大約 1,000,000 行,在該表上執(zhí)行這樣一條查詢(xún): SELECT * FROM TABLE_X WHERE COLUMN_Y = 'VALUE_Z'
難道 DB2 優(yōu)化器知道 TABLE_X 中有多少行滿(mǎn)足條件 COLUMN_Y = 'VALUE_Z' 不重要嗎?換句話(huà)說(shuō):知道這個(gè)查詢(xún)將返回 1 行、100 行、1000 行還是 10000 行有什么不好呢? 實(shí)際上,通過(guò)基本統(tǒng)計(jì)信息,DB2 優(yōu)化器只能估計(jì) 'VALUE_Z' 在 COLUMN_Y 中出現(xiàn)的頻率。在這種情況下,優(yōu)化器認(rèn)為所有值在 COLUMN_Y 中是平均分布的,這意味著它認(rèn)為所有的值都有相同的出現(xiàn)頻率。如果事實(shí)碰巧如此,這樣估計(jì)并無(wú)大礙。但是,如果有些值比其他值出現(xiàn)得更頻繁一些(例如,如果 'VALUE_Z' 出現(xiàn) 900,000 次,即占所有行的 90%),那么優(yōu)化器不能考慮到這一點(diǎn),因而生成的訪問(wèn)計(jì)劃就不是最優(yōu)的。而分布統(tǒng)計(jì)信息可以填補(bǔ)這一空白。分布統(tǒng)計(jì)信息可以提供關(guān)于數(shù)據(jù)出現(xiàn)頻率及其分布情況的信息,如果數(shù)據(jù)庫(kù)中存儲(chǔ)了很多重復(fù)值,并且數(shù)據(jù)在表中并非平均分布的時(shí)候,分布統(tǒng)計(jì)信息對(duì)于基本統(tǒng)計(jì)信息是一個(gè)重要的補(bǔ)充。
分布統(tǒng)計(jì)信息----頻率分布統(tǒng)計(jì)信息和分位數(shù)分布統(tǒng)計(jì)信息 有兩種不同類(lèi)型的分布統(tǒng)計(jì)信息 —— 頻率統(tǒng)計(jì)信息和分位數(shù)統(tǒng)計(jì)信息。讓我們通過(guò)一個(gè)示例表來(lái)研究一下這兩種不同類(lèi)型的分布統(tǒng)計(jì)信息。 示例表 “CARS” 表示一家汽車(chē)制造商,對(duì)于生產(chǎn)的每一輛汽車(chē),在表中都有相應(yīng)的一行。每輛汽車(chē)可以由它的 ID 來(lái)標(biāo)識(shí),因此 “ID” 是表 “CARS” 的主鍵(PK)。此外,表中有一個(gè) “STATE” 列,表明汽車(chē)當(dāng)前處在制造流程中的哪一步。一輛汽車(chē)的制造流程從第 1 步開(kāi)始,然后是第 2 步、第 3 步,...、第 49 步、第 50 步、第 51 步、...、第 98 步、第 99 步,一直到第 100 步 —— 第 100 步意味著汽車(chē)已經(jīng)完工了。已完工的汽車(chē)所對(duì)應(yīng)的行仍然保留在表中,后續(xù)流程(例如投訴管理、質(zhì)量保證等)仍要用到這些行。汽車(chē)制造商生產(chǎn) 10 種不同型號(hào)(“TYPE” 列)的汽車(chē)。為了簡(jiǎn)化問(wèn)題,在這個(gè)示例表中,各種汽車(chē)型號(hào)命名為 A、B、C、D、...、J。除主鍵索引(在 “ID” 列上)之外,“STATE” 列上也有一個(gè)索引(“I_STATE”),在 “TYPE” 列上還有一個(gè)索引(“I_TYPE”)。實(shí)際上,一個(gè) “CARS” 表包含的列遠(yuǎn)不止 “ID”、“STATE” 和 “TYPE”。為簡(jiǎn)單起見(jiàn),示例表中沒(méi)有出現(xiàn)其他這些列。 頻率分布統(tǒng)計(jì)信息 假設(shè)表 CARS 現(xiàn)在有大約 1,000,000 條記錄,不同的型號(hào)在表中出現(xiàn)的頻率如下所示: 薩1. 表 CARS 中 TYPE 列的頻率統(tǒng)計(jì)信息
TYPE | COUNT(TYPE) |
---|
A | 506135 | B | 301985 | C | 104105 | D | 52492 | E | 19584 | F | 10123 | G | 4876 | H | 4589 | I | 4403 | J | 3727 |
型號(hào)為 A 的汽車(chē)最受購(gòu)買(mǎi)者的青睞,因此生產(chǎn)的汽車(chē)中大約有 50% 是這種型號(hào)。型號(hào) B 和型號(hào) C 僅次于型號(hào) A ,分別占所有汽車(chē)的 30% 和 10%。其他所有型號(hào)加在一起僅占 10%。 上面的表顯示了 “TYPE” 列的頻率統(tǒng)計(jì)信息。通過(guò)基本統(tǒng)計(jì)信息,DB2 優(yōu)化器只能了解到該表包含 1,000,000 行(表的基數(shù))和 10 種不同的值(型號(hào)),即 A 到 J。如果沒(méi)有分布統(tǒng)計(jì)信息,優(yōu)化器會(huì)認(rèn)為每種值以相同的頻率出現(xiàn),大約都是出現(xiàn) 100,000 次。而一旦生成了關(guān)于 “TYPE” 列的分布統(tǒng)計(jì)信息,優(yōu)化器即可了解每種型號(hào)真正的出現(xiàn)頻率。因此,優(yōu)化器清楚各種已有型號(hào)出現(xiàn)的不同頻率。 優(yōu)化器使用頻率統(tǒng)計(jì)信息來(lái)計(jì)算用于檢查相等或不等的謂詞的過(guò)濾因子。例如: SELECT * FROM CARS WHERE TYPE = 'H';
分位數(shù)分布統(tǒng)計(jì)信息
與頻率統(tǒng)計(jì)信息不同,分位數(shù)統(tǒng)計(jì)信息與不同值的出現(xiàn)頻率無(wú)關(guān),而與一個(gè)表中有多少行小于或大于某個(gè)值(或者有多少行介于兩個(gè)值之間)相關(guān)。分位數(shù)統(tǒng)計(jì)信息提供關(guān)于一個(gè)列中的值是否聚合的信息。為獲得這樣的信息,DB2 假定列中的值是按升序排列的,并根據(jù)正則行間隔確定相應(yīng)的值。 我們來(lái)看看表 CARS 中的 “STATE” 列,該列按升序排列。根據(jù)正則行間隔,即可確定 “STATE” 的對(duì)應(yīng)值。 2CARS 表中 STATE 列的分位數(shù)統(tǒng)計(jì)信息
COUNT(row) | STATE ASC |
---|
5479 | 1 | 54948 | 10 | 109990 | 21 | 159885 | 31 | 215050 | 42 | 265251 | 52 | 320167 | 63 | 370057 | 73 | 424872 | 84 | 475087 | 94 | 504298 | 100 | ... | 100 | 1012019 | 100 |
由于已完工的汽車(chē)仍然沒(méi)有從表中刪除,因此狀態(tài)為 100 (=完工)的汽車(chē)數(shù)量比所有處于其他狀態(tài)的汽車(chē)總和還多。已完工的汽車(chē)占表中所有記錄的 50%。 注意: 在實(shí)際情況下,已完工的汽車(chē)數(shù)量甚至還要更多(例如超過(guò) 99%)。在后文中的具體例子中可看到這種情況。 上表顯示了 “STATE” 列的分位數(shù)統(tǒng)計(jì)信息。有了這種關(guān)于有多少行分別小于和大于確定值的信息,優(yōu)化器即可計(jì)算出用于測(cè)試小于(小于等于)、大于(大于等于)或介于兩值之間的謂詞的過(guò)濾因子。例如: SELECT * FROM CARS WHERE STATE <>
SELECT * FROM CARS WHERE STATE BETWEEN 50 AND 70
根據(jù)已有的分位數(shù)統(tǒng)計(jì)信息計(jì)算出來(lái)的過(guò)濾因子不是很精確,但即使只收集 20 個(gè)值,其誤差仍然低于 5%。
DB2優(yōu)化器對(duì)分布統(tǒng)計(jì)信息的使用--示例 如何創(chuàng)建訪問(wèn)計(jì)劃要為 SQL 語(yǔ)句創(chuàng)建訪問(wèn)計(jì)劃,最簡(jiǎn)單的方法就是使用 Visual Explain Tool,這是 DB2 Command Center 中的一個(gè)工具。如果您想使用命令行創(chuàng)建一個(gè)更詳細(xì)的訪問(wèn)計(jì)劃,那么可以按如下步驟操作: - 創(chuàng)建 Explain 表(只需創(chuàng)建一次):
從下面的目錄中可以找到用于生成 Explain 表的腳本:- UNIX/Linux:/sqllib/misc/EXPLAIN.DDL
- Windows:\SQLLIB\MISC\EXPLAIN.DDL
- 用 DB2 CLP 執(zhí)行該腳本:
db2 CONNECT TO USER
db2 -tf EXPLAIN.DDL
db2 CONNECT RESET
- 執(zhí)行以下 SQL 語(yǔ)句,填充 Explain 表:
db2 CONNECT TO USER
db2 SET CURRENT EXPLAIN MODE EXPLAIN
db2
db2 SET CURRENT EXPLAIN MODE NO
db2 CONNECT RESET
- 讀取 Explain 表,并將訪問(wèn)計(jì)劃寫(xiě)入一個(gè)文件:
db2exfmt -d -u -g -o -w -1 -n % -s % -# 0
我們來(lái)看一個(gè)完整的示例,在此例中,DB2 優(yōu)化器可以使用分布統(tǒng)計(jì)信息來(lái)更合理地估計(jì)過(guò)濾因子,以便生成更好的訪問(wèn)計(jì)劃。 這個(gè)示例查詢(xún)從已經(jīng)定義好的 CARS 表中讀取數(shù)據(jù)。對(duì)于表 CARS 中的汽車(chē)數(shù)據(jù),有以下假設(shè): - 該表的基數(shù)為 1,000,000,也就是說(shuō)該表包含 1,000,000 行。
- 表中 99.9% 的汽車(chē)是已經(jīng)完工(“STATE” 列 = 100)的,這些汽車(chē)的信息相關(guān)必須保留,以用于后續(xù)處理(投述管理、質(zhì)量保證等)。剩下的 1,000 輛汽車(chē)目前還處在制造流程中。
- 在該表中,制造商提供的從 A 到 J 的 10 種不同汽車(chē)型號(hào)(“TYPE” 列)幾乎以相同的頻率出現(xiàn)。
注意: 腳本 create_table_cars.sql 用于創(chuàng)建前述 CARS 表,包括 “STATE” 列和 “TYPE” 列上的索引,該腳本可以通過(guò)本文 下載。這個(gè)示例表是在 DB2 SAMPLE 數(shù)據(jù)庫(kù)中(命令 db2sampl ),使用 DBM CFG 和 DB CFG 的默認(rèn)設(shè)置創(chuàng)建的。 示例查詢(xún)選擇型號(hào)為 A 且正處在制造流程中、尚未完工的所有汽車(chē): SELECT * FROM CARS WHERE STATE < 100="" and="" type='A'>
首先來(lái)分析一下,在沒(méi)有分布統(tǒng)計(jì)信息,而只有 CARS 表的基本統(tǒng)計(jì)信息及其索引的情況下,優(yōu)化器選擇的訪問(wèn)計(jì)劃是怎樣的。 1. 沒(méi)有分布統(tǒng)計(jì)信息時(shí)示例查詢(xún)的訪問(wèn)計(jì)劃
由于優(yōu)化器不知道 STATE 值不是均勻分布的,因此它決定使用索引 I_TYPE。使用這個(gè)索引會(huì)帶來(lái)較高的成本,因?yàn)樵谠L問(wèn) CARS 表之前,需要從索引中讀取大約 100,000 個(gè) RID(記錄 ID)。此外,對(duì)于查詢(xún)返回行數(shù)的假設(shè)也是錯(cuò)誤的。因?yàn)閮?yōu)化器認(rèn)為所有制造步驟(從 1 到 100)都有相同的頻率,所以它無(wú)法預(yù)知謂詞 STATE <> 將過(guò)濾掉大量有價(jià)值的行。但是您知道,事實(shí)是在所有 1,000,000 輛汽車(chē)中,只有 1,000 輛汽車(chē)正處在生成流程中。 在沒(méi)有分布統(tǒng)計(jì)信息的情況下,執(zhí)行該查詢(xún)時(shí),動(dòng)態(tài) SQL 的一個(gè)快照返回以下監(jiān)視器值(假定所需的監(jiān)視器開(kāi)關(guān)已激活): Number of executions = 1Number of compilations = 1Worst preparation time (ms) = 9Best preparation time (ms) = 9Internal rows deleted = 0Internal rows inserted = 0Rows read = 99336Internal rows updated = 0Rows written = 0Statement sorts = 0Statement sort overflows = 0Total sort time = 0Buffer pool data logical reads = 8701Buffer pool data physical reads = 8131Buffer pool temporary data logical reads = 0Buffer pool temporary data physical reads = 0Buffer pool index logical reads = 165Buffer pool index physical reads = 155Buffer pool temporary index logical reads = 0Buffer pool temporary index physical reads = 0Total execution time (sec.ms) = 0.530903Total user cpu time (sec.ms) = 0.280403Total system cpu time (sec.ms) = 0.230332Statement text = SELECT ID, TYPE, STATE FROM SAMPLE.CARS WHERE STATE < 100="" and="" type='A'> |
在此,我們不會(huì)進(jìn)一步分析這些值,但是請(qǐng)記住它們,以便與有分布統(tǒng)計(jì)信息時(shí)相同查詢(xún)得到的監(jiān)視器值相比較。 接下來(lái),為 CARS 表生成分布統(tǒng)計(jì)信息,并再次執(zhí)行查詢(xún)。此時(shí),優(yōu)化器選擇了以下訪問(wèn)計(jì)劃: 如何閱讀訪問(wèn)計(jì)劃訪問(wèn)計(jì)劃顯示在執(zhí)行某條 SQL 語(yǔ)句時(shí),數(shù)據(jù)庫(kù)引擎必須執(zhí)行的所有操作。訪問(wèn)計(jì)劃呈倒置樹(shù)型,需要從下往上讀。其中為各操作顯示以下信息: - 估計(jì)該操作將返回的行數(shù)
- 操作的類(lèi)型。例如:
TBSCAN = 表掃描
IXSCAN = 索引掃描
FETCH = 從表中獲取數(shù)據(jù)
NLJOIN = 嵌套的循環(huán)連接
SORT = 排序 - 用于惟一標(biāo)識(shí)操作的一個(gè)數(shù)字
- 該操作的預(yù)計(jì)總成本
- 該操作的預(yù)計(jì) I/O 成本
成本用 timerons 來(lái)表達(dá),這是一個(gè)人造的單位,不能解釋為實(shí)際時(shí)間單位,但有助于判斷一個(gè)操作和整個(gè)訪問(wèn)計(jì)劃的開(kāi)銷(xiāo)。成本是累積的,因?yàn)槊總€(gè)操作的成本都要加到之前所有操作的成本上。
這個(gè)訪問(wèn)計(jì)劃的成本明顯低于沒(méi)有分布統(tǒng)計(jì)信息時(shí)的成本:前者為 203.809,而后者為 3242.63。這是因?yàn)閮?yōu)化器現(xiàn)在知道,謂詞STATE <> 有一個(gè)較高的過(guò)濾因子,因而只會(huì)返回大約 1,000 輛正處在生產(chǎn)流程中尚未完工的汽車(chē)。因此,在這種情況下,CARS 表不是使用索引 I_TYPE 來(lái)訪問(wèn)的,而是使用索引 I_STATE 來(lái)訪問(wèn)的。此外,現(xiàn)在可以正確地估計(jì)結(jié)果集中的總行數(shù)?,F(xiàn)有 1,000 輛汽車(chē)尚未完工,不同的型號(hào)出現(xiàn)頻率相同。故結(jié)果集中包含大約 100 行。 有分布統(tǒng)計(jì)信息時(shí)的訪問(wèn)計(jì)劃要優(yōu)于沒(méi)有分布統(tǒng)計(jì)信息時(shí)的訪問(wèn)計(jì)劃。但是,這是否會(huì)影響查詢(xún)的執(zhí)行時(shí)間?清單 2 包含相應(yīng)的監(jiān)視器數(shù)據(jù)快照: Number of executions = 1Number of compilations = 1Worst preparation time (ms) = 9Best preparation time (ms) = 9Internal rows deleted = 0Internal rows inserted = 0Rows read = 1000Internal rows updated = 0Rows written = 0Statement sorts = 1Statement sort overflows = 0Total sort time = 5Buffer pool data logical reads = 11Buffer pool data physical reads = 10Buffer pool temporary data logical reads = 0Buffer pool temporary data physical reads = 0Buffer pool index logical reads = 12Buffer pool index physical reads = 9Buffer pool temporary index logical reads = 0Buffer pool temporary index physical reads = 0Total execution time (sec.ms) = 0.014597Total user cpu time (sec.ms) = 0.000000Total system cpu time (sec.ms) = 0.010014Statement text = SELECT ID, TYPE, STATE FROM SAMPLE.CARS WHERE STATE < 100="" and="" type='A'> |
下表 比較了有分布統(tǒng)計(jì)信息和沒(méi)有分布統(tǒng)計(jì)信息這兩種不同情況下的快照監(jiān)視器值:.較快照監(jiān)視器值 快照值 | 無(wú)分布統(tǒng)計(jì)信息 | 有分布統(tǒng)計(jì)信息 |
---|
Rows read | 99,336 | 1,000 | Buffer pool data logical reads | 8,701 | 11 | Buffer pool index logical reads | 165 | 12 | Total execution time (sec.ms) | 0.530903 | 0.014597 |
您可以看到,有分布統(tǒng)計(jì)信息的情況下,DB2 執(zhí)行查詢(xún)時(shí)需要計(jì)算的行數(shù)更少。這對(duì)于 CPU 成本和 I/O 成本都有積極的影響。最重要的是總執(zhí)行成本,因?yàn)榭倛?zhí)行成本關(guān)系到應(yīng)用程序的響應(yīng)時(shí)間。在具有分布統(tǒng)計(jì)信息的情況下,這個(gè)時(shí)間是 0.014597 秒,而在沒(méi)有分布統(tǒng)計(jì)信息的情況下,這個(gè)時(shí)間是 0.530903,相差 36 倍之多。 在我們的示例中,兩種情況下的執(zhí)行時(shí)間分別為 0.014597 秒和 0.530903 秒,這個(gè)差距還不夠明顯,因?yàn)檫@兩個(gè)值只是次秒級(jí)的。然而,這樣的差距不應(yīng)被忽略。如果要執(zhí)行更復(fù)雜的查詢(xún),或者要連續(xù)執(zhí)行多個(gè)查詢(xún),那么執(zhí)行時(shí)間的差距就不是次秒級(jí)的,而是以秒甚至分鐘來(lái)計(jì)算的。
分布統(tǒng)計(jì)信息的生成 如前所述,在使用 RUNSTATS 命令生成統(tǒng)計(jì)信息時(shí),并不是 總會(huì)收集分布統(tǒng)計(jì)信息。這是有意義的,因?yàn)閮H在存在很多重復(fù)值或者數(shù)據(jù)分布不均勻的情況下,分布統(tǒng)計(jì)信息才重要。而在其他情況下,分布統(tǒng)計(jì)信息并不能帶來(lái)多大的好處。 下面的 RUNSTATS 命令只收集 CARS 表(在模式 SAMPLE 中)和相應(yīng)索引的基本統(tǒng)計(jì)信息: RUNSTATS ON TABLE SAMPLE.CARS AND INDEXES ALL
此外,如果需要收集 CARS 表中所有列的分布統(tǒng)計(jì)信息(頻率統(tǒng)計(jì)信息和分位數(shù)統(tǒng)計(jì)信息),那么可以執(zhí)行以下命令: RUNSTATS ON TABLE SAMPLE.CARS WITH DISTRIBUTION AND INDEXES ALL
生成分布統(tǒng)計(jì)信息意味著給 DB2 帶來(lái)額外的、可觀的開(kāi)銷(xiāo),從而影響 RUNSTATS 命令的執(zhí)行時(shí)間。所以,應(yīng)該只為那些需要分布統(tǒng)計(jì)信息的列生成分布統(tǒng)計(jì)信息。 RUNSTATS ON TABLE SAMPLE.CARS WITH DISTRIBUTION ON COLUMNS (TYPE, STATE) AND INDEXES ALL
應(yīng)為滿(mǎn)足以下條件的列收集分布統(tǒng)計(jì)信息: - 該列有很多重復(fù)的值(頻率統(tǒng)計(jì)信息),或者該列的值分布不均勻,即它們?cè)谀承┚植渴蔷酆系模ǚ治粩?shù)統(tǒng)計(jì)信息)。
- 檢查等于或不等于的謂詞中使用到該列(頻率統(tǒng)計(jì)信息),或者檢查小于(小于等于)、大于(大于等于)或介于兩個(gè)值之間的謂詞中使用到該列(分位數(shù)統(tǒng)計(jì)信息)。
對(duì)于頻率統(tǒng)計(jì)信息,重要的是定義好收集多少個(gè)值的重復(fù)數(shù)。如果為一個(gè)列中的所有值收集頻率統(tǒng)計(jì)信息,那么成本就太高了。如果在執(zhí)行 RUNSTATS 時(shí)沒(méi)有顯式定義數(shù)量,那么 DB2 將使用由數(shù)據(jù)庫(kù)參數(shù) NUM_FREQVALUES 提供的默認(rèn)數(shù)量。由于NUM_FREQVALUES 的默認(rèn)值為 10,DB2 將為列中出現(xiàn)最頻率的 10 個(gè)值收集重復(fù)次數(shù),這里假定 RUNSTATS 是在沒(méi)有顯式定義數(shù)量,且數(shù)據(jù)庫(kù)參數(shù) NUM_FREQVALUES 沒(méi)有被修改的情況下執(zhí)行的。 與頻率統(tǒng)計(jì)信息類(lèi)似,也必須為分位數(shù)統(tǒng)計(jì)信息定義一個(gè)數(shù)量,以保證精確性。分位數(shù)統(tǒng)計(jì)信息定義應(yīng)該使用多少 “度量值(measurement)“。列中的值被認(rèn)為是按升序排列的,并且有一個(gè)正則的行間隔,相應(yīng)的值是確定的。使用的度量值越多,優(yōu)化器對(duì)于檢查范圍( 、> 、<> 、>= 、BETWEEN )的謂詞的過(guò)濾因子的估計(jì)就越準(zhǔn)確。如果在執(zhí)行 RUNSTATS 時(shí)沒(méi)有明確指定一個(gè)值,那么 DB2 將使用由數(shù)據(jù)庫(kù)參數(shù) NUM_QUANTILES 提供的默認(rèn)數(shù)量。NUM_QUANTILES 的默認(rèn)值是 20,也就是說(shuō)使用 20 個(gè)度量值。這已經(jīng)是一個(gè)較好的值,因?yàn)樗梢员WC優(yōu)化器在使用分位數(shù)統(tǒng)計(jì)信息的情況下對(duì)確定過(guò)濾因子的估計(jì)誤差最大只有 5%。 如果數(shù)據(jù)庫(kù)配置(DB CFG )不能提供 NUM_FREQVALUES 和 NUM_QUANTILES 的值,那么可以在執(zhí)行 RUNSTATS 時(shí)顯式定義: RUNSTATS ON TABLE SAMPLE.CARS WITH DISTRIBUTION ON COLUMNS (TYPE NUM_FREQVALUES 10 NUM_QUANTILES 20, STATE NUM_FREQVALUES 15 NUM_QUANTILES 30) AND INDEXES ALL
如何檢查是否存在分布統(tǒng)計(jì)信息 為檢查某個(gè)表的分布統(tǒng)計(jì)信息是否已收集,可以查看分類(lèi)視圖 SYSCAT.COLDIST 的內(nèi)容: SELECT * FROM SYSCAT.COLDIST WHERE TABSCHEMA = 'SAMPLE' AND TABNAME = 'CARS'
視圖 SYSCAT.COLDIST 結(jié)構(gòu)如下: 列名 | 數(shù)據(jù)類(lèi)型 | 是否可以為空 | 描述 |
---|
TABSCHEMA | VARCHAR(128) | 不可以 | 本條目對(duì)應(yīng)的表的限定符 | TABNAME | VARCHAR(128) | 不可以 | 本條目對(duì)應(yīng)的表的名稱(chēng) | COLNAME | VARCHAR(128) | 不可以 | 本條目對(duì)應(yīng)的列的名稱(chēng) | TYPE | CHAR(1) | 不可以 | F = Frequency(最大頻率) Q = 分位數(shù)值 | SEQNO | SMALLINT | 不可以 | 如果 TYPE = F,則該列中的 N 表示第 N 頻繁的值 如果 TYPE = Q,那么該列中的 N 表示第 N 個(gè)分位數(shù)值 | COLVALUE | VARCHAR(254) | 可以 | 數(shù)據(jù)值,其形式為字符字面值,或者一個(gè) NULL 值 | VALCOUNT | BIGINT | 不可以 | 如果 TYPE = F,那么 VALCOUNT 是 COLVALUE 出現(xiàn)在該列中的次數(shù) 如果 TYPE = Q,那么 VALCOUNT 是其值小于或等于 COLVALUE 的行的數(shù)量 | DISTCOUNT | BIGINT | 可以 | 如果 TYPE = Q,那么該列記錄小于或等于 COLVALUE 的不同值的數(shù)量(如果沒(méi)有,則為 NULL) |
僅在收集了一個(gè)表中至少一個(gè)列的分布統(tǒng)計(jì)信息時(shí),SYSCAT.COLDIST 才會(huì)包含關(guān)于該表的條目。如果在沒(méi)有 WITH DISTRIBUTION 的情況下再次執(zhí)行 RUNSTATS ,那么 SYSCAT.COLDIST 中與該表對(duì)應(yīng)的條目將被刪除。
分布統(tǒng)計(jì)信息的參數(shù)標(biāo)記/主變量 JDBC 提供了兩種途徑來(lái)執(zhí)行動(dòng)態(tài) SQL,因而也提供了兩種不同的接口: java.sql.Statement java.sql.PreparedStatement
PreparedStatement 是 Statement 的子接口,它允許使用參數(shù)標(biāo)記(= 占位符;在其他編程語(yǔ)言中,此類(lèi)占位符也被稱(chēng)為主變量) —— 而不是 Statement 。在使用 PreparedStatement 的情況下,首先編譯要執(zhí)行的包括參數(shù)標(biāo)記的 SQL 語(yǔ)句,然后將值綁定到參數(shù)標(biāo)記,最后執(zhí)行 SQL 語(yǔ)句。
下面的代碼片段顯示使用 Statement 與使用 PreparedStatement 的不同之處。 java.sql.Connection con = ...;java.sql.Statement stmt1 = con.createStatement();String insert1 = 'INSERT INTO TABLE_X (COL_Y) VALUES ('ABC')';stmt1.executeUpdate(insert1);java.sql.Statement stmt2 = con.createStatement();String insert2 = 'INSERT INTO TABLE_X (COL_Y) VALUES ('XYZ')';stmt2.executeUpdate(insert2);con.commit(); |
java.sql.Connection con = ...;String insert = 'INSERT INTO TABLE_X (COL_Y) VALUES (?)';java.sql.PreparedStatement pstmt = con.prepareStatement(insert);pstmt.setString(1, 'ABC');pstmt.executeUpdate();pstmt.setString(1, 'XYZ');pstmt.executeUpdate();con.commit(); |
如果一條簡(jiǎn)單 SQL 語(yǔ)句執(zhí)行多次(例如示例中的 INSERT 語(yǔ)句),那么使用 PreparedStatement 有優(yōu)勢(shì),因?yàn)閿?shù)據(jù)庫(kù)只需編譯該語(yǔ)句一次,即可多次執(zhí)行,而不需要重復(fù)編譯。假設(shè)在示例中需要插入數(shù)千行記錄,那么使用 PreparedStatement 可以交付更短的執(zhí)行時(shí)間,因?yàn)橹恍枰淮螠?zhǔn)備/編譯時(shí)間,而不需要數(shù)千次。 然而,Java 開(kāi)發(fā)人員使用 PreparedStatement 往往是因?yàn)樾枰谶\(yùn)行時(shí)提供值/過(guò)濾器標(biāo)準(zhǔn)時(shí),使用這種接口編寫(xiě)的代碼更為優(yōu)雅。請(qǐng)看如下代碼片段: int state = 100;String type = 'A';...java.sql.Connection con = ...;java.sql.Statement stmt = con.createStatement();String select = 'SELECT * FROM CARS WHERE STATE < '="" +="" state="" +="" '="" and="" type='' +="" type="" +="" ''';java.sql.resultset="" rs="stmt.executeQuery(select);while" (rs.next())="" {=""> |
int state = 100;String type = 'A';...java.sql.Connection con = ...;String select = 'SELECT * FROM CARS WHERE STATE < and="" type="?';java.sql.PreparedStatement" pstmt="con.prepareStatement(select);pstmt.setInt(1," state);pstmt.setstring(2,="" type);java.sql.resultset="" rs="pstmt.executeQuery();while" (rs.next())="" {=""> |
使用 PreparedStatement 的那種代碼片段在編碼方面更為優(yōu)雅,因?yàn)樵趯?nbsp;STATE 和 TYPE 的值置入 SQL 語(yǔ)句時(shí),不需要進(jìn)行字符串運(yùn)算。但這種方法存在一個(gè)缺點(diǎn),在綁定 WHERE 子句中謂詞的值之前,需要編譯 SELECT (創(chuàng)建訪問(wèn)計(jì)劃)。為使優(yōu)化器能夠使用可用的分布統(tǒng)計(jì)信息,帶有具體值的謂詞極為重要。 讓我們?cè)俅慰纯?CARS 表的查詢(xún)示例,但這次使用參數(shù)標(biāo)記,而不是具體值: SELECT * FROM CARS WHERE STATE < and="" type="">
CARS 表的分布統(tǒng)計(jì)信息仍然可用,因?yàn)樵诖似陂g這些統(tǒng)計(jì)信息也已經(jīng)被收集。然而,使用參數(shù)標(biāo)記時(shí),將生成另一個(gè)訪問(wèn)計(jì)劃。
雖然這個(gè)訪問(wèn)計(jì)劃比使用具體值但沒(méi)有分布統(tǒng)計(jì)信息情況下的訪問(wèn)計(jì)劃好,但不如既使用具體值又有分布統(tǒng)計(jì)信息時(shí)的訪問(wèn)計(jì)劃。在下表 中,動(dòng)態(tài) SQL 快照中的監(jiān)視器輸出也顯示了這一點(diǎn): Number of executions = 1Number of compilations = 1Worst preparation time (ms) = 10Best preparation time (ms) = 10Internal rows deleted = 0Internal rows inserted = 0Rows read = 99Internal rows updated = 0Rows written = 0Statement sorts = 1Statement sort overflows = 0Total sort time = 0Buffer pool data logical reads = 11Buffer pool data physical reads = 10Buffer pool temporary data logical reads = 0Buffer pool temporary data physical reads = 0Buffer pool index logical reads = 268Buffer pool index physical reads = 125Buffer pool temporary index logical reads = 0Buffer pool temporary index physical reads = 0Total execution time (sec.ms) = 0.081908Total user cpu time (sec.ms) = 0.060086Total system cpu time (sec.ms) = 0.010014Statement text = SELECT ID, TYPE, STATE FROM SAMPLE.CARS WHERE STATE < and="" type=""> |
將這些值與之前收集到的值相比較,可以得到下表: 快照值 | 沒(méi)有分布統(tǒng)計(jì)信息 | 有分布統(tǒng)計(jì)信息 | 既有分布統(tǒng)計(jì)信息又有參數(shù)標(biāo)記 |
---|
Rows read | 99,336 | 1,000 | 99 | Buffer pool data logical reads | 8,701 | 11 | 11 | Buffer pool index logical reads | 165 | 12 | 268 | Total execution time (sec.ms) | 0.530903 | 0.014597 | 0.081908 |
在有參數(shù)標(biāo)記時(shí)的 “Rows read” 值最小,但實(shí)際上這并不重要,因?yàn)榇藭r(shí) “Buffer pool index logical reads” 值較高。重要的是,與既有分布統(tǒng)計(jì)信息又有具體值的情況相比,“Total execution time” 更差一些,在本例中,其差距達(dá)到 8 倍。如前所述,在本例中,我們觀察的是對(duì)少量數(shù)據(jù)執(zhí)行的一個(gè)非常簡(jiǎn)單的查詢(xún)。因此,執(zhí)行時(shí)間上的差距并不是很明顯。但是,如果要執(zhí)行更復(fù)雜的查詢(xún),其執(zhí)行時(shí)間以秒/分鐘來(lái)計(jì)算,那么這個(gè)差距就會(huì)非常明顯。 這個(gè)示例表明,如果將分布統(tǒng)計(jì)信息與參數(shù)標(biāo)記一起使用,分布統(tǒng)計(jì)信息的作用就會(huì)受到限制。如果存在很多重復(fù)/不均勻的數(shù)據(jù)分布和復(fù)雜的查詢(xún),這可能會(huì)導(dǎo)致執(zhí)行時(shí)間高于未使用參數(shù)標(biāo)記情況下的執(zhí)行時(shí)間。這并不意味著使用 PreparedStatement 總是很糟糕的。相反,如果要反復(fù)執(zhí)行多次一條簡(jiǎn)單的語(yǔ)句,并且每次使用不同的值執(zhí)行,建議使用 PreparedStatement ,因?yàn)檎Z(yǔ)句只需編譯一次。在處理復(fù)雜的查詢(xún)時(shí),由于這些查詢(xún)常常要向系統(tǒng)和 DSS(決策支持系統(tǒng))報(bào)告,因此使用參數(shù)標(biāo)記很可能得不償失。這是因?yàn)榉植冀y(tǒng)計(jì)信息得不到充分的利用,編譯時(shí)間只應(yīng)用了一次,因?yàn)檫@些查詢(xún)最多也只是執(zhí)行一次。
結(jié)束語(yǔ) 在本文中,您看到了 DB2 優(yōu)化器對(duì)分布統(tǒng)計(jì)信息的使用。如果一個(gè)數(shù)據(jù)庫(kù)包含很多重復(fù)的值,或者數(shù)據(jù)分布不均勻,那么,如果除了基本統(tǒng)計(jì)信息外,另外還生成了分布統(tǒng)計(jì)信息,DB2 優(yōu)化器就可為 SQL 語(yǔ)句產(chǎn)生更好的訪問(wèn)計(jì)劃。本文解釋了不同類(lèi)型的分布統(tǒng)計(jì)信息,展示如何使用 RUNSTATS 命令生成這些統(tǒng)計(jì)信息,并提供了一個(gè)示例場(chǎng)景,說(shuō)明分布統(tǒng)計(jì)信息在某些情況下的重要性。您還研究了參數(shù)標(biāo)記/主變量的使用會(huì)為 DB2 優(yōu)化器對(duì)分布統(tǒng)計(jì)信息的考慮帶來(lái)怎樣的限制。
下載 描述 | 名字 | 大小 | 下載方法 |
---|
DDL for sample table CARS | create_table_cars.zip | 1KB | HTTP |
轉(zhuǎn)自IBM官方文檔庫(kù):http://www.ibm.com/developerworks/cn/data/library/techarticles/dm-0606fechner/ 最后貼個(gè)英文版地址: http://www.ibm.com/developerworks/data/library/techarticle/dm-0606fechner/index.html?S_TACT=105AGX52&S_CMP=cn-a-db2
|