欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

介绍PostgreSQL中的jsonb数据类型

程序员文章站 2022-04-13 08:35:01
postgresql 9.4 正在加载一项新功能叫jsonb,是一种新型资料,可以储存支援gin索引的json 资料。换言之,此功能,在即将来临的更新中最重要的是,如果连这...

postgresql 9.4 正在加载一项新功能叫jsonb,是一种新型资料,可以储存支援gin索引的json 资料。换言之,此功能,在即将来临的更新中最重要的是,如果连这都不重要的话,那就把postgres 置于文件为本数据库系统的推荐位置吧。

自从9.2开始,一个整合json 资料类型已经存在,带有一整套功能(例如资料产生和资料解构功能),还有9.3新增的操作者。当使用json 资料类型,资料的被存储成一完全一样的副本,功能还在此之上运作,还另外需要后台运作的重新分析。

这心得jsonb 资料类型以已降解的2元格式存储,所以,插入此资料会比json高效,因为后台不再需要重新分析,因此让它更快速运行,而且还兼顾gin 索引。就是因为最后这个原因,我们实际上建议读者使用jsonb来代替json制作程式(当然你还可以因应需要而使用json)。请记住jsonb使用相同的操作者和功能,读者们可以看我之前的帖子去令你得到些什么启发(或者干脆看postgres的文件)。
 

现在让我们看一下jsonb是如何工作的,同时和json比较一下。采用的测试数据是860万的类型数据,大概1.1g大小,包括了城市名,国家代码(可以参见完整列表)等很多字段。首先通过底层复制(raw copy)来把这些数据存储到数据库的一个新表里面,之后把这张表通过一组填充因子是100的表转换成json/jsonb,之后来看它们各占多少空间。
 

=# copy geodata from '$home/downloads/allcountries.txt';
copy 8647839
=# create table geodata_jsonb (data jsonb) with (fillfactor=100);
create table
=# create table geodata_json (data json) with (fillfactor=100);
create table
=# \timing
timing is on.
=# insert into geodata_json select row_to_json(geodata) from geodata;
insert 0 8647839
time: 287158.457 ms
=# insert into geodata_jsonb select row_to_json(geodata)::jsonb from geodata;
insert 0 8647839
time: 425825.967 ms

生成jsonb数据花费稍微长一点时间,大小有没有区别呢?
 

=# select pg_size_pretty(pg_relation_size('geodata_json'::regclass)) as json,
     pg_size_pretty(pg_relation_size('geodata_jsonb'::regclass)) as jsonb;
 json  | jsonb 
---------+---------
 3274 mb | 3816 mb
(1 row)

在json数据上面做索引从9.3版本开始,比如用操作符(注意 因为它返回文本,所以'->>'被采用;并且根据查询不同,索引采用不同的关键字)
 

=# create index geodata_index on
  geodata_json ((data->>'country_code'), (data->>'asciiname'));
create index
=# select pg_size_pretty(pg_relation_size('geodata_index'::regclass))
  as json_index;
 json_index 
------------
 310 mb
(1 row)
=# select (data->>'population')::int as population,
     data->'latitude' as latitude,
     data->'longitude' as longitude
  from geodata_json where data->>'country_code' = 'jp' and
    data->>'asciiname' = 'tokyo' and
    (data->>'population')::int != 0;
 population | latitude | longitude 
------------+----------+-----------
  8336599 | 35.6895 | 139.69171
(1 row)
=# -- explain of previous query
                            query plan                            
-------------------------------------------------------------------------------------------------------------------------
 bitmap heap scan on geodata_json (cost=6.78..865.24 rows=215 width=32)
  recheck cond: (((data ->> 'country_code'::text) = 'jp'::text) and ((data ->> 'asciiname'::text) = 'tokyo'::text))
  filter: (((data ->> 'population'::text))::integer <> 0)
  -> bitmap index scan on geodata_index (cost=0.00..6.72 rows=216 width=0)
     index cond: (((data ->> 'country_code'::text) = 'jp'::text) and ((data ->> 'asciiname'::text) = 'tokyo'::text))
 planning time: 0.172 ms
(6 rows)

在这个例子里,计划(planner)可以使用bitmap索引扫描,同时使用了之前产生的索引。


现在,jsonb的一个新特点就是检查包含带有操作符@>的数据容量,这种数据是可以用gin来索引的,这种操作符数据也包括了?,?|和?&(为了检查给定的关键字是否存在)。 gin索引对两类操作符起作用:

    缺省操作符类,之前列出的四个;

    jsonb_hash_ops,仅支持@>,但是当搜索数据时性能表现不错,而且所占磁盘空间较小;

下面是它如何工作:
 

=# create index geodata_gin on geodata_jsonb
   using gin (data jsonb_hash_ops);
create index
=# select (data->>'population')::int as population,
   data->'latitude' as latitude,
   data->'longitude' as longitude
  from geodata_jsonb where data @> '{"country_code": "jp", "asciiname": "tokyo"}' and
    (data->>'population')::int != 0;
 population | latitude | longitude 
------------+----------+-----------
  8336599 | 35.6895 | 139.69171
(1 row)
 =# select pg_size_pretty(pg_relation_size('geodata_gin'::regclass)) as jsonb_gin;
 jsonb_gin
-----------
 1519 mb
(1 row)
=# -- explain of previous query
                   query plan                   
-------------------------------------------------------------------------------------
 bitmap heap scan on geodata_jsonb (cost=131.01..31317.76 rows=8605 width=418)
  recheck cond: (data @> '{"asciiname": "tokyo", "country_code": "jp"}'::jsonb)
  filter: (((data ->> 'population'::text))::integer <> 0)
  -> bitmap index scan on geodata_gin (cost=0.00..128.86 rows=8648 width=0)
     index cond: (data @> '{"asciiname": "tokyo", "country_code": "jp"}'::jsonb)
 planning time: 0.134 ms

根据应用的需求,你或许想采用空间消耗低的索引,比如btree建立在json数据上的索引类型;gin索引有着更多的优点,因为它覆盖了所有的json字段,并且检查容量;