PostgreSQL中的索引—9(BRIN)
在之前的文章中,我們討論了PostgreSQL索引引擎、訪問方法的接口,以及以下方法:哈希索引、B樹、GiST、SP-GiST、GIN和RUM。本文的主題是BRIN索引。
BRIN
一般概念
與我們已經熟悉的索引不同,BRIN的想法是避免查看絕對不合適的行,而不是快速找到匹配的行。BRIN是一個不準確的索引:它根本不包含表行的TID。
簡單地說,BRIN適用于值與其在表中的物理位置相關的列。換句話說,適用于沒有ORDER BY子句的查詢,但卻以遞增或遞減的順序返回列值(并且該列上沒有索引)的數據。
這種訪問方法是在歐洲大型分析數據庫項目Axex的范圍內創建的,著眼于幾TB或幾十TB大的表。BRIN的一個重要特性使我們能夠在這樣的表上創建索引,那就是它的小尺寸和最小的維護開銷。
其工作原理如下。該表被拆分為多個頁面大的范圍(或多個塊大的,這是相同的)(因此得名:塊范圍索引,Block Range Index,BRIN)。索引存儲每個范圍內數據的摘要信息。通常,這是最小值和最大值,但恰好不同(一個是最大值與最小值不同,一個是這個區間的最大值與最小值包含的范圍與其他區間包含的范圍不會完全重合),后面會說。假設執行了一個包含列條件的查詢,如果搜索的值沒有進入區間,則可以跳過整個范圍,但如果它們在這個區間,那么所有塊中的所有行都必須被檢查一遍,以便從中選擇匹配的行。
與其將BRIN視為索引,不如將其視為順序掃描的加速器。如果我們將每個范圍視為“虛擬”分區,我們可以將BRIN視為分區的替代。
現在讓我們更詳細地討論索引的結構。
結構
第一個(更準確地說是第0個)頁面包含元數據。
包含摘要信息的頁面位于元數據的某個偏移位置。這些頁面上的每個索引行都包含一個范圍的摘要信息。
在元頁面和摘要數據之間,有反向范圍映射(縮寫為“revmap”)的頁面被定位。實際上,這是指向相應索引行的指針數組(TID)(這里應該理解為指向索引某一頁的某一元組的指針,與指向數據頁的某一元組的指針是一樣,即TID)。
?對于某些范圍,“revmap”中的指針可能導致沒有指向索引行(圖中有一個指針用灰色標記)。在這種情況下,有范圍被認為還沒有摘要信息。
索引掃描
如果索引不包含對表行的引用,如何使用它?這種訪問方法當然不能逐個TID返回行(上一段說的TID是指向索引行的指針,而不是指向數據行的指針。該索引只能用于查看一個值是否可能在表中,需要進一步順序掃描,而不能直接返回目標值。注意逐個TID返回行只是按物理順序返回,不是按數值排序返回),但它可以構建位圖。位圖頁面可以有兩種:精確到行和不精確到頁面。使用的位圖不準確。
算法很簡單。按順序掃描范圍圖(也就是說,范圍按其在表中的位置順序掃描)。指針用于確定索引行,其中包含每個范圍的摘要信息(也就是先知道范圍,再確定該范圍對應的指針,指針其實和范圍是一致的順序,再根據指針指向的索引行知道該范圍的摘要信息)。如果某個范圍不包含所搜索的值,則跳過該范圍,如果該范圍可能包含該值(或摘要信息不可用),則該范圍的所有頁面都將添加到位圖中。然后像往常一樣使用生成的位圖。
更新索引
更有趣的是,當表發生變化時,索引是如何更新的。
當向表頁面添加一行的新版本時,我們確定它包含在哪個范圍內,并使用范圍映射來查找包含摘要信息的索引行。所有這些都是簡單的算術運算。例如,假設一個范圍的大小為4,在第13頁出現了一個值為42的行版本。范圍數(從零開始)為13/4=3,因此,在“revmap”中,我們取偏移量為3的指針(其順序號為4)。
這個范圍的最小值是31,最大值是40。由于新值42超出了區間,我們更新了最大值(見圖)(所以范圍會有一些重合)。但如果新值仍在存儲的限制范圍內,則不需要更新索引。
所有這些都與頁面的新數據是否出現在摘要信息指定的范圍內有關。創建索引時,會計算所有可用范圍的摘要信息,但當數據表進一步擴展時,可能會出現超出限制的新頁面(就是數據頁面增加,舊范圍包不下去)。這里有兩個選擇:
當出現新范圍時,“revmap”的大小可能會增加。每當位于元頁面和摘要數據之間的映射需要擴展另一個頁面時,現有的行版本就會移動到其他頁面。因此,范圍圖總是位于元數據頁和摘要數據之間。
當一行被刪除時。。。什么都沒發生。我們可以注意到,有時會刪除最小值或最大值,在這種情況下,間隔可以縮短。但要檢測到這一點,我們必須讀取范圍內的所有值,這是非常昂貴的。
索引的正確性不受影響,但搜索可能需要查看比實際需要更多的范圍(檢索時認為它可能出現在多個范圍中,而實際范圍比標的范圍更小,所以其實只落在更少的范圍里)。通常,可以手動重新計算此類范圍的摘要信息(通過調用“brin_desummarize_range”和“brin_summarize_new_values”函數),但我們如何檢測這種需求?無論如何,沒有常規的程序可用于此目的。(這是一個很大的缺陷)
最后,更新一行只是刪除過時的版本并添加一個新版本。
示例
讓我們嘗試為演示數據庫表中的數據構建我們自己的迷你數據庫。讓我們假設,為了BI報告的目的,需要一個非規范化的表格來反映從機場起飛或降落在機場的航班,精確到機艙中的座位。每個機場的數據將在每天的午夜添加到表中一次。數據既不會更新也不會刪除。
該表如下所示:
demo=# create table flights_bi(airport_code char(3),airport_coord point, -- geo coordinates of airportairport_utc_offset interval, -- time zoneflight_no char(6), -- flight numberflight_type text. -- flight type: departure / arrival scheduled_time timestamptz, -- scheduled departure/arrival time of flightactual_time timestamptz, -- actual time of flightaircraft_code char(3),seat_no varchar(4), -- seat numberfare_conditions varchar(10), -- travel classpassenger_id varchar(20),passenger_name text );我們可以模擬使用嵌套循環加載數據的過程:外部為逐天循環(我們將考慮一個大數據庫,因此循環365天),內部逐時區循環(從UTC + 02到UTC + 12)。這個查詢很長,沒有什么特別的意思,所以此處不展示代碼。
demo=# select count(*) from flights_bi;count ----------30517076 (1 row) demo=# select pg_size_pretty(pg_total_relation_size('flights_bi'));pg_size_pretty ----------------4127 MB (1 row)我們查詢到3000萬行和4GB。雖然尺寸不太大,但對于一臺筆記本電腦來說足夠了:順序掃描花了我大約10秒鐘。
我們應該在哪些列上創建索引?
由于BRIN索引的大小較小,開銷適中,而且更新也不太頻繁(如果有的話),因此出現了一個難得的機會,可以在所有字段上構建許多索引“以防萬一”,例如,分析師用戶可以在這些字段上創建臨時查詢。(有些列的索引)不會有用也沒關系,但即使是效率不高的索引也肯定比順序掃描更有效。當然,有些領域建立索引是絕對無用的;純粹的常識會促使他們這樣做。
但是,將我們自己局限于這條建議會很奇怪,因此,讓我們嘗試陳述一個更準確的標準。
我們已經提到,數據必須與其物理位置有一定的關聯。這里有必要記住,PostgreSQL收集表列統計信息,其中包括相關值。planner使用該值在常規索引掃描和位圖掃描之間進行選擇,我們可以使用它來估計BRIN索引的適用性。
在上面的例子中,數據顯然是按天排序的(按“計劃時間”和“實際時間”——沒有太大區別)。這是因為當向表中添加行時(不會刪除和更新),它們會在文件中一個接一個地排列。在數據加載的模擬中,我們甚至沒有使用ORDER BY子句,因此,一天內的日期通常可以以任意方式混合,但必須進行排序。讓我們檢查一下:
demo=# analyze flights_bi; demo=# select attname, correlation from pg_stats where tablename='flights_bi' order by correlation desc nulls last;attname | correlation --------------------+-------------scheduled_time | 0.999994actual_time | 0.999994fare_conditions | 0.796719flight_type | 0.495937airport_utc_offset | 0.438443aircraft_code | 0.172262airport_code | 0.0543143flight_no | 0.0121366seat_no | 0.00568042passenger_name | 0.0046387passenger_id | -0.00281272airport_coord | (12 rows)不太接近零的值(理想情況下,接近±1,如本例所示)表示BRIN索引是合適的。
旅行艙“fare_condition”(該列包含三個可選項)和航班類型“flight_type”(兩個可選項)意外地出現在第二和第三位。這是一種錯覺:從表面上看,相關性很高,而實際上,在連續的幾頁中,所有可能的值都肯定會遇到,這意味著使用BRIN不會有任何好處。
接下來是時區“airport_utc_offset”:在所考慮的示例中,在一天的周期內,航班按時區排序(就是按結構)。
我們將進一步試驗這兩個領域,時間和時區。
相關性可能減弱
當數據發生變化時,“按結構”放置的相關性很容易被削弱。這里的問題不在于對某個特定值的更改,而在于多版本并發控制的結構:過時的行版本在一個頁面上被刪除,但新版本可以在任何可用空間插入。因此,在更新過程中,所有行都會混淆。
我們可以通過減少“fillfactor”存儲參數的值來部分控制這種效果,并通過這種方式在頁面上留出可用空間,以便將來進行更新。但是我們并不想增加一張已經很大的表的尺寸。此外,這并不能解決刪除問題:它們還通過釋放現有頁面中的某個位置來為新行“設置空位”。因此,本來會放置在文件末尾的行(就是放置在預留空間的行)將被插入到任意位置。
順便說一句,這是一個奇怪的事實。因為BRIN索引不包含對表行的引用,所以它的可用性不應該妨礙熱更新,但它確實會。
因此,BRIN主要是為大型甚至超大型的表格設計的,這些表格要么根本沒有更新,要么更新得非常輕微(比如百度貼吧之類的不準挖墳)。但是,它可以完美地處理添加新行(到表的末尾)的問題。這并不奇怪,因為創建這種訪問方法是為了數據倉庫和分析報告。
選擇多大的范圍合適?
如果我們處理1TB的表,在選擇范圍大小時,我們主要關心的可能是不要使BRIN索引太大。然而,在上述的例子中,我們可以更準確地分析數據。
為此,我們可以選擇列的某一值,并查看它們出現在多少頁上。這些值的分布增加了應用BRIN索引的成功幾率。此外,找到的頁數將提示范圍的大小。但是,如果該值“分散”在所有頁面上,那么BRIN是無用的。
當然,我們應該使用這種技術,密切關注數據的內部結構。例如,考慮每個日期(更確切地說,時間戳,也包括時間)作為一個唯一的值是沒有意義的-我們需要設置幾天作為一個循環。
從技術上講,這種分析可以通過查看隱藏的“ctid”列的值來完成,該列提供指向行版本(TID)的指針:頁面編號和頁面內的行編號。不幸的是,沒有常規技術將TID分解為兩個組件(即分解為頁面編號與行編號),因此,我們必須通過文本表示轉換類型:
demo=# select min(numblk), round(avg(numblk)) avg, max(numblk) from ( select count(distinct (ctid::text::point)[0]) numblkfrom flights_bigroup by scheduled_time::date ) t;min | avg | max ------+------+------1192 | 1500 | 1796 (1 row)demo=# select relpages from pg_class where relname = 'flights_bi';relpages ----------528172 (1 row)(第一個查詢的代碼計算了每天的數據涉及的頁的數量的最大值、最小值與平均值)
我們可以看到,每一天在頁面上的分布相當均勻,而天數之間的分布略有混淆(就是某些頁包含兩天及以上的數據)(1500×365=547500,僅略大于表528172中的頁數)。不管怎樣,這一點實際上是明確的。
這里有價值的信息是特定頁數。傳統的范圍大小為128頁,每天將填充9-14個范圍(1192/128~1796/128)。這似乎是符合實際的:對于特定日期的查詢,我們可以預期大約10%的錯誤。
讓我們試試:
demo=# create index on flights_bi using brin(scheduled_time);索引的大小小到184KB:
demo=# select pg_size_pretty(pg_total_relation_size('flights_bi_scheduled_time_idx'));pg_size_pretty ----------------184 kB (1 row)在這種情況下,以失去精度為代價增加范圍的尺寸幾乎沒有意義(如果增加范圍的大小,那么整個索引會更小,但是這里已經很小了)。但是如果需要的話,我們可以減少范圍的大小,來增加準確度(索引大小也會增加)。
現在讓我們看看時區。在這里,我們也不能使用暴力手段。所有數值應除以日周期數,因為該分布在每天內重復。此外,由于時區很少,我們可以查看整個分布:
demo=# select airport_utc_offset, count(distinct (ctid::text::point)[0])/365 numblk from flights_bi group by airport_utc_offset order by 2;airport_utc_offset | numblk --------------------+--------12:00:00 | 606:00:00 | 802:00:00 | 1011:00:00 | 1308:00:00 | 2809:00:00 | 2910:00:00 | 4004:00:00 | 4707:00:00 | 11005:00:00 | 23103:00:00 | 932 (11 rows)平均而言,每個時區的數據每天有133頁,但分布非常不均勻:彼得羅巴甫洛夫斯克-堪察茨基和阿納代爾的數據只有6頁,而莫斯科及其周邊地區需要數百頁(就是每天飛到不同時區的飛機數隨時區變化很大,但是起飛的飛機數每天分布很均勻)。在這里,范圍的默認大小不好;例如,讓我們將其設置為四頁。
demo=# create index on flights_bi using brin(airport_utc_offset) with (pages_per_range=4); demo=# select pg_size_pretty(pg_total_relation_size('flights_bi_airport_utc_offset_idx'));pg_size_pretty ----------------6528 kB (1 row)執行計劃
讓我們看看我們的索引是如何工作的。讓我們選擇一周前的某一天(在演示數據庫中,“今天”由“booking.now”功能決定):
demo=# \set d 'bookings.now()::date - interval \'7 days\'' demo=# explain (costs off,analyze)select *from flights_biwhere scheduled_time >= :d and scheduled_time < :d + interval '1 day';QUERY PLAN --------------------------------------------------------------------------------Bitmap Heap Scan on flights_bi (actual time=10.282..94.328 rows=83954 loops=1)Recheck Cond: ...Rows Removed by Index Recheck: 12045Heap Blocks: lossy=1664-> Bitmap Index Scan on flights_bi_scheduled_time_idx(actual time=3.013..3.013 rows=16640 loops=1)Index Cond: ...Planning time: 0.375 msExecution time: 97.805 ms正如我們所見,規劃器使用了創建的索引。它有多準確?符合查詢條件的行數(“Bitmap Heap Scan ”節點的行)與使用索引返回的行總數的比率(相同的值加上Rows Removed by Index Recheck的行)告訴我們這一點。在本例中為83954/(83954+12045),約為預期的90%(該值會隨目標日期變化)。
位圖索引掃描節點的“實際行”中的數字16640來自哪里?問題是,計劃的這個節點構建了一個不準確的(逐頁)位圖,并且完全不知道位圖將接觸多少行,同時需要顯示一些內容。因此,在絕望中,假設一頁包含10行。位圖總共包含1664頁(該值顯示在“Heap Blocks:lossy=1664”中);所以,我們只得到16640。總而言之,這是一個毫無意義的數字,我們不應該注意。
使用航班構建的索引表現如何呢?例如,讓我們以符拉迪沃斯托克的時區為例,該時區每天有28頁:
demo=# explain (costs off,analyze)select *from flights_biwhere airport_utc_offset = interval '8 hours';QUERY PLAN ----------------------------------------------------------------------------------Bitmap Heap Scan on flights_bi (actual time=75.151..192.210 rows=587353 loops=1)Recheck Cond: (airport_utc_offset = '08:00:00'::interval)Rows Removed by Index Recheck: 191318Heap Blocks: lossy=13380-> Bitmap Index Scan on flights_bi_airport_utc_offset_idx(actual time=74.999..74.999 rows=133800 loops=1)Index Cond: (airport_utc_offset = '08:00:00'::interval)Planning time: 0.168 msExecution time: 212.278 ms規劃器再次使用創建的BRIN索引。準確度更差(在這種情況下約為75%),但這是意料之中的,因為(數據與物理排序)相關性較低。
幾個BRIN索引(就像任何其他索引一樣)當然可以在位圖級別聯合(就是多索引加范圍查詢)。例如,以下是所選時區一個月的數據(請注意“BitmapAnd”節點):
demo=# \set d 'bookings.now()::date - interval \'60 days\'' demo=# explain (costs off,analyze)select *from flights_biwhere scheduled_time >= :d and scheduled_time < :d + interval '30 days'and airport_utc_offset = interval '8 hours';QUERY PLAN ---------------------------------------------------------------------------------Bitmap Heap Scan on flights_bi (actual time=62.046..113.849 rows=48154 loops=1)Recheck Cond: ...Rows Removed by Index Recheck: 18856Heap Blocks: lossy=1152-> BitmapAnd (actual time=61.777..61.777 rows=0 loops=1)-> Bitmap Index Scan on flights_bi_scheduled_time_idx(actual time=5.490..5.490 rows=435200 loops=1)Index Cond: ...-> Bitmap Index Scan on flights_bi_airport_utc_offset_idx(actual time=55.068..55.068 rows=133800 loops=1)Index Cond: ...Planning time: 0.408 msExecution time: 115.475 ms與B-樹的比較
如果我們在與BRIN相同的字段上創建常規的B樹索引呢?
demo=# create index flights_bi_scheduled_time_btree on flights_bi(scheduled_time); demo=# select pg_size_pretty(pg_total_relation_size('flights_bi_scheduled_time_btree'));pg_size_pretty ----------------654 MB (1 row)它似乎比我們的BRIN大幾千倍!但是,查詢的執行速度要快一點:planner使用統計數據來確定數據是按物理順序排列的,不需要構建位圖,主要是不需要重新檢查索引條件:
demo=# explain (costs off,analyze)select *from flights_biwhere scheduled_time >= :d and scheduled_time < :d + interval '1 day';QUERY PLAN ----------------------------------------------------------------Index Scan using flights_bi_scheduled_time_btree on flights_bi(actual time=0.099..79.416 rows=83954 loops=1)Index Cond: ...Planning time: 0.500 msExecution time: 85.044 ms這就是BRIN的美妙之處:我們犧牲了效率,卻獲得了很大的空間。
操作符類
極小極大
對于其值可以相互比較的數據類型,摘要信息由最小值和最大值組成。相應運算符類的名稱包含“minmax”,例如“date_minmax_ops”。實際上,這些都是我們目前正在考慮的數據類型,大多數類型都是這種類型。
包含
并非所有數據類型都定義了比較運算符。例如,它們沒有為代表機場地理坐標的點(“點”類型)定義。順便說一句,正是因為這個原因,統計數據沒有顯示這一列的相關性。
demo=# select attname, correlation from pg_stats where tablename='flights_bi' and attname = 'airport_coord';attname | correlation ---------------+-------------airport_coord | (1 row)但許多這樣的類型使我們能夠引入“矩形框”的概念,例如,幾何形狀的矩形框。我們詳細討論了GiST索引如何使用此功能。類似地,BRIN還支持收集具有這些數據類型的列的摘要信息:范圍內所有值的邊界區域就是摘要值。
與GiST不同,BRIN的摘要值必須與被索引的值的類型相同。因此,我們無法建立點的索引,盡管很明顯,坐標可以在BRIN中工作:經度與時區密切相關(就是經緯度可以,但是點不行,因為經緯度可以與時區相關,然后將BRIN建在時區上)。幸運的是,在將點轉換為退化矩形后,沒有什么可以阻止在表達式上創建索引。同時,我們將范圍的大小設置為一頁,以顯示限制情況:
demo=# create index on flights_bi using brin (box(airport_coord)) with (pages_per_range=1);即使在這種極端情況下,索引的大小也只有30 MB:
demo=# select pg_size_pretty(pg_total_relation_size('flights_bi_box_idx'));pg_size_pretty ----------------30 MB (1 row)現在我們可以通過坐標限制機場進行查詢。例如:
demo=# select airport_code, airport_name from airports where box(coordinates) <@ box '120,40,140,50';airport_code | airport_name --------------+-----------------KHV | Khabarovsk-NovyiVVO | Vladivostok (2 rows)然而,規劃器將拒絕使用我們的索引。
demo=# analyze flights_bi; demo=# explain select * from flights_bi where box(airport_coord) <@ box '120,40,140,50';QUERY PLAN ---------------------------------------------------------------------Seq Scan on flights_bi (cost=0.00..985928.14 rows=30517 width=111)Filter: (box(airport_coord) <@ '(140,50),(120,40)'::box)為什么?讓我們禁用順序掃描,看看會發生什么:
demo=# set enable_seqscan = off; demo=# explain select * from flights_bi where box(airport_coord) <@ box '120,40,140,50';QUERY PLAN --------------------------------------------------------------------------------Bitmap Heap Scan on flights_bi (cost=14079.67..1000007.81 rows=30517 width=111)Recheck Cond: (box(airport_coord) <@ '(140,50),(120,40)'::box)-> Bitmap Index Scan on flights_bi_box_idx(cost=0.00..14072.04 rows=30517076 width=0)Index Cond: (box(airport_coord) <@ '(140,50),(120,40)'::box)(主要這里沒有analyse,cost是假設的,不是實際的)
似乎可以使用索引,但規劃器假設位圖必須構建在整個表上(Bitmap Index Scan節點的“行”),因此,規劃器在這種情況下選擇順序掃描也就不足為奇了。這里的問題是,對于幾何類型,PostgreSQL不收集任何統計數據,所以規劃器一定是盲目的(就是只能假設將位圖構建在整個表上,而不是只構建在需要索引的范圍上。下面展示了PG沒有收集以box數據類型構建的索引的信息):
demo=# select * from pg_stats where tablename = 'flights_bi_box_idx' \gx-[ RECORD 1 ]----------+------------------- schemaname | bookings tablename | flights_bi_box_idx attname | box inherited | f null_frac | 0 avg_width | 32 n_distinct | 0 most_common_vals | most_common_freqs | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram |唉。但沒有人抱怨該索引——它確實有效,而且效果很好:
demo=# explain (costs off,analyze) select * from flights_bi where box(airport_coord) <@ box '120,40,140,50';QUERY PLAN ----------------------------------------------------------------------------------Bitmap Heap Scan on flights_bi (actual time=158.142..315.445 rows=781790 loops=1)Recheck Cond: (box(airport_coord) <@ '(140,50),(120,40)'::box)Rows Removed by Index Recheck: 70726Heap Blocks: lossy=14772-> Bitmap Index Scan on flights_bi_box_idx(actual time=158.083..158.083 rows=147720 loops=1)Index Cond: (box(airport_coord) <@ '(140,50),(120,40)'::box)Planning time: 0.137 msExecution time: 340.593 ms結論必須是這樣的:如果幾何體需要任何特別的東西,就需要PostGIS。它可以收集統計數據。
內部構件
傳統的擴展名“pageinspect”使我們能夠查看BRIN索引內部。
首先,元信息將提示我們范圍的大小以及為“revmap”分配的頁面數:
demo=# select * from brin_metapage_info(get_raw_page('flights_bi_scheduled_time_idx',0));magic | version | pagesperrange | lastrevmappage ------------+---------+---------------+----------------0xA8109CFA | 1 | 128 | 3 (1 row)這里的第1-3頁是為“revmap”分配的,而其余部分包含摘要數據。從“revmap”中,我們可以獲得每個范圍的摘要數據的參考。比如,包含前128頁的第一個范圍的信息位于此處:(第6頁,offset為197,第4、5頁是預留給之后擴展revmap的)
demo=# select * from brin_revmap_data(get_raw_page('flights_bi_scheduled_time_idx',1)) limit 1;pages ---------(6,197) (1 row)這是摘要數據本身:
demo=# select allnulls, hasnulls, value from brin_page_items(get_raw_page('flights_bi_scheduled_time_idx',6),'flights_bi_scheduled_time_idx' ) where itemoffset = 197;allnulls | hasnulls | value ----------+----------+----------------------------------------------------f | f | {2016-08-15 02:45:00+03 .. 2016-08-15 17:15:00+03} (1 row)下一個范圍:
//定位TID所在頁與行 demo=# select * from brin_revmap_data(get_raw_page('flights_bi_scheduled_time_idx',1)) offset 1 limit 1;pages ---------(6,198) (1 row)//得到該行包含的摘要信息 demo=# select allnulls, hasnulls, value from brin_page_items(get_raw_page('flights_bi_scheduled_time_idx',6),'flights_bi_scheduled_time_idx' ) where itemoffset = 198;allnulls | hasnulls | value ----------+----------+----------------------------------------------------f | f | {2016-08-15 06:00:00+03 .. 2016-08-15 18:55:00+03} (1 row)等等。
對于“包含”類,“值”字段將顯示如下內容
{(94.4005966186523,69.3110961914062),(77.6600036621,51.6693992614746) .. f .. f}第一個值是嵌入矩形,末尾第一個“f”字母表示缺少空元素,第二個f表示缺少不可合并的值。實際上,唯一不可合并的值是“IPv4”和“IPv6”地址(“inet”數據類型)。
屬性
我們之前已經提供了查詢方法。
以下是訪問方法的屬性:
amname | name | pg_indexam_has_property --------+---------------+-------------------------brin | can_order | fbrin | can_unique | fbrin | can_multi_col | tbrin | can_exclude | f可以在多個列上創建索引。在這種情況下,它會為每一列收集自己的摘要統計信息,但它們會將每個范圍存儲在一起。當然,如果一個相同大小的范圍適用于所有列,那么這個索引是有意義的。
以下索引層的特性:
name | pg_index_has_property ---------------+-----------------------clusterable | findex_scan | fbitmap_scan | tbackward_scan | f顯然,只支持位圖掃描。
然而,集群的缺乏似乎令人困惑。從表面上看,由于BRIN索引對行的物理順序很敏感,因此能夠根據索引對數據進行聚類是合乎邏輯的。但事實并非如此。我們只能創建一個“常規”索引(B樹或GiST,取決于數據類型)并根據它進行聚類。順便問一下,你愿意對一個巨大的表進行集群并同時考慮到獨占鎖、執行時間和重建期間的磁盤空間消耗嗎?
以下是列層面的特性:
name | pg_index_column_has_property --------------------+------------------------------asc | fdesc | fnulls_first | fnulls_last | forderable | fdistance_orderable | freturnable | fsearch_array | fsearch_nulls | t唯一可用的屬性是操縱空值的能力。
總結
以上是生活随笔為你收集整理的PostgreSQL中的索引—9(BRIN)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 豆瓣读书TOP250书籍信息爬虫脚本
- 下一篇: ios逆向工具tweak logos语法