为什么 PostgreSQL 能代替 MongoDB?

news2024/9/23 23:27:48

在进行技术选型时,需要考虑众多因素,如功能、性能、可靠性、成本效益、社区支持和团队技术能力等,然而,影响最终决定的关键因素的往往是团队 Leader 的技术品味,这也能解释为什么阿里偏爱 Java,而字节跳动更倾向 Go、Rust 等新兴语言。技术本身无好坏之分,根据实际业务问题选择适当的技术方案是关键。

本文旨在为读者提供一种新的选择,而非论证 PostgreSQL 比 MongoDB 更优秀。

MongoDB 因其灵活的 "Schema-less"(无模式)特性而著名。"Schema-less" 意味着 MongoDB 不要求严格定义数据的结构和字段(使用 BSON 格式存储数据),允许在同一集合中存储具有不同结构的文档,这为开发人员提供了极大的灵活性,能够轻松适应数据模型的变化和演进。

PostgreSQL 提供的 JSONB 类型可用于存储和处理 JSON 数据,包括嵌套的对象、数组和基本数据类型。因此,PostgreSQL 具备 MongoDB 存储 document 的能力。

PostgreSQL 对 JSON 的支持

我们来了解一下 PostgreSQL 支持 JSON 特性的时间线(统计到版本 14):

- PG 9.2 Introduction of JSON (JSON text; no indexes) -- 2012/9/10
- PG 9.4 Introduction of JSONB (binary format; indexes) -- 2014/12/18
- PG 9.5 jsonb_set(), jsonb_object(), jsonb_build_object(), jsonb_build_array, jsonb_agg, || operator etc.
- PG 9.6 jsonb_insert()
- PG 10 Full text search support for JSONB
- PG 11 jsonb_plpython
- PG 12 json_path (like xpath in XML; part of SQL Standard 2016)
- PG 13 jsonpath.datetime()
- PG 14 JSONB subscripting can be used to extract and assign to portions of JSONB

可以看出,PostgreSQL 对 JSON 的支持虽晚于 MongoDB(MongoDB 1.0 在 2009年9月前后发布),但其后续的版本 JSON 相关的特性开发非常活跃,提供了强大而灵活的 JSON 处理能力。

PostgreSQL提供两种存储 JSON[1] 的数据类型:json 和 jsonb,二者接受几乎相同的输入值,主要区别在存取效率。json 存储插入文本的精确副本,处理函数在每次执行时必须重新解析;jsonb 则存储解析过的的二进制格式(类似 MongoDB 的 BSON),由于需要额外的转换开销,插入时稍慢一些,但因为无需重新解析,处理速度更胜一筹。另外 jsonb 还支持索引,这是一个重要的优势。

大多数场景我们都应该使用 jsonb,除非以下几种情况:

  • • 需要保留原始输入的格式,比如空格

  • • 需要保留键的顺序或冗余的键值

  • • 需要快速插入和读取而无需对数据进行处理

PostgreSQL 和 MongoDB 使用对比

在了解了 PostgreSQL 的 JSON 特性之后,我们来对比一下 PostgreSQL 和 MongoDB 使用上的区别。

我们将 MongoDB 的 _id 单独存为一个字段,其它字段存为 doc:

create table inventory(_id bigserial PRIMARY KEY , doc jsonb);

1. CRUD

MongoDB:

db.inventory.insertOne(
   { item: "canvas", qty: 100, tags: ["cotton"], size: { h: 28, w: 35.5, uom: "cm" } }
)

db.inventory.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
   { item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
   { item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])

db.inventory.find( { tags: ["red", "blank"] } )

db.inventory.find( { qty: { $gt: 25 } } )

db.inventory.find( { "tags": { $size: 2 } } )

db.inventory.find( { 'size.h': { $lte: 20 } } )

db.inventory.updateOne( { _id: 3 }, [ { $set: { "item": "notepad"} } ] )

db.inventory.deleteOne( { qty: { $gt: 90 } } )

PG:

insert into inventory(doc) values('{ "item": "canvas", "qty": 100, "tags": ["cotton"], "size": { "h": 28, "w": 35.5, "uom": "cm" } }');

insert into inventory(doc) values('{ "item": "journal", "qty": 25, "tags": ["blank", "red"], "size": { "h": 14, "w": 21, "uom": "cm" } }'),
                                  ('{ "item": "mat", "qty": 85, "tags": ["gray"], "size": { "h": 27.9, "w": 35.5, "uom": "cm" } }'),
                                  ('{ "item": "mousepad", "qty": 25, "tags": ["gel", "blue"], "size": { "h": 19, "w": 22.85, "uom": "cm" } }');

SELECT * FROM inventory WHERE doc->'tags' @> '["red", "blank"]'::jsonb;

SELECT * FROM inventory WHERE (doc->>'qty')::integer > 25;

SELECT * FROM inventory WHERE jsonb_array_length(doc->'tags') = 2;

SELECT * FROM inventory WHERE (doc->'size'->>'h')::float <= 20;

UPDATE inventory SET doc = jsonb_set(doc, '{item}', '"notepad"', true) WHERE _id = 3;

DELETE FROM inventory WHERE (doc->>'qty')::integer > 90 AND ctid IN (SELECT ctid FROM inventory LIMIT 1);
注意: 将全部数据存储在 PostgreSQL 的一个 jsonb 字段虽然可行,但 jsonb 没有状态统计数据,将固定的列抽成一个单独的列往往能获得更好的查询性能。

2. 索引

MongoDB 支持的索引在 PostgreSQL 中基本都支持,虽然通配符索引在 PostgreSQL 没看到类似的能力,但 PostgreSQL 的索引能力应该不逊于 MongoDB。

MongoDBPostgreSQL
Single Field indexesB-tree Index
Compound IndexesMulticolumn Indexes
Text IndexesGIN Indexes + tsvector
Wildcard Indexes
Geospatial IndexesPostgis geometry GiST Indexes
Hashed IndexesHash Indexes
BRIN Indexes

Can PostgreSQL with its JSONB column type replace MongoDB?[2] 👈🏻 这篇文章对 MongoDB 和 PostgreSQL 中的部分索引进行了性能对比。

3. sharding

MongoDB 使用 sharding(分片)来支持超大数据集和高吞吐量的集群部署,通过将数据集水平拆分为多个分片,并将每个分片分布在不同的服务器上。每个服务器(或分片)都可以独立处理一部分数据写入和查询负载,从而提高整个系统的扩展性和性能。其架构如下:

  • • shard: 每个 shard 可以部署为一个副本集(replica set),单个 shard 上包含多个分片

  • • mongos: 接收客户端的请求,并将请求路由到 shard

  • • config servers: 存储集群元数据和配置信息

MongoDB 在 collection 级别进行数据切分,使用 shard key(由文档中的一个或多个字段组成) 将 collection 切分为多个 chunks分布在集群中的各个分片上。

PostgreSQL 本身不支持分布式,但 Citus[3] 提供了 sharding 的能力,其组件几乎都能跟 MongoDB 对应上:

MongoDBCitus
数据存储节点shardworker
元数据存储节点config servercoordinator
请求接入节点mongoscoordinator
数据分布方法Hashed + RangedHash
分区级别collection levelschema level + table level
分片名称chunkshard

PostgreSQL + Citus 能够实现 MongoDB 分片的能力。不过 Citus 不支持使用多字段作为 shard key,且只支持 Hash 分布(代码仓库中有 RANGE_DISTRIBUTED,但实际并不支持)。

FerretDB

说到替换 MongoDB,不得不提一下 FerretDB[4],FerretDB 的目标是允许使用现有的关系型数据库来处理 MongoDB 的查询请求,它充当一个代理,接收来自应用程序的 MongoDB 查询,并将其转换为相应的 SQL 查询,然后将结果返回给应用程序。FerretDB 支持使用 PostgreSQL 或 SQLite 等关系型数据库作为后端存储引擎,其架构如下:

 

FerretDB 能够处理 MongoDB 大部分请求,但目前有些能力还不具备,如:

  • • $lookup aggregation pipeline[5]

  • • geospatial index[6]

  • • text indexes[7]

  • • sharding[8]

  • • ...

不过 FerretDB 的社区非常活跃,相信未来他们会将这些特性逐渐完善。

总结

相比 MongoDB 的 "Schema-less",PostgreSQL 确实需要事先定义好表结构,包括列名、数据类型和约束等,但 PostgreSQL 的 JSONB 类型提供了一种在关系型数据库中存储和查询 JSON 数据的灵活性。也正是这种表结构和 JSONB 类型相结合的能力,使得笔者认为 PostgreSQL 比 MongoDB 单纯的 "Schema-less" 更灵活、更强大。

虽然 Michael Stonebraker 早在 2005 年就指出 One size fits all[9] 是一个不切实际的观点,但数据库开发者从未停止探索的脚步,期望为用户提供一站式的解决方案。MongoDB 从最初的 NoSQL[10] 到后来支持了事务[11]、时序[12]等特性,PostgreSQL 生态也有时序(TimescaleDB[13])、图(AGE[14])、消息队列(PGMQ[15])相关的扩展。虽然在功能特性上有所重合,但不同的是,MongoDB 作为一个商业公司,其产品在易用性上会更胜一筹,而 PostgreSQL 则需要使用者不断去探索来发掘其无限可能。

最后,Michael Stonebraker 三篇关于 NoSQL 的博客:

  • • "Schema Later" Considered Harmful[16]: If you have data that will require a schema at some point, you are way better off doing the work up front to avoid data debt, because the cost of schema later is a lot higher.

  • • Comparison of JOINS: MongoDB vs. PostgreSQL[17]

  • • Those Who Forget the Past Are Doomed to Repeat It[18]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1285436.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

判断一个链表是否为回文结构

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 &#x1f324;️题目结构 给定一个…

Vue 理解MVVM设计模式

vue作者在最初设计vue的时候参考了mvvm这个模型。没有完全遵循mvvm&#xff0c;是在一定程度上遵循了这个模型。根据这个模型设计了vue里面的模型。 MVVM模型 1.M&#xff1a;模型(Model)&#xff1a;对应vue代码当中data中的数据&#xff0c;模型就是vue里面的数据 2.V&#x…

AT COMMAND(转载)

AT&#xff08;Attention&#xff09;指令是由 Dennis Hayes 发明的&#xff0c;所以也称为 Hayes command set。AT 指令最初是用来指导 modem 工作的&#xff0c;后面随着技术的发展&#xff0c;低速 modem 已经退出了市场&#xff0c;但 AT 指令却不断发展&#xff0c;并且在…

掌握Python异步IO利器:深入解读Asyncio

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 异步 IO 与 Asyncio 在 Python 中的应用 在当今互联网时代&#xff0c;处理大量并发请求或I/O密集型任务对于软件应用程序至关重要。Python的异步IO框架——Asyncio成为了处理此类问题的有力工具。本文将深入介…

吉客云与金蝶云星辰业财一体化数据集成

吉客云与金蝶云星辰业财一体化数据集成为例&#xff0c;探讨如何利用轻易云数据集成平台高效整合企业系统。金蝶云星辰在供应链和财务管理方面具有显著优势&#xff0c;而吉客云则专注于订单处理和发货。两者的协同运作&#xff0c;是企业数字化转型的典型案例。 二、集成总体蓝…

java:封装统一的响应体code、data、msg、paging

背景 我们在写接口的时候一般不会直接返回给前端数据&#xff0c;而是会有响应体&#xff0c;比如 code、data、msg&#xff0c;这样就有一个统一的结构方便前端处理&#xff0c;那么今天就来封装一个统一的响应体 封装基本响应体 1、在 config 包里新建 ApiResponse.java …

03、pytest初体验

官方实例 # content of test_sample.py def func(x):return x 1def test_ansewer():assert func(3) 5步骤解释 [100%]指的是所有测试用例的总体进度&#xff0c;完成后&#xff0c;pytest显示一个失败报告&#xff0c;因为func(3)没有返回5 注意&#xff1a;你可以使用ass…

smartkit巡检E9000设备

https://support.huawei.com/enterprise/zh/doc/EDOC1100325140/f6eeacd6 打开链接&#xff0c;里面的内容很详细。

2023年【道路运输企业主要负责人】最新解析及道路运输企业主要负责人模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 道路运输企业主要负责人最新解析是安全生产模拟考试一点通总题库中生成的一套道路运输企业主要负责人模拟考试&#xff0c;安全生产模拟考试一点通上道路运输企业主要负责人作业手机同步练习。2023年【道路运输企业主…

继上海车展后,英信翻译再进广州车展大显身手

第二十一届广州车展于2023年11月17日-26日在广州琶洲盛大举行 &#xff0c;历时十天的展会共吸引到场观众84.7万人次&#xff0c;举办了67场新闻发布会&#xff0c;近5000家海内外媒体机构的1.2万名媒体人员参与报道了展会盛况&#xff0c;再创历史新高。本届广州车展在国内外企…

学生信息管理系统

摘 要 学生成绩管理系统是典型的信息管理系统(MIS)&#xff0c;其开发主要包括后台数据库的建立和维护以及前端应用程序的开发两个方面。经过分析&#xff0c;我们使用Microsoft公司的C语言开发工具&#xff0c;将与C语言技术与数据库SQL2008相结合进行设计。首先&#xff0c;…

C# WPF上位机开发(图形显示软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在实际应用中&#xff0c;有一种情况就是&#xff0c;我们需要经常对数据进行图形化显示&#xff0c;这样会比较直观一点。比如经济统计里面的同比…

java每日一记 —— BeanFactory 与 ApplicationContext 的区别

BeanFactory 与 ApplicationContext 1.容器与 bean1.BeanFactory 接口2.ApplicationContext 接口 2.测试几个小案例 此篇代码在jdk11上正常运行&#xff0c;springBoot版本为2.7.14 1.容器与 bean 1.BeanFactory 接口 1.先看下源码&#xff1a; 2.从源码中的接口中可以得出…

Netty01-NIO与BIO

NIO 什么是NIO Java NIO 全称 java non-blocking IO&#xff0c;是指JDK 1.4 及以上版本提供的新API&#xff08;New IO&#xff09;。从 JDK1.4 开始&#xff0c;Java 提供了一系列改进的输入/输出的新特性&#xff0c;为所有的原始类型&#xff08;boolean类型除外&#xf…

亲测体验Go语言PGO

本文是对官方 Profile-guided optimization in Go 1.21[1] 的学习与实践. 对于PGO的思路,之前就有过类似的想法,有些许差异. 但本质都是通过对以往运行情况的"学习",优化以后程序的运行(有点以史为鉴和鉴于往事,资于治道的感觉) 过程很简单: 收集程序运行过程中的数据…

zabbix的自动发现机制:

zabbix的自动发现机制: zabbix客户端主动的和服务端联系&#xff0c;将自己的地址和端口发送给服务端&#xff0c;实现自动添加监控主机 客户端是主动的一方 缺点&#xff1b;如果自定义网段中主机数量太多&#xff0c;等级耗时会很久&#xff0c;而且这个自动发现机制不是很…

银河麒麟服务器部署Prometheus+Grafana

#年薪百万# 一、环境准备 操作系统&#xff1a;Operating System: Kylin Linux Advanced Server V10 (Sword) (X86-64) prometheus&#xff1a;prometheus-2.48.0.linux-amd64 grafana&#xff1a;grafana-enterprise-10.2.2.linux-amd64 node_exporter&#xff1a;node_expor…

个体诊所电子处方系统哪个好用,推荐一款可以自由设置配方模板能够填写病历可以查询历史病历的门诊处方笺软件

一、前言 1、功能实用&#xff0c;操作简单&#xff0c;不会电脑也会操作&#xff0c;软件免安装&#xff0c;已内置数据库。 2、中医西医均可用此软件开电子处方&#xff0c;支持病历记录查询&#xff0c;药品进出库管理&#xff0c;支持配方模板一键导入电子处方。 二、电子…

Anaconda创建虚拟环境以及Pycharm和Jupyter如何切换虚拟环境

文章目录 Anaconda创建管理虚拟环境0. 进入到终端1. 创建新环境2. 切换环境3. 删除环境4. 查询当前已有的环境 Pycharm切换虚拟环境0. 更换解析器1. 添加虚拟环境&#xff08;之前默认的是base环境&#xff09;2. 验证切换虚拟环境 Jupyter Notebook 切换虚拟环境1. 安装ipyker…

FPGA串口接收解帧、并逐帧发送有效数据——1

FPGA串口接收解帧、并逐帧发送有效数据 工程实现的功能&#xff1a;FPGA串口接收到串口调试助手发来的数据&#xff0c;将其数据解帧。判断到正确的帧头和帧尾之后&#xff0c;将有效数据存入rx_data中&#xff1b;另一方面发送端将有效数据逐帧发送出去。 参考&#xff1a;正…