從Oracle索引的Clustering Factor看PG的Correlation
十多年前我為某企業(yè)的集采招標(biāo)組織了一次PC SERVER的基準(zhǔn)測(cè)試,參測(cè)的包括IBM、HP、華為、曙光、浪潮等。實(shí)際上我們對(duì)各廠商提出的配置要求是一致的,使用的CPU,磁盤,內(nèi)存都差不多。雖然各個(gè)廠商調(diào)教產(chǎn)品的水平不同會(huì)導(dǎo)致一些差異。因此對(duì)于大多數(shù)性能測(cè)試用例來說測(cè)試成績(jī)應(yīng)該差不多,在功耗和耐力測(cè)試上才能看出差距來。不過實(shí)際測(cè)試時(shí),IBM在性能測(cè)試上的分?jǐn)?shù)就比其他廠商高出很多。
這讓我十分疑惑,檢查了多次也沒有發(fā)現(xiàn)IBM有作弊的情況。我們的檢查工具會(huì)對(duì)數(shù)據(jù)做嚴(yán)格的檢查,一旦出現(xiàn)篡改測(cè)試數(shù)據(jù)等情況肯定是能發(fā)現(xiàn)的。就在我百思不得其解的時(shí)候,我看到IBM的測(cè)試區(qū)的桌上放著一本我寫的《ORACLE 優(yōu)化日記》,其中書簽頁(yè)放在Clustering Factor相關(guān)的案例上。于是我眼前一亮,讓同事對(duì)幾張大表中的數(shù)據(jù)順序做了檢查。其中兩張?jiān)跍y(cè)試中大量做范圍掃描的表確實(shí)被他們重新做了排序。我們以前的檢查工具只檢查是否有數(shù)據(jù)被刪除,并沒有檢查數(shù)據(jù)的順序,因此就沒有發(fā)現(xiàn)這個(gè)作弊的行為。說實(shí)在的前些年做基準(zhǔn)測(cè)試,IBM的測(cè)試團(tuán)隊(duì)的技術(shù)水平還是很高的,他們總是能夠在不違反測(cè)試規(guī)則的情況下找到測(cè)試用例的漏洞。他們靠的是自身的優(yōu)化能力來利用測(cè)試漏洞,這比某些靠野蠻作弊的廠商要高出不少。
我介紹的這個(gè)例子中有個(gè)概念索引的“Clustering Factor”,集簇因子。什么是集群因子(Clustering Factor)呢?集群因子反映的是如果通過一個(gè)索引范圍掃描一張表,需要訪問的表數(shù)據(jù)塊的數(shù)量。集群因子的計(jì)算方法如下:
(1) 掃描一個(gè)索引;
(2) 比較某行的ROWID和前一行的ROWID,如果這兩個(gè)ROWID不屬于同一個(gè)數(shù)據(jù)塊,那么集群因子增加1;
(3) 整個(gè)索引掃描完畢后,就得到了該索引的集群因子。
從上面集簇因子的計(jì)算方式我們可以看出,集簇因子反映了索引范圍掃描可能帶來的對(duì)整個(gè)表訪問過程的開銷情況,特別是IO開銷。實(shí)際上哪怕所有的塊都在DB CACHE里,如果這些行存在于少數(shù)數(shù)據(jù)塊中,掃描操作依然可以因?yàn)楦俚拈V鎖訪問而變得更快。
如果集簇因子接近于表存儲(chǔ)的塊數(shù),說明這張表是按照索引字段的順序存儲(chǔ)的。如果集群因子接近于行的數(shù)量,那說明這張表不是按索引字段順序存儲(chǔ)的。在計(jì)算索引掃描的訪問成本時(shí),集群因子十分有用。集群因子乘以選擇性參數(shù)就是訪問索引的開銷。如果這個(gè)統(tǒng)計(jì)數(shù)據(jù)不能反映出索引的真實(shí)情況,那么可能會(huì)造成優(yōu)化器錯(cuò)誤選擇執(zhí)行計(jì)劃,全表掃描、索引范圍掃描還是快速索引掃描。另外,如果某張表上的大多數(shù)訪問是按照某個(gè)索引做索引掃描,那么將該表的數(shù)據(jù)按照索引字段的順序重新組織,可以提高該表的訪問性能。
PostgreSQL數(shù)據(jù)庫(kù)的索引沒有Oracle Clustering Factor的概念,那么PostgreSQL是根據(jù)什么來判斷索引掃描的效率的呢?在pg_stats視圖里,有一個(gè)字段:correlation(列數(shù)據(jù)相關(guān)性)。PostgreSQL在做表的Analyze的時(shí)候,也會(huì)分析字段的順序,通過correlation來告知DBA某個(gè)字段的物理存儲(chǔ)順序和邏輯順序之間的相關(guān)性是多少。
列的相關(guān)性是介于 -1 和 1 之間的值,代表了這個(gè)字段邏輯順序和物理順序之間的匹配程度。如果相關(guān)性為 1,則表中數(shù)據(jù)行的物理存儲(chǔ)是按升序存儲(chǔ)在表文件中的;如果為 -1,則按降序存儲(chǔ)。越接近-1或者+1,說明數(shù)據(jù)越有序。值為 0 表示物理順序和邏輯順序之間沒有聯(lián)系。
大家通過Oracle 的CF和PG的Correlation在算法上的差異可以看出,Oracle的CF能夠更為精準(zhǔn)地反映出索引掃描的效率,PG有Correlation,也可以部分解決問題。下面我們通過一個(gè)例子來進(jìn)一步說明。
圖片
我們來做個(gè)試驗(yàn),首先創(chuàng)建兩張測(cè)試表,都是從一個(gè)統(tǒng)一的基表數(shù)據(jù)來創(chuàng)建,只不過一個(gè)是按照ID排序的,一個(gè)是隨機(jī)選取的。
圖片
我們來看看這兩張表的ID字段的correlation,查看pg_stats之前一定要做一次analyze,否則是看不到數(shù)據(jù)的。
圖片
接下來我們對(duì)這兩張表做一次范圍掃描,看看效率有何不同。如上圖,我們看到按照ID排序的表的訪問效率要高不少。對(duì)于實(shí)際生產(chǎn)環(huán)境,如果數(shù)據(jù)塊不在內(nèi)存里,這個(gè)差異還會(huì)放大,因?yàn)镮O的成本更高。
從上面的案例我們可以學(xué)到些什么呢?首先我們可以學(xué)到一個(gè)優(yōu)化的小技巧。對(duì)于經(jīng)常會(huì)使用范圍掃描來訪問數(shù)據(jù)的表,如果大多數(shù)范圍掃描都是基于某個(gè)索引,那么將表數(shù)據(jù)針對(duì)索引順序進(jìn)行重新排序,會(huì)大大提升掃描的效率。十多年前我們經(jīng)常使用這個(gè)方法來優(yōu)化用戶的系統(tǒng)。如果表的數(shù)據(jù)變化不是特別大,那么這種優(yōu)化一年做一次就可以管用好久,如果是分區(qū)表的話,只需要對(duì)部分分區(qū)做就行了。這個(gè)方法對(duì)于絕大多數(shù)HEAP結(jié)構(gòu)存儲(chǔ)數(shù)據(jù)的數(shù)據(jù)庫(kù)是有效的,對(duì)于B+樹或者LSM-TREE存儲(chǔ)結(jié)構(gòu)的數(shù)據(jù)庫(kù)沒什么用處。因此對(duì)于Oracle、PG,這個(gè)小技巧是有用的。
對(duì)于PostgreSQL的用戶和DBA來說,我們也可以學(xué)會(huì)通過Correlation字段去分析某個(gè)字段的數(shù)據(jù)關(guān)聯(lián)性,從而分析一些范圍掃描的性能問題。
對(duì)于國(guó)產(chǎn)數(shù)據(jù)庫(kù)廠商來說,我想Oracle的Clustering Factor肯定比PG的Correlation更加精準(zhǔn),如果在做索引分析的時(shí)候,采集類似Oracle 集簇因子的屬性,對(duì)于CBO生成更為精準(zhǔn)的執(zhí)行計(jì)劃是有益的。
今天就寫這么多,希望我今天介紹的這個(gè)小知識(shí)點(diǎn),能夠?qū)Υ蠹矣兴鶐椭?,有所啟發(fā)。