如何访问JSONb的内部表示形式?

时间:2019-05-22 10:58:54

标签: postgresql jsonb

在大数据查询中,中介“从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 ++实现可以解决此问题吗?

2 个答案:

答案 0 :(得分:4)

该功能在Postgres 11:

中实现
  

E.4.3.4。数据类型

     

[...]

     

将JSONB标量的类型转换添加到数字和布尔数据类型(Anastasia Lubennikova)

Db<>Fiddle.

答案 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;

因此,这里不再进行隐式类型转换(仅适用于给定的常量,但这是一次性操作,不适用于每一行)。