Postgresql 如何访问JSONb的内部表示?

Postgresql 如何访问JSONb的内部表示?,postgresql,jsonb,Postgresql,Jsonb,在大数据查询中,文本转换中介是一个性能瓶颈。。。好的二进制信息就在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数据类型中:如何拯救它

典型选择,例如:

 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++吗?有可能创建一个C++实现来解决这个问题吗?< /P> < P>该特性是在

中实现的。 E.4.3.4。数据类型

[……]

将JSONB标量的强制转换添加到数值和布尔数据类型Anastasia Lubennikova

TL;博士

在性能方面,最好使用>和适当的索引,该索引覆盖所有JSON属性,包括类型转换,以避免访问索引时发生类型转换:

在tj>'{flag1}',j>'{flag2}'上创建索引idx_flags_btree_jsonb; 在1000000行中选择相同5195行的次数:

->>::boolean | ~75 ms
->::boolean  | ~55 ms
@>           | ~80 ms
#>           | ~40 ms
可扩展性:

有趣的是,一个本地测试显示,除了每个查询的第一次和最后一次运行外,10次运行中有以下最佳数字,其中40万行都缓存在内存中,没有I/O延迟:

->>::boolean |  222.333 ms
->::boolean  |  268.002 ms
@>           | 1644.605 ms
#>           |  207.230 ms
因此,事实上,新的cast似乎减慢了更大数据集的速度,我怀疑这是因为它在转换为布尔值之前仍然转换为文本,但在包装器中,而不是直接转换为文本

我们还可以看到,使用GIN索引的@>操作符在这里的伸缩性不是很好,这是意料之中的,因为它比其他专用索引更通用,因此需要在幕后做更多的工作

但是,如果这些特殊用途的btree索引无法放置到位或I/O成为瓶颈,那么GIN索引将更优越,因为它只消耗磁盘和内存中的一小部分空间,增加了索引缓冲区命中的机会

但这取决于许多因素,需要在了解所有访问应用程序的情况下确定

详情:

最好使用带有单个GIN索引的@>包容运算符,因为它可以节省大量专用索引:

使用tx,j作为选择'hello','{flag1:true,flag2:false}'::jsonb 从t中选择x 其中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)
                                               QUERY PLAN                                               
--------------------------------------------------------------------------------------------------------
 CTE Scan on t  (cost=0.01..0.04 rows=1 width=32)
   Filter: (((j #> '{flag1}'::text[]) = 'true'::jsonb) AND ((j #> '{flag2}'::text[]) = 'false'::jsonb))
   CTE t
     ->  Result  (cost=0.00..0.01 rows=1 width=64)
(4 rows)
作为一种替代方法,如果您可以创建特殊用途的索引,并因此产生写惩罚,请使用>运算符而不是->或->>,这样可以跳过任何性能代价高昂的类型转换,例如

使用tx,j作为选择'hello','{flag1:true,flag2:false}'::jsonb 从t中选择x 其中j>'{flag1}'='true':jsonb和j>'{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)
                                               QUERY PLAN                                               
--------------------------------------------------------------------------------------------------------
 CTE Scan on t  (cost=0.01..0.04 rows=1 width=32)
   Filter: (((j #> '{flag1}'::text[]) = 'true'::jsonb) AND ((j #> '{flag2}'::text[]) = 'false'::jsonb))
   CTE t
     ->  Result  (cost=0.00..0.01 rows=1 width=64)
(4 rows)

因此,这里不再针对给定的常量进行隐式类型转换,但这是一次性操作,不是针对每一行。

谢谢,这是对旧Pg版本的一个很好的提示。对于第11页来说,这是个好消息!,正如@Klin answer所示,它解决了这个问题。您好,感谢所有的基准测试/演示!。编辑后的评论:您使用了PostgreSQL v11?结论是有?是的,我使用的是PostgreSQL 11。不,这不是谎言,类型转换对于v11中的JSONB来说是非常新的,只是它的实现没有比将文本转换为布尔值更好。查看解释计划,很明显,行越多,类型转换越慢。我刚刚用更多的行做了另一个测试,因为我在这里看到了一个趋势来验证…嗨,谢谢你的回复!。是的,这只是一个关于性能的谎言,它是一个糖语法,但不是一个真正严肃的实现。。。也许需要一些选票来说嘿,表现也很重要!给PostgreSQL开发团队。嗯。。。请参阅@Ancoron analysis,关于pg v11的新闻与性能无关,似乎只是一种语法糖。@Ancoron-您对查询计划的解释是错误的。j->“flag1”::文本与j->“flag1”::文本不同。在查询计划中,在本例中,flag1常量自动转换为文本。获取点。是的,那是真的,我的错是误解了这里的解释。我只是亲自查看了一下代码,它实际上是直接从JSON结构中提取的真正的布尔值:。