相关文章推荐
伤情的消防车  ·  RDS ...·  1 月前    · 
骑白马的骆驼  ·  5.2 最小的Perl ...·  1 年前    · 
备案 控制台
学习
实践
活动
专区
工具
TVP
写文章
专栏首页 超级架构师 「Postgresql架构」使用PostgreSQL中的JSONB数据类型加快操作
2 0

海报分享

「Postgresql架构」使用PostgreSQL中的JSONB数据类型加快操作

从版本9.4开始,PostgreSQL在使用JSON数据的二进制表示jsonb时提供了显着的加速,这可以为您提供增加性能所需的额外优势。

什么是jsonb

由PostgreSQL文档定义的数据类型json和jsonb几乎相同;关键的区别在于json数据存储为JSON输入文本的精确副本,而jsonb以分解的二进制形式存储数据;也就是说,不是ASCII / UTF-8字符串,而是二进制代码。

这有一些直接的好处:

  1. 效率更高,
  2. 加工速度明显加快
  3. 支持索引(这可能是一个重要的优势,我们稍后会看到),
  4. 更简单的模式设计(用jsonb列替换实体 - 属性 - 值(EAV)表,可以查询,索引和连接,从而使性能提高到1000倍!)

还有一些缺点:

  1. 输入稍慢(由于增加的转换开销),
  2. 它可能需要比普通json更多的磁盘空间,因为更大的表占用空间,尽管并非总是如此,
  3. 由于缺乏统计信息,某些查询(尤其是聚合查询)可能会变慢。

最后一个问题背后的原因是,对于任何给定的列,PostgreSQL保存描述性统计信息,例如不同和最常见值的数量,NULL条目的分数,以及 - 对于有序类型 - 数据分布的直方图。当信息作为JSON字段输入时,所有这些都将不可用,并且您将遭受严重的性能损失,尤其是在大量JSON字段之间聚合数据(COUNT,AVG,SUM等)时。

为避免这种情况,您可以考虑存储稍后可能在常规字段上汇总的数据。

有关此问题的进一步评论,您可以阅读Heap的博客文章何时在PostgreSQL架构中避免使用JSONB。

用例:书籍条目

让我们使用带有书籍条目的玩具模型来说明在PostgreSQL中使用JSON数据时的一些基本操作。

如果您使用json或jsonb,本节中的操作将基本相同,但让我们回顾它们以刷新我们可以用JSON做什么,并在我们看到jsonb好吃之后立即设置我们的用例。

在表中定义列

很简单,我们使用jsonb数据类型指定数据列:

CREATE TABLE books ( book_id serial NOT NULL, data jsonb );

插入JSON数据

要将数据输入books表,我们只需将整个JSON字符串作为字段值传递:

INSERT INTO books VALUES (1, '{"title": "Sleeping Beauties", "genres": ["Fiction", "Thriller", "Horror"], "published": false}');
INSERT INTO books VALUES (2, '{"title": "Influence", "genres": ["Marketing & Sales", "Self-Help ", "Psychology"], "published": true}');
INSERT INTO books VALUES (3, '{"title": "The Dictator''s Handbook", "genres": ["Law", "Politics"], "authors": ["Bruce Bueno de Mesquita", "Alastair Smith"], "published": true}');
INSERT INTO books VALUES (4, '{"title": "Deep Work", "genres": ["Productivity", "Reference"], "published": true}');
INSERT INTO books VALUES (5, '{"title": "Siddhartha", "genres": ["Fiction", "Spirituality"], "published": true}');

查询数据

我们现在可以查询JSON数据中的特定键:

SELECT data->'title' AS title FROM books;

这将从JSONB数据中提取的标题作为列返回:

title --------------------------- "Sleeping Beauties" "Influence" "The Dictator's Handbook" "Deep Work" "Siddhartha" (5 rows)

过滤结果

您也可以使用WHERE子句但通过JSON键以正常方式过滤结果集:

SELECT * FROM books WHERE data->'published' = 'false';

在这种情况下,返回原始JSON数据:

book_id | data ---------+------------------------------------------------------------------------------------------------- 1 | {"title": "Sleeping Beauties", "genres": ["Fiction", "Thriller", "Horror"], "published": false} (1 row)

展开数据

这是一个重要的问题,因为它将使我们能够在处理关系数据库时使用我们熟悉的聚合函数,但是在JSON数据的反直觉环境中也是如此。

SELECT jsonb_array_elements_text(data->'genres') AS genre FROM books WHERE book_id = 1; 这会将JSON数组扩展为一列: genre ---------- Fiction Thriller Horror (3 rows)

特殊的jsonb功能

除了效率之外,还有其他方法可以让您以二进制形式存储JSON。

其中一个增强功能是GIN(广义倒置索引)索引以及随附的新品牌运营商。

检查遏制(Checking Containment)

Containment测试一个文档(一个集合或一个数组)是否包含在另一个文档中。这可以使用@>运算符在jsonb数据中完成。

例如,数组[“Fiction”,“Horror”]包含在数组[“Fiction”,“Thriller”,“Horror”]中(其中t代表true):

SELECT '["Fiction", "Thriller", "Horror"]'::jsonb @> '["Fiction", "Horror"]'::jsonb; t

然而,相反的是["Fiction", "Thriller", "Horror"]包含在["Fiction", "Horror"]中,是错误的:

SELECT '["Fiction", "Horror"]'::jsonb @> '["Fiction", "Thriller", "Horror"]'::jsonb;

使用这个原则,我们可以轻松检查单一书籍类型:

SELECT data->'title' FROM books WHERE data->'genres' @> '["Fiction"]'::jsonb;
"Sleeping Beauties"
"Siddhartha"

通过传递一个数组(注意它们的关键顺序根本不重要),或者同时使用多个类型:

SELECT data->'title' FROM books WHERE data->'genres' @> '["Fiction", "Horror"]'::jsonb;
"Sleeping Beauties"

此外,从9.5版开始,PostgreSQL引入了检查顶级键和空对象包含的功能:

SELECT '{"book": {"title": "War and Peace"}}'::jsonb @> '{"book": {}}'::jsonb;

检查存在

作为包含的变体,jsonb还有一个存在运算符(?),可用于查找是否存在对象键或数组元素。

在这里,让我们计算出输入作者字段的书籍:

SELECT COUNT(*) FROM books WHERE data ? 'authors';

在这种情况下只有一个(“独裁者的手册”):

count