簡體   English   中英

如何提高Postgres select語句的速度?

[英]How do I increase the speed of my Postgres select statement?

我有以下表格:

CREATE TABLE views (
    view_id bigint NOT NULL,
    usr_id bigint,
    ip inet,
    referer_id bigint,
    country_id integer,
    validated smallint,
    completed smallint,
    value numeric
);

ALTER TABLE ONLY views
    ADD CONSTRAINT "Views_pkey" PRIMARY KEY (view_id);

CREATE TABLE country (
    country_id integer NOT NULL,
    country character varying(2)
);

ALTER TABLE ONLY country
    ADD CONSTRAINT country_pkey PRIMARY KEY (country_id);

CREATE TABLE file_id_view_id (
    file_id bigint,
    view_id bigint,
    created_ts timestamp without time zone
);

CREATE TABLE file_owner (
    file_id bigint NOT NULL,
    owner_id bigint
);

ALTER TABLE ONLY file_owner
        ADD CONSTRAINT owner_table_pkey PRIMARY KEY (file_id);

CREATE TABLE referer (
    referer_id bigint NOT NULL,
    referer character varying(255)
);

ALTER TABLE ONLY referer
    ADD CONSTRAINT referer_pkey PRIMARY KEY (referer_id);

viewsfile_id_view_id各有大約340M行。 每小時它們都會增加600K行。

file_owner表有75K行,每小時增加100行。

country表有233行,很少更改。

referer表有6494行,很少更改。

我的目標是能夠執行如下查詢:

SELECT Count(ft.*)                     AS total_views,
       ( Count(ft.*) - SUM(ft.valid) ) AS invalid_views,
       SUM(ft.valid)                   AS valid_views,
       SUM(ft.values)                  AS VALUES,
       ft.day                          AS day,
       ( CASE
           WHEN r.referer IS NULL THEN 'Unknown'
           ELSE r.referer
         END )                         AS referer,
       ( CASE
           WHEN c.country IS NULL THEN 'Unknown'
           ELSE c.country
         END )                         AS country
FROM   country c
       right join (referer r
                   right join (SELECT v.validated  AS valid,
                                      v.value      AS VALUES,
                                      vf.day       AS day,
                                      vf.view_id   AS view_id,
                                      v.referer_id AS referer_id,
                                      v.country_id AS country_id
                               FROM   VIEWS v,
                                      (SELECT view_id,
fivi.created_ts :: timestamp :: DATE AS
day
FROM   file_id_view_id fivi
join (SELECT file_id
      FROM   file_owner
      WHERE  owner_id = 75
      GROUP  BY file_id) fo
  ON ( fo.file_id = fivi.file_id )
WHERE  ( fivi.created_ts BETWEEN
  '2015-11-01' AND '2015-12-01' )
GROUP  BY view_id,
   day) vf
WHERE  v.view_id = vf.view_id) ft
ON ( ft.referer_id = r.referer_id ))
ON ( ft.country_id = c.country_id )
GROUP  BY day,
          referer,
          country;

生產:

total_views | invalid_views | valid_views | values |    day     |     referer     | country 
------------+---------------+-------------+--------+------------+-----------------+---------

使用EXPLAIN ANALYZE運行此類查詢時,會生成以下內容:

GroupAggregate  (cost=38893491.99..40443007.61 rows=182295955 width=52) (actual time=183725.696..205882.889 rows=172 loops=1)
  Group Key: ((fivi.created_ts)::date), r.referer, c.country
  ->  Sort  (cost=38893491.99..38984639.97 rows=182295955 width=52) (actual time=183725.655..200899.098 rows=8390217 loops=1)
        Sort Key: ((fivi.created_ts)::date), r.referer, c.country
        Sort Method: external merge  Disk: 420192kB
        ->  Hash Left Join  (cost=16340128.88..24989809.75 rows=182295955 width=52) (actual time=23399.900..104337.332 rows=8390217 loops=1)
              Hash Cond: (v.country_id = c.country_id)
              ->  Hash Left Join  (cost=16340125.36..24800637.72 rows=182295955 width=49) (actual time=23399.782..102534.655 rows=8390217 loops=1)
                    Hash Cond: (v.referer_id = r.referer_id)
                    ->  Merge Join  (cost=16340033.52..24051874.62 rows=182295955 width=29) (actual time=23397.410..99955.000 rows=8390217 loops=1)
                          Merge Cond: (fivi.view_id = v.view_id)
                          ->  Group  (cost=16340033.41..16716038.36 rows=182295955 width=16) (actual time=23397.298..30454.444 rows=8390217 loops=1)
                                Group Key: fivi.view_id, ((fivi.created_ts)::date)
                                ->  Sort  (cost=16340033.41..16434985.73 rows=189904653 width=16) (actual time=23397.294..28165.729 rows=8390217 loops=1)
                                      Sort Key: fivi.view_id, ((fivi.created_ts)::date)
                                      Sort Method: external merge  Disk: 180392kB
                                      ->  Nested Loop  (cost=6530.43..8799350.01 rows=189904653 width=16) (actual time=63.123..15131.956 rows=8390217 loops=1)
                                            ->  HashAggregate  (cost=6530.31..6659.62 rows=43104 width=8) (actual time=62.983..90.331 rows=43887 loops=1)
                                                  Group Key: file_owner.file_id
                                                  ->  Bitmap Heap Scan on file_owner  (cost=342.90..6508.76 rows=43104 width=8) (actual time=5.407..50.779 rows=43887 loops=1)
                                                        Recheck Cond: (owner_id = 75)
                                                        Heap Blocks: exact=5904
                                                        ->  Bitmap Index Scan on owner_id_index  (cost=0.00..340.74 rows=43104 width=0) (actual time=4.327..4.327 rows=45576 loops=1)
                                                              Index Cond: (owner_id = 75)
                                            ->  Index Scan using file_id_view_id_indexing on file_id_view_id fivi  (cost=0.11..188.56 rows=4406 width=24) (actual time=0.122..0.306 rows=191 loops=43887)
                                                  Index Cond: (file_id = file_owner.file_id)
                                                  Filter: ((created_ts >= '2015-11-01 00:00:00'::timestamp without time zone) AND (created_ts <= '2015-12-01 00:00:00'::timestamp without time zone))
                                                  Rows Removed by Filter: 184
                          ->  Index Scan using "Views_pkey" on views v  (cost=0.11..5981433.17 rows=338958763 width=25) (actual time=0.088..46804.757 rows=213018702 loops=1)
                    ->  Hash  (cost=68.77..68.77 rows=6591 width=28) (actual time=2.344..2.344 rows=6495 loops=1)
                          Buckets: 1024  Batches: 1  Memory Usage: 410kB
                          ->  Seq Scan on referer r  (cost=0.00..68.77 rows=6591 width=28) (actual time=0.006..1.156 rows=6495 loops=1)
              ->  Hash  (cost=2.70..2.70 rows=233 width=7) (actual time=0.078..0.078 rows=233 loops=1)
                    Buckets: 1024  Batches: 1  Memory Usage: 10kB
                    ->  Seq Scan on country c  (cost=0.00..2.70 rows=233 width=7) (actual time=0.005..0.042 rows=233 loops=1)
Planning time: 1.015 ms
Execution time: 206034.660 ms
(37 rows)

計划在explain.depesz.com上: http//explain.depesz.com/s/OiN

206s運行時間。

有些事情需要注意,

Postgresql版本9.4

我調整了配置如下:

  1. shared_buffers = 30GB
  2. work_mem = 32MB
  3. random_page_cost = 2.0
  4. cpu_tuple_cost = 0.0030
  5. cpu_index_tuple_cost = 0.0010
  6. cpu_operator_cost = 0.0005
  7. effective_cache_size = 52GB

目前存在以下索引:

  1. CREATE INDEX country_index使用btree(國家)的國家/地區;
  2. CREATE INDEX created_ts_index ON file_id_view_id使用btree(created_ts);
  3. CREATE INDEX file_id_created_ts_index ON file_id_view_id使用btree(created_ts,file_id);
  4. CREATE INDEX file_id_view_id_indexing ON file_id_view_id使用btree(file_id);
  5. CREATE INDEX owner_id_file_id_index ON file_owner使用btree(file_id,owner_id);
  6. CREATE INDEX owner_id_index ON file_owner使用btree(owner_id);
  7. CREATE INDEX referer_index ON referer使用btree(referer);

上一個查詢使用的是保守選擇的所有者ID ,一些查詢可能會導致file_id_view_id表的1/3視圖連接

改變數據結構是最后的手段。 在這個階段,這種變化必須引起嚴重關切。

如果需要,db可以被認為是只讀的,正在寫入的數據是每小時完成的,並且在每次寫入后給Postgres充足的喘息空間。 600K每小時寫入期間的當前時刻,數據庫將在1100s內返回(這是由於插入成本旁邊的其他原因)。 如果它會增加讀取速度,則有足夠的空間來添加附加索引,讀取速度是優先級。

硬件規格如下:

CPU: http//ark.intel.com/products/83356/Intel-Xeon-Processor-E5-2630-v3-20M-Cache-2_40-GHz

內存: 128GB

存儲: 1.5TB PCIE SSD

如何優化我的數據庫或查詢,以便我可以在合理的時間范圍內從數據庫中檢索出我需要的信息?

我該怎么做才能優化我目前的設計?

我相信Postgres及其運行的硬件具有比目前更好的性能。

UPDATE

我努力了:

  1. 分析表,不影響性能。
  2. 增加work_mem,這導致速度增加到116s。
  3. 通過避免子選擇來依賴Postgres的查詢規划器,這會對性能產生負面影響。
  4. 事先單獨進行數據庫查找,這似乎沒有正面/負面影響。

有沒有人有重建這么大的經驗? 這可行嗎? 需要幾天,幾小時(當然估計)?

我正在考慮對數據庫進行反規范化,因為它實際上只會在此方法中引用。 我唯一擔心的是 - 如果要從帶有索引owner_id的表中調用100M行,它會足夠快還是我仍然面臨相同的性能問題? 不願意走一條路然后不得不回溯。

我正在研究的另一個解決方案是@ ivan.panasuik建議,將所有日期數據分組到另一個表中,因為一旦過了一天,該信息是不變的,不需要更改或更新。 但是我不確定如何順利地實現這一點 - 我是否應該在插入處於暫停狀態時查詢數據並盡可能快地捕獲日期? 從那時起有一個觸發器設置?

數據庫的速度通常不是您的硬件,而是您使用引擎本身的智能和功能的程度。

  1. 盡量避免使用子選擇 - 特別是在處理大量數據時。 這些通常無法由查詢計划程序優化。 在大多數情況下,如果需要,您應該能夠將簡單的子選擇轉換為JOIN或甚至單獨的數據庫查找。

  2. 對表進行分區 - PostgreSQL本身不會這樣做(有點)但如果你經常只訪問最近的數據,你可以通過移動存檔數據來刪除大量工作。

  3. 考慮一個數據倉庫策略 - 當你處理這些數據時,你應該考慮以非規范化的方式存儲數據的副本,這樣很容易檢索,因為已經處理了令人討厭的JOIN。 我們使用Redshift(PostgeSQL的派生物)來做這件事,這樣我們在運行報告時就不需要做任何JOIN。

  1. 刪除(Count(ft。*) - SUM(ft.valid))AS invalid_views,因為您已經擁有此值,並且您可以在顯示結果期間稍后計算
  2. 在file_owner.file_id上​​添加索引,並檢查在查詢中使用的每個字段是否都有索引(您在條件中使用的字段:where,group等)
  3. 我沒有更多地分析查詢,但似乎您應該在一些較小(和更快)的查詢中拆分查詢並使用臨時表或存儲過程連接它。
  4. 假設昨天的結果是不會改變的......你可以使用條件day = today()運行查詢並避免按天分組。 全天結果您可以保存在一個單獨的表中。 我發現它大部分時間都適用於分組。

沒有嘗試嘗試就很難預測優化....所以一個接一個地嘗試。 還有祝你好運。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM