2016-01-07 83 views
1

我的表中有一個JsonB列,它包含分層信息。現在Postgres分層(jsonb)CTE不必要慢

MyTable (id uuid, indexes jsonb, content bytea) 

如果我創建一個CTE說

WITH RECURSIVE hierarchy(pid, id, content) AS (
    --load first parents 
    SELECT t.indexes ->> 'parentId' as pId, t.id, t.content FROM MyTable c 
    JOIN MyTable t ON t.indexes ->> 'Id' = c.indexes ->> 'parentId' 
    WHERE c.Id = ANY('{..Some UUIDS}') 
    UNION 
    SELECT t.indexes ->> 'parentId' as pId, t.id, t.content 
    FROM hierarchy h, MyTable t 
    WHERE t.indexes ->> 'Id' = h.pid 
) SELECT id, content from hierarchy 

現在的300K記錄的表內建立2個節點的父樹的例子運行大約需要10秒。現在

如果我創建一個索引

CREATE INDEX MyIndex ON MyTable 
USING btree 
((indexes ->> 'Id') 

這減少了時間4.5S。 由此產生的

-> Recursive Union (cost=23.81..4528423.71 rows=80794929 width=1219) (actual time=0.188..1802.636 rows=5 loops=1) 
      -> Nested Loop (cost=23.81..3150.15 rows=899 width=1219) (actual time=0.132..0.133 rows=1 loops=1) 
       Output: (t.indexes ->> 'parentId'::text), t.id, t.content 
       -> Index Scan using "MyTable_pkey" on "TEST"."MyTable" c (cost=0.42..8.44 rows=1 width=123) (actual time=0.053..0.053 rows=1 loops=1) 
         Output: c.id, c.content, c.indexes 
         Index Cond: (c.id = ANY ('{1c725f08-0324-41e9-b417-5ec885fb1cc9}'::uuid[])) 
       -> Bitmap Heap Scan on "TEST"."MyTable" t (cost=23.39..3130.48 rows=899 width=1219) (actual time=0.066..0.066 rows=1 loops=1) 
         Output: t.id, t.content, t.indexes 
         Recheck Cond: (((t.indexes ->> 'Id'::text) = (c.indexes ->> 'parentId'::text))) 
         Heap Blocks: exact=1 
         -> Bitmap Index Scan on "MyIndex" (cost=0.00..23.17 rows=899 width=0) (actual time=0.055..0.055 rows=1 loops=1) 
          Index Cond: ((t.indexes ->> 'Id'::text) = (c.indexes ->> 'parentId'::text)) 

//UNION PART 
      -> Merge Join (cost=770.60..290937.50 rows=8079403 width=1219) (actual time=360.467..360.476 rows=1 loops=5) 
       Output: (t_1.indexes ->> 'parentId'::text), t_1.id, t_1.content 
       Merge Cond: ((t_1.indexes ->> 'Id'::text) = h.pid) 
       -> Index Scan using "MyIndex" on "TEST"."MyTable" t_1 (cost=0.42..127680.55 rows=179742 width=1219) (actual time=0.019..288.168 rows=60478 loops=5) 
         Output: t_1.id, t_1.sourceid, t_1.content, t_1.indexes 
       -> Sort (cost=770.18..792.65 rows=8990 width=32) (actual time=0.010..0.011 rows=1 loops=5) 
         Output: h.pid 
         Sort Key: h.pid 
         Sort Method: quicksort Memory: 25kB 
         -> WorkTable Scan on hierarchy h (cost=0.00..179.80 rows=8990 width=32) (actual time=0.001..0.001 rows=1 loops=5) 
          Output: h.pid 

分析現在,我可以通過更換指標獲得海量速度impovements - >>「的parentId」在熱膨脹係數的功能,並創建該函數的索引。

CREATE FUNCTION "TEST"."MyFunction"(idarg uuid) 
    RETURNS text AS 
$BODY$ 
SELECT t.indexes ->> 'Id' as result FROM "TEST"."MyTable" t 
WHERE t.id = idarg 
$BODY$ 
LANGUAGE sql IMMUTABLE; 

隨着指數

CREATE INDEX MyFunctionIndex ON MyTable 
USING btree 
(MyFunction(id)) 

這現在只需0.01秒,以執行查詢 隨着分析

-> Recursive Union (cost=23.81..5333205.06 rows=80794929 width=1219) (actual time=0.163..0.291 rows=5 loops=1) 
     -> Nested Loop (cost=23.81..3372.65 rows=899 width=1219) (actual time=0.082..0.084 rows=1 loops=1) 
      Output: (t.indexes ->> 'parentId'::text), t.id, t.content, t.modified 
      -> Index Scan using "MyTable_pkey" on "TEST"."MyTable" c (cost=0.42..8.44 rows=1 width=123) (actual time=0.019..0.019 rows=1 loops=1) 
        Output: c.id, c.sourceid, c.viewid, c.content, c.indexes, c.statekey, c.modified 
        Index Cond: (c.id = ANY ('{1c725f08-0324-41e9-b417-5ec885fb1cc9}'::uuid[])) 
      -> Bitmap Heap Scan on "TEST"."MyTable" t (cost=23.39..3352.98 rows=899 width=1219) (actual time=0.037..0.037 rows=1 loops=1) 
        Output: t.id, t.content, t.indexes 
        Recheck Cond: (("TEST"."MyFunction"(t.id) = (c.indexes ->> 'parentId'::text))) 
        Heap Blocks: exact=1 
        -> Bitmap Index Scan on "MyFunctionIndex" (cost=0.00..23.17 rows=899 width=0) (actual time=0.025..0.025 rows=1 loops=1) 
         Index Cond: ("TEST"."MyFunction"(t.id) = (c.indexes ->> 'parentId'::text)) 

//UNION PART 
      -> Nested Loop (cost=0.42..371393.38 rows=8079403 width=1219) (actual time=0.012..0.013 rows=1 loops=5) 
       Output: (t_1.indexes ->> 'parentId'::text), t_1.id, t_1.content 
       -> WorkTable Scan on hierarchy h (cost=0.00..179.80 rows=8990 width=32) (actual time=0.000..0.000 rows=1 loops=5) 
         Output: h.pid, h.id, h.content 
       -> Index Scan using "MyFunctionIndex" on "TEST"."MyTable" t_1 (cost=0.42..30.06 rows=899 width=1219) (actual time=0.010..0.010 rows=1 loops=5) 
         Output: t_1.id, t_1.content, t_1.indexes 
         Index Cond: ("TEST"."MyFunction"(t_1.id) = h.pid) 

那麼爲什麼不能在指數運行速度極快,在functionindex?
那裏似乎有一種多餘的排序。 而我不想僅僅使用函數索引的原因是它是不可變的,所以在INSERT/UPDATE/DELETE之後索引不會自動更新。

PS我不是在尋找模式更改建議。

+2

請顯示** complete **執行計劃 –

+0

使用函數進行大幅度的速度改進部分實際上可能是函數的'IMMUTABLE'揮發性。嘗試將其更改爲「穩定」,看看是否和如何改變。如果你將其聲明爲'VOLATILE',所有的改進都將消失。另外,正如a_horse_with_no_name所寫,發佈完整的執行計劃以獲得完整的圖片。 – Eggplant

+0

@a_horse_with_no_name,完成後,您會注意到頂部部分的相似性。 –

回答

0

看起來像杜松子酒指數表現良好。 如果我創建索引列杜松子酒指數,然後更改加入到

ON t.indexes @> jsonb_build_object('Id', c.indexes -> 'parentId') 

而且凡

WHERE t.indexes @> jsonb_build_object('Id', h.pid) 

它的速度比不上純函數索引,但至少它將動態更新,並且執行計劃沒有那種不必要的排序

通過添加杜松子酒索引標誌可以進一步提高性能jsonb_path_ops