在大数据查询中,中介“从CAST到文本”是性能瓶颈... JSONb数据类型处有很好的二进制信息:如何挽救它?
典型的“选择位置”示例:
with t(x,j) as (select 'hello','{"flag1":true,"flag2":false}'::jsonb)
SELECT x FROM t
WHERE (j->>'flag1')::boolean AND NOT((j->>'flag2')::boolean)
“广播到文本”是一个很大的性能损失。理想的机制是直接从JSONb到Boolean
WHERE (j->'flag1')::magic_boolean AND NOT((j->'flag2')::magic_boolean)
PS:可以使用C ++吗? CREATE CAST
C ++实现可以解决此问题吗?
答案 0 :(得分:4)
该功能在Postgres 11:
中实现E.4.3.4。数据类型
[...]
将JSONB标量的类型转换添加到数字和布尔数据类型(Anastasia Lubennikova)
答案 1 :(得分:2)
TL; DR
在性能方面,最好将#>
与适当的索引一起使用,该索引涵盖所有JSON属性,包括类型转换(以避免在访问索引时进行类型转换):https://dbfiddle.uk/?rdbms=postgres_11&fiddle=4da77576874651f4d2cf801142ae34d2
CREATE INDEX idx_flags_btree_jsonb ON t ((j#>'{flag1}'), (j#>'{flag2}'));
次数(在1,000,000中都选择了相同的5,195行):
->>::boolean | ~75 ms
->::boolean | ~55 ms
@> | ~80 ms
#> | ~40 ms
可扩展性:
有趣的是,对每个查询进行的10次运行(不包括首次运行和最后一次运行)中,有40M行(均缓存在内存中,此处没有I / O延迟)的本地测试显示以下(最佳)数字:
->>::boolean | 222.333 ms
->::boolean | 268.002 ms
@> | 1644.605 ms
#> | 207.230 ms
因此,实际上, new 强制转换似乎会使大型数据集的运行速度变慢(我怀疑这是因为它在转换为{之前仍然转换为text
{1}},但不是直接在包装器中)。
我们还可以看到,使用boolean
索引的@>
运算符在此处无法很好地扩展,这是可以预期的,因为它比其他专用索引更通用,因此,需要做更多的事情。
但是,如果无法放置这些特殊用途的GIN
索引或I / O成为瓶颈,那么btree
索引将是更好的选择,因为它仅占用磁盘上的一小部分空间磁盘(以及内存),增加了索引缓冲区命中的机会。
但这取决于很多因素,需要在了解所有正在访问的应用程序的情况下进行决定。
详细信息:
最好将GIN
包含运算符与单个@>
索引一起使用,因为它可以节省许多特殊用途的索引:
GIN
...给出的计划如下:
with t(x,j) as (select 'hello','{"flag1":true,"flag2":false}'::jsonb)
SELECT x FROM t
WHERE j @> '{"flag1":true, "flag2":false}'::jsonb;
作为替代方法(如果您有能力创建专用索引并产生写损失),请使用 QUERY PLAN
-----------------------------------------------------------
CTE Scan on t (cost=0.01..0.03 rows=1 width=32)
Filter: (j @> '{"flag1": true, "flag2": false}'::jsonb)
CTE t
-> Result (cost=0.00..0.01 rows=1 width=64)
(4 rows)
运算符而不是#>
或->
,从而跳过任何性能-昂贵的类型转换,例如
->>
...产生的计划如下:
with t(x,j) as (select 'hello','{"flag1":true,"flag2":false}'::jsonb)
SELECT x FROM t
WHERE j#>'{flag1}' = 'true'::jsonb AND j#>'{flag2}' = 'false'::jsonb;
因此,这里不再进行隐式类型转换(仅适用于给定的常量,但这是一次性操作,不适用于每一行)。