导读:找出哪种数据模型(RDBMS 或文档)更适合大型、复杂与生成式 AI 的工作负载。
各位朋友,生成式人工智能 (GenAI) 应用程序使用数据的方式与传统的在线事务处理 (OLTP) 工作负载是不一样的。
后者以小块的形式处理数据,通常以行和表的形式,并且数据结构相对简单。大语言模型 (LLM)训练需要更丰富的文档结构或二进制对象,其大小可能为几千字节甚至几兆字节。
检索增强生成 (RAG)是一种广泛应用于 GenAI 应用程序的数据检索技术,它利用复杂的数据结构来构建提示语并实时生成响应。
此外,与任何其他类型的现代工作负载相比,人工智能驱动的工作负载更需要底层数据库系统能够快速高效地处理针对此类数据的查询。
传统关系数据库管理系统 (RDBMS)数据库平台未针对此类工作负载进行过设计。它们所基于的存储引擎在行大小方面假设存在一致性,并且它们在使用具有类型属性的记录行时运行效率最高。
使用 RDBMS 的最佳实践包括最小化表中的属性数量、使用垂直分区以及保持行数较小以及避免行外存储。这也适用于在 RDBMS 中使用JSON属性类型,这允许开发者使用可在复杂查询中引用的灵活架构对象来扩展其关系表的行。
为了让它们保持相关性,一种新的基于文档和对象的 NoSQL 替代方案已经发展起来,更能够满足这一需求。
在开源 RDBMS PostgreSQL中,标准实践指导是尽量减少值中包含 JSON 或 JSONB 属性的数量,也可以使用稀疏填充对象。
除了数据模型的差异之外,对于 GenAI 的工作负载而言, MongoDB等文档数据库相对于 PostgreSQL 等 RDBMS 平台的最大优势之一在于底层设计。
MongoDB 建立在存储引擎之上,该引擎旨在处理大小各异(从几个字节到几兆字节)丰富且复杂的文档。
当今 OLTP 工作负载越来越频繁地让小型、一致行设计的 RDBMS 平台对大型数据对象采用行外存储。但是行外存储技术(如 PostgreSQL 的TOAST)可能会带来性能瓶颈,因为查询无法再从表的行中访问所需的数据,从而导致从大型对象存储层进行二次检索。
另外,MongoDB 利用一个称为 BSON 的二进制文档结构来存储和传输强类型属性数据,这些数据由服务器处理,并以相同的格式存储在磁盘上,从而无需到服务器端解析。
RDBMS 数据库(例如 PostgreSQL)将复杂的 JSON 传输为文本格式。这要求服务器端解析写入内容,至少验证 JSON 格式或(在 JSONB 反序列化的情况下)属性值,以便它们可以在需要计算值的服务器端查询中有效使用。
在输出过程中,数据必须先序列化为 JSON 文本,然后才能传输到客户端。
我们将在下面的基准测试中,展示这一对大型文档的开销,虽然巨大但是可衡量的过程。
PostgreSQL v16.2 MongoDB v7.0.8
我们开始测量单线程性能,以便更好地了解客户端和服务器之间的协议开销。
如前所述,MongoDB 专门使用 BSON 来减少这种开销,即无需序列化和反序列化数据。线程化或运行多个客户端会掩盖这种开销,而无需测量整个系统的整体资源利用率。
对于写入工作负载,我们将 10,000 个文档插入到单个表或集合中,具有以下配置和有效负载大小:
具有 10、200、1000、2000 和 4000 字节有效载荷的单一属性。
多属性,具有 10 个 1 字节、50 个 4 字节、100 个 10 字节、100 个 20 字节和 100 个 40 字节属性。
其中,PostgreSQL 服务器使用以下参数进行配置:
所有表均创建为0记录表。
设置shared_buffers=8GB。
设置synchronous_commit=off。
设置 wal_buffers=48MB。
MongoDB 服务器是默认安装,未应用任何调整。
我们进行的第一个测试是插入 10,000 个单属性文档,同时测量有效负载从 10B 增加到 ~4KB 时完成所需的总时间(以毫秒为单位)。
下图显示了原始数据与结果图表。
10K 记录插入(单一属性有效载荷) | |||
MongoDB | Postgres(JSONB) | Postgres(JSON) | |
n=1,s=10 | 773 | 399 | 331 |
n=1,s=200 | 789 | 2184 | 969 |
n=1,s=1000 | 750 | 8393 | 4071 |
n=1,s=2000 | 850 | 16387 | 7944 |
n=1,s=4000 | 829 | 31705 | 15767 |
当涉及大型文档时,数据非常清楚:PostgreSQL 的性能与 MongoDB 相当,直到文档大小开始增长到几百字节以上,一旦 TOAST 在 2KB 左右启动,情况就会变得“糟糕”。
另一方面,毫不奇怪,PostgreSQL 在处理其设计用于处理的工作负载类型(处理小块数据)方面相当有竞争力。然而,随着有效负载的大小增加,处理开销很快就会变得明显。PostgreSQL 仅验证 JSON 属性的格式,而 JSONB 还会解析属性值。
在本测试中可以看到 JSONB 产生的额外开销。MongoDB BSON 在所有有效负载上的表现都远远优于 JSON 和 JSONB,并且曲线非常平坦。
10K 物品插入(多属性有效载荷) | |||
MongoDB | Postgres(JSONB) | Postgres(JSON) | |
n=10,s=10 | 531 | 415 | 322 |
n=10,s=200 | 569 | 2040 | 793 |
n=50,s=1000 | 641 | 9504 | 4419 |
n=100,s=2000 | 812 | 19213 | 9181 |
n=200,s=4000 | 1085 | 37278 | 17460 |
主要之结论为,随着属性数量的增加,JSONB 的解析开销会更高。
MongoDB BSON 和 PostgreSQL JSON 在单属性和多属性测试中都保持了相对平稳的性能。
BSON 没有任何解析开销,而 JSON 只是验证格式,因此在插入复杂文档时,两者都不需要做太多工作。MongoDB BSON 仍然是明显的赢家,而 JSONB 则要落后很多。
数组索引查询测试 | |||
MongoDB | Postgres(JSONB) | Postgres(JSON) | |
n=10,s=10 | 3587 | 19933 | 18749 |
n=10,s=200 | 3810 | 23619 | 23946 |
n=50,s=1000 | 4741 | 27760 | 21311 |
n=100,s=2000 | 6023 | 36701 | 23264 |
n=200,s=4000 | 8352 | 53808 | 27789 |
我们看到,JSON 和 JSONB 的序列化相关开销都很高。MongoDB 显然受益于 BSON 不需要序列化的事实。对于 PostgreSQL,没有测量与 JSON 文本反序列化相关的开销,因为文本在代码中会被丢弃。
即使文档较小,MongoDB 也以较大地优势胜出。一个有趣的结论是 JSONB 也会在读取时产生影响,因为类型化属性值的序列化似乎会产生更多成本。
相比之下,尽管 JSONB 的读取速度更快,但如果文档中存储的数据值实际上并未在查询或处理的服务器端引用,那么最好使用 JSON。
作者:万能的大雄
参考:
https://db-engines.com/en/system/Cassandra%3BMongoDB%3BPostgreSQL
https://airbyte.com/data-engineering-resources/mongodb-vs-postgresql
本文为 @ 万能的大雄 创作并授权 21CTO 发布,未经许可,请勿转载。
内容授权事宜请您联系 webmaster@21cto.com或关注 21CTO 公众号。
该文观点仅代表作者本人,21CTO 平台仅提供信息存储空间服务。