介绍PostgreSQL中的jsonb数据类型
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/';
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))
json检查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字段,并且检查容量;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论