我的表中有一个JsonB列,它包含分层信息。
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.5秒。 这产生了
的分析 -> 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
现在我可以通过替换索引来获得大量的速度 - >> ' parentId的'使用cte中的函数,并在函数上创建索引。
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)
那么为什么索引的运行速度和函数索引一样快呢? 那里似乎有一种多余的排序。 我不想只使用函数索引的原因是它是IMMUTABLE所以索引不会在INSERT / UPDATE / DELETE之后自动更新。
PS我不是在寻找架构变更建议。
答案 0 :(得分:0)
看起来Gin索引表现良好。 如果我在索引列上创建Gin索引,然后将Join更改为
ON t.indexes @> jsonb_build_object('Id', c.indexes -> 'parentId')
以及
的地方WHERE t.indexes @> jsonb_build_object('Id', h.pid)
它没有纯函数索引那么快,但它最少会动态更新,执行计划没有那种不必要的排序
通过添加gin索引标志 jsonb_path_ops
可以进一步提高性能