NoSQL文档存储可能是管理大量非结构化数据的理想选择。然而,一些组织在处理非结构化数据时,仍然希望获得传统SQL数据库的功能。例如,媒体或新闻内容机构可能运行以大量文本和图像内容为中心的高流量网站。尽管他们需要存储这些非结构化数据,但他们也许并不真正需要NoSQL数据库所具有的灵活模式或水平扩展性。相反,他们需要像PostgreSQL这样的关系型数据库所带来的数据库管理的便利性和一致性。
是否有可能得到这两个世界的最好结果?是的。
由于其数据类型旨在支持非结构化数据,PostgreSQL提供了一个快乐的媒介,使你能够在一个具有成本效益和简单管理的关系数据库中利用NoSQL功能。在这篇文章中,我们将看看你如何使用PostgreSQL中的HStore和JSONB数据类型来处理非结构化数据。
在我们深入研究之前,让我们简单看看SQL和NoSQL数据库的主要区别。
了解SQL与NoSQL
SQL和NoSQL数据库都有其独特的优势和劣势。就哪种数据库最能满足你的数据需求做出明智的决定,取决于对其差异的深刻理解。
SQL(关系型)数据库,如PostgreSQL和MySQL,在表、行和列中以清晰和可预测的结构表示数据。它们坚持ACID属性(原子性、一致性、隔离性和耐久性),通过确保数据库事务得到可靠的处理,为数据的完整性打下了坚实的基础。
SQL数据库在数据一致性和完整性至关重要的地方大放异彩,例如在处理复杂的查询和交易系统(如金融应用)时。
相比之下,NoSQL数据库(文档存储)迎合了大型和不同的数据集,不一定适合用表格表示。NoSQL数据库的例子包括MongoDB、Cassandra和Couchbase。NoSQL数据库使用灵活的模式,允许数据结构随着时间的推移而不断发展。它们还支持横向可扩展性,将数据分布在多个服务器上,以改善对大型数据负载和高流量的处理。
NoSQL数据库经常被用于对可扩展性至关重要的应用中,如处理实时应用中的大量数据或大型语言模型(LLMs)。在处理不同的和不断发展的数据结构时,NoSQL数据库也是有益的,因为它们允许组织在其数据需求发生变化时进行调整。
为什么你可能会使用PostgreSQL作为文件存储?
PostgreSQL是一个关系型数据库,所以把它作为满足NoSQL需求的一个选项似乎不符合常规。然而,你的情况可能有充分的理由使用PostgreSQL作为一个文档存储。
如果你的数据存储需求是多样化的--既需要结构化的、符合ACID标准的数据存储,又需要灵活的、无模式的文档存储--那么你可以利用PostgreSQL来结合关系型和非关系型模型。或者,也许你想要某些NoSQL功能,但也想要ACID属性带来的数据一致性保证。最后,作为一个拥有活跃社区的成熟技术,PostgreSQL带来了全面的SQL支持、高级索引和全文搜索。这些功能与它的NoSQL功能相结合,使PostgreSQL成为一个多功能的数据存储解决方案。
使用PostgreSQL处理NoSQL式数据的局限性
尽管PostgreSQL具有多功能性,但与传统的NoSQL数据库相比,它有一定的限制。虽然PostgreSQL可以纵向扩展,但它本身并不支持横向扩展或自动分片的分布式数据,而这些功能是NoSQL数据库通常提供的。PostgreSQL也没有为某些NoSQL数据结构提供优化,比如宽列存储或图形数据库。最后,PostgreSQL不提供用于优化性能的可调整一致性,而你可能从一些NoSQL数据库中得到这种一致性。
当你考虑将PostgreSQL用于大型非结构化数据集时,要知道这些限制可能会影响性能和你的扩展能力。此外,混合SQL和NoSQL的数据操作会引入复杂性。仔细规划和了解这两种范式将帮助你避免潜在的陷阱。
然而,有了正确的理解和使用案例,PostgreSQL可以作为一个强大的工具,提供SQL和NoSQL世界的最佳功能。
PostgreSQL中的HStore和JSONB
当我们考虑使用PostgreSQL作为NoSQL解决方案的可能性时,我们遇到了三种提供类似NoSQL功能的数据类型,但它们各自有独特的特点和使用情况。
- 储存:这种数据类型允许你在一个单一的PostgreSQL值中存储键值对。它对于存储没有固定模式的半结构化数据很有用。
- JSONB:这是一个类似JSON数据的二进制表示。与HStore相比,它可以存储更复杂的结构并支持完整的JSON功能。JSONB是可索引的,使它成为大量数据的一个好选择。
- JSON:这与JSONB类似,尽管它缺乏JSONB的许多能力和效率。JSON数据类型存储的是输入文本的精确拷贝,其中包括空白和重复的键。
当你不需要JSONB提供的全部功能时,我们提到JSON数据类型是存储JSON格式数据的一个有效选择。然而,在本文的剩余部分,我们主要关注的是HStore和JSONB。
储存器
PostgreSQL文档描述HStore在你有 "有许多属性的行,很少被检查,或半结构化的数据 "时很有用。在你可以使用HStore数据类型之前,确保启用HStore扩展:
> CREATE EXTENSION hstore;
HStore表示为0个或更多的key => value,用逗号分隔。成对的顺序并不重要,也不能在输出时可靠地保留。
> SELECT 'foo => bar, prompt => "hello world", pi => 3.14'::hstore;
hstore
-----------------------------------------------------
"pi"=>"3.14", "foo"=>"bar", "prompt"=>"hello world"
(1 row)
每个HStore的键都是唯一的。如果一个HStore声明有重复的键,那么只有一个重复的键会被存储,而且不能保证是哪一个。
> SELECT 'key => value1, key => value2'::hstore;
hstore
-----------------
"key"=>"value1"
(1 row)
由于其扁平的键值结构,HStore提供了简单和快速的查询功能,使其成为简单场景的理想选择。然而,HStore只支持文本数据,不支持嵌套数据,这使得它在复杂的数据结构中受到限制。
另一方面,JSONB可以处理更多种类的数据类型。
JSONB
JSONB数据类型接受JSON格式的输入文本,然后以分解的二进制格式存储。虽然这种转换使输入稍慢,但结果是快速处理和有效的索引。JSONB不保留白色空间或对象键的顺序。
> SELECT '{"foo": "bar", "pi": 3.14, "nested": { "prompt": "hello", "count": 5 } }'::jsonb;
jsonb
-----------------------------------------------------------------------
{"pi": 3.14, "foo": "bar", "nested": {"count": 5, "prompt": "hello"}}
(1 row)
如果给出了重复的对象键,则保留最后的值。
> SELECT '{"key": "value1", "key": "value2"}'::jsonb;
jsonb
-------------------
{"key": "value2"}
(1 row)
因为JSONB支持复杂的结构和完整的JSON功能,它是复杂或嵌套数据的理想选择,比HStore或JSON更适合。然而,与HStore相比,使用JSONB会带来一些性能开销和增加存储用量。
实际例子:使用HStore和JSONB工作
让我们考虑一些实际的例子来演示如何使用这些数据类型。我们将看一下创建表、基本查询和操作以及索引。
基本的HS存储操作
就像对待其他数据类型一样,你可以在PostgreSQL的数据表中定义字段为HStore数据类型。
> CREATE TABLE articles ( id serial primary key, title varchar(64), meta hstore );
插入一条带有HStore属性的记录看起来像这样:
> INSERT INTO articles (title, meta)
VALUES (
'Data Types in PostgreSQL',
'format => blog, length => 1350, language => English, license => "Creative Commons"');
> SELECT * FROM articles;
id | title | meta ----+--------------------------+------------------------------------------ 1 | Data Types in PostgreSQL | "format"=>"blog", "length"=>"1350", "license"=>"Creative Commons", "language"=>"English"(1 row)
通过HStore字段,你可以从字段中获取由你提供的键指定的特定键值对:
> SELECT title, meta -> 'license' AS license, meta -> 'format' AS format FROM articles;
title | license | format
---------------------------------+------------------+------------
Data Types in PostgreSQL | Creative Commons | blog
Advanced Querying in PostgreSQL | None | blog
Scaling PostgreSQL | MIT | blog
PostgreSQL Fundamentals | Creative Commons | whitepaper
(4 rows)
你也可以根据HStore字段内的特定值用标准来查询。
> SELECT id, title FROM articles WHERE meta -> 'license' = 'Creative Commons';
id | title
----+--------------------------
1 | Data Types in PostgreSQL
4 | PostgreSQL Fundamentals
(2 rows)
有时你可能只想查询那些在HStore字段中包含特定键的记录。例如,下面的查询只返回元HStore包含备注键的记录。要做到这一点,你需要使用"?"操作符。
> SELECT title, meta->'note' AS note FROM articles WHERE meta ? 'note';
title | note
---------------------------------+-----------------
PostgreSQL Fundamentals | hold for review
Advanced Querying in PostgreSQL | needs edit
(2 rows)
在这里可以找到一个有用的HStore操作符和函数的列表。例如,你可以将HStore的键提取为数组,或者你可以将HStore转换为JSON表示。
> SELECT title, akeys(meta) FROM articles where id=1;
title | akeys
--------------------------+----------------------------------
Data Types in PostgreSQL | {format,length,license,language}
(1 row)
> SELECT title, hstore_to_json(meta) FROM articles where id=1;
title | hstore_to_json
--------------------------+------------------------------------------------
Data Types in PostgreSQL | {"format": "blog", "length": "1350", "license": "Creative Commons", "language": "English"}
(1 row)
基本的JSONB操作
在PostgreSQL中使用JSONB数据类型是很简单的。表的创建和记录的插入看起来像这样:
> CREATE TABLE authors (id serial primary key, name varchar(64), meta jsonb);
> INSERT INTO authors (name, meta) VALUES ('Adam Anderson', '{ "active":true, "expertise": ["databases", "data science"], "country": "UK" }');
注意jsonb meta字段是以JSON格式的文本字符串提供的。如果你提供的值不是一个有效的JSON,PostgreSQL会抱怨。
> INSERT INTO authors (name, meta) VALUES ('Barbara Brandini', '{ "this is not valid JSON" }');
ERROR: invalid input syntax for type json
与HStore类型不同,JSONB支持嵌套数据。
> INSERT INTO authors (name, meta) VALUES ('Barbara Brandini', '{ "active":true, "expertise": ["AI/ML"], "country": "CAN", "contact": { "email": "barbara@example.com", "phone": "111-222-3333" } }');
与HStore类似,JSONB字段可以被部分检索,只用某些键。比如说:
> SELECT name, meta -> 'country' AS country FROM authors;
name | country ------------------+--------- Adam Anderson | "UK" Barbara Brandini | "CAN" Charles Cooper | "UK"(3 rows)
JSONB数据类型有许多操作符,其用法与HStore类似。例如,下面使用? 操作符只检索那些元字段包含联系人关键字的记录。
> SELECT name, meta -> 'active' AS active, meta -> 'contact' AS contact FROM authors WHERE meta ? 'contact';
name | active | contact
------------------+--------+-----------------------------------------------
Barbara Brandini | true | {"email": "barbara@example.com", "phone": "111-222-3333"}
Charles Cooper | false | {"email": "charles@example.com"}
(2 rows)
使用索引的工作
根据文档,HStore数据类型 "具有GiST和GIN索引,支持@>、?、?&和?|操作符"。关于这两种类型的索引的区别的详细解释,请看这里。JSONB的索引使用GIN索引来促进键或键值对的有效搜索。
创建索引的语句就像人们所期望的那样:
> CREATE INDEX idx_hstore ON articles USING GIN(meta);
> CREATE INDEX idx_jsonb ON authors USING GIN(meta);
具有NoSQL灵活性的SQL结构
让我们重温一下我们在介绍中提到的原始用例。想象一下,一个新闻内容机构以与NoSQL文档存储相同的方式来存储其文章。也许文章可以用JSON表示为代表章节的对象的有序数组,每个对象都有文本内容、注释和格式。此外,每篇文章都有大量的元数据,而这些元数据属性在不同的文章中是不一致的。
上述描述概括了该组织的大部分NoSQL需求,但关于它如何管理和组织其数据的其他一切都与关系型数据模型密切相关。
通过将JSONB这样的数据类型的NoSQL能力与PostgreSQL的传统SQL优势相结合,组织可以享受灵活的模式和在嵌套数据中的快速查询,同时仍然能够执行联合操作和执行数据关系。PostgreSQL的HStore和JSONB数据类型为那些需要关系型数据库的结构但又需要NoSQL风格的数据存储的开发者提供了强大的选择。
规模化的PostgreSQL
你是否希望在传统关系型数据库的框架内支持NoSQL风格的数据存储和查询?也许你的组织处理文档的方式与我们在这篇文章中描述的类似。或者,你正在寻找处理非结构化数据的选项,用于大型语言模型(LLM)或其他一些AI/ML工作。
LinodeMarketplace 中的 PostgreSQL 群集为您提供了 SQL 数据库的关系模型和结构,以及 NoSQL 数据库的水平可扩展性。结合使用 HStore 或 JSONB 数据类型,您就拥有了一个理想的混合解决方案,可以在 PostgreSQL 中利用 NoSQL 功能。
注释