2015-09-17 47 views
0

下面是用約600萬記錄的表結構:PostgreSQL的網絡地址範圍的查詢優化

CREATE TABLE "ip_loc" (
    "start_ip" inet, 
    "end_ip" inet, 
    "iso2" varchar(4), 
    "state" varchar(100), 
    "city" varchar(100) 
); 

CREATE INDEX "index_ip_loc" on ip_loc using gist(iprange(start_ip,end_ip)); 

這需要約1秒至執行查詢。

EXPLAIN ANALYZE select * from ip_loc where iprange(start_ip,end_ip)@>'180.167.1.25'::inet; 

Bitmap Heap Scan on ip_loc (cost=1080.76..49100.68 rows=28948 width=41) (actual time=1039.428..1039.429 rows=1 loops=1) 
    Recheck Cond: (iprange(start_ip, end_ip) @> '180.167.1.25'::inet) 
    Heap Blocks: exact=1 
    -> Bitmap Index Scan on index_ip_loc (cost=0.00..1073.53 rows=28948 width=0) (actual time=1039.411..1039.411 rows=1 loops=1) 
     Index Cond: (iprange(start_ip, end_ip) @> '180.167.1.25'::inet) Planning time: 0.090 ms Execution time: 1039.466 ms 

iprange是一個定製的類型:

CREATE TYPE iprange AS RANGE (
    SUBTYPE = inet 
); 

有沒有辦法做到的查詢速度更快?

+0

索引掃描的期望行和實際行之間存在相當大的差異。如果你運行'analyze ip_loc',這會改變嗎?漫長的掃描時間_might_表示您正在飽受指數膨脹之苦。如果使用'reindex'重建索引,這會改進嗎? –

回答

0

這些範圍是不重疊的?我想嘗試B樹索引end_ip做:

with candidate as (
    select * from ip_loc 
    where end_ip<='38.167.1.53'::inet 
    order by end_ip desc 
    limit 1 
) 
select * from candidate 
where start_ip<='38.167.1.53'::inet; 

作品在我的電腦上4M行爲0.1ms。

記得在填充數據後分析表。

+0

仍然不夠快,我想在1毫秒內完成。 – Joey

+0

然後嘗試這個改進的查詢。 – Tometzky

+0

@a_horse_with_no_name:以前的版本幾乎沒有那麼快,所以這個評論是適當的 – Tometzky

0

inet類型是一種複合類型,而不是構建IPv4地址所需的簡單32位;它包含一個網絡掩碼。這使得存儲,索引和檢索成爲不必要的複雜如果所有你感興趣的是真正的IP地址(即32位的實際地址,而不是與網絡掩碼的地址,例如你會從列出客戶端的Web服務器的應用程序),而且您不會操縱數據庫中的IP地址。如果是這種情況,您可以將start_ipend_ip作爲簡單整數存儲,並使用簡單整數比較進行操作。 (同樣可以使用integer[4]數據類型的IPv6地址來完成。)

要記住的一點是,the default range constructor behaviour is to include the lower bound and exclude the upper bound所以在你的索引和查詢,不包含實際end_ip

最後,如果您堅持使用範圍類型,請在索引上添加range_ops operator class以獲得最佳性能。

0

僅爲end_ip添加聚簇索引