1. 程式人生 > >PostgreSQL使用函數的多表關聯視圖在排序時的性能問題

PostgreSQL使用函數的多表關聯視圖在排序時的性能問題

計劃 limit 性能問題 src reat 更多 var lar 測試

一、問題描述

近日PostgreSQL的某個表的記錄數由萬級增加到一百萬級(設計能力是一億)時,建立在該表之上的某個多表關聯VIEW的查詢性能急劇變慢(大約從10ms級躍升到100s級)。經分析查詢計劃,發現瓶頸在於排序用時很長;而排序用時的誘因是什麽?在排除掉一個個其它因素後,發現是VIEW定義中使用了函數(非內部函數,特此說明)。在將自定義函數更改為等效的子查詢或連接查詢時,性能得到很好的改善。

因為實際的表、VIEW、函數都過於復雜,為描述及重現問題的關鍵,以下將表、VIEW、函數等都進行簡化,。

二、測試對象

創建三個測試用表:

create table t1
(
  id int not
null primary key, name varchar(20) ); create table t2 ( id int not null primary key, name varchar(20) ); create table t3 ( id int not null primary key, name varchar(20) );

再創建函數、包含它的VIEW、作為對比的等效VIEW:

create or replace function f1(p_id in int)
  returns int as $$
  declare v_name varchar(40
); begin select name3 into v_name from t3 where id=p_id; if v_name is null then return 0; else return length(v_name); end if; end; $$ language plpgsql; create view v1 as select t1.id id, t1.name name1, t2.name name2, f1(t1.id) length from t1 left join t2 on t2.id=t1.id;

create view v2 as
select t1.id id, t1.name1 name1, t2.name2 name2,
coalesce(length(t3.name3), 0) length
from t1 left join t2 on t2.id=t1.id left join t3 on t3.id=t1.id;

最後向表t1插入8000條記錄,id為1..8000,name的值則無所謂;再分別拷貝部分數據到表t2、t3:

insert into t2 select * from t1 where t1.id%2=0;
insert into t3 select * from t1 where t1.id%3=0;

三、檢查執行計劃

完成所有準備工作後,檢查在兩個VIEW上排序查詢的執行計劃:

技術分享圖片

技術分享圖片

顯然,排序用時是大頭,而v1在排序上的用時差不多是v2的3倍。

再進一步測試,如果加上limit ... offset(常跟order by一起用於分頁),兩個VIEW的差距更加明顯:

技術分享圖片

技術分享圖片

如果去掉排序操作,v1的查詢用時也會多於v2,但相比之前少差不多一個數量級。限於篇幅,這裏不貼圖了。

本次測試的記錄數還不到萬級,已經可以看出兩種VIEW的差距,有興趣的同行可以自行驗證表記錄數在十萬級及更多以上的情況。

四、結論

根據本次簡單測試,並結合實踐中的情況(抱歉不能給出詳情),得出以下結論:

  1. PostgreSQL中,如果多表關聯VIEW中包使用了函數,盡量修改為子查詢或連接查詢;
  2. 對於排序操作,含函數的VIEW的性能遠低於等效的子查詢或連接查詢的VIEW,如再含有分頁操作,兩者的性能差距更大;
  3. 實踐中,當主表記錄數達到一定數量(臨界值未知),含函數的VIEW的排序甚至無法利用索引而走全表掃描,從而引發用時劇增;
  4. 以前的實踐中,Oracle中兩種VIEW的差別並不是十分明顯。

PostgreSQL使用函數的多表關聯視圖在排序時的性能問題