接下来的基准测试将对不同的对象关系映射(ORM)工具以及查询构建器的每秒操作数进行衡量。所有相关代码和说明都可在此仓库中找到。
这些测量数据是在 2023 年 6 月获取的。由于处于活跃维护状态的库会持续更新,所以请将这些基准测试结果仅看作近似值,并且可能已经过时。
参与此次测试的对象包括:
- Orchid ORM,版本为 v1.11.0
- Prisma,版本为 v4.15.0
- Sequelize,版本为 v6.32.0
- Knex,版本为 v2.4.2
- Kysely,版本为 v0.25.0
若要对 Drizzle ORM 进行比较,可以查看这些基准测试。
每个 ORM 的连接池大小都设置为 10。基准测试会运行 10 个并行查询,持续时间为 10 秒,以此来计算平均每秒操作数(ops/s)指标。
测试结果来自于我的笔记本电脑,其配置为 Intel Core i7 10 代 U 系列处理器,16GB 内存,操作系统为 Manjaro Linux。
为了确保在不同的 ORM 之间,数据库端没有缓存数据,每个 ORM 都使用单独的数据库进行测试。
从单表加载所有记录
此测试衡量从包含 10 列和 100 条记录的表中执行简单全量查询的性能。
ops/s(每秒操作数):数值越高表示性能越好。
加载嵌套关系记录
在这项测试中,我们构建了一个包含多层关联的数据模型:
- 创建 30 个用户
- 每个用户关联 1 个帖子(帖子属于用户)
- 每个帖子关联 5 个标签(通过中间表实现多对多关联)
- 每个帖子关联 10 条评论(评论同时属于帖子和用户)
测试目标是获取所有帖子及其完整关联数据:包括帖子作者、标签列表、最新 10 条评论及评论作者信息。
Orchid ORM 实现示例
await db.post
.select('id', 'title', 'description', {
author: (q) => q.author.select('id', 'firstName', 'lastName'),
tags: (q) => q.postTags.pluck('tagName'),
lastComments: (q) =>
q.comments
.select('id', 'text', {
author: (q) => q.author.select('id', 'firstName', 'lastName'),
})
.order({ createdAt: 'DESC' })
.limit(10),
})
.order({ createdAt: 'DESC' });
各 ORM 实现差异说明
- Orchid ORM:采用单 SQL 查询方案,利用
JOIN LATERAL语法高效加载所有嵌套数据 - Prisma:通过多个独立查询加载各层关联数据,在本地测试环境中网络延迟影响较小
- Sequelize:使用
UNION ALL语句合并数据,查询效率较低 - Kysely/Knex:由于实现复杂度较高,未参与此项测试
性能指标:ops/s(每秒操作数)值越高表示处理复杂嵌套查询的性能越好
简单插入测试
此测试衡量向数据库插入一条包含 7 列数据的单条记录的性能。
ops/s(每秒操作数):数值越高表示插入性能越好。
嵌套插入测试
此测试衡量向数据库插入一条包含复杂关联关系的数据的性能,具体包括:
- 插入一个帖子(Post)
- 同时为该帖子关联 3 条评论(Comments)
- 同时为该帖子关联 5 个标签(Tags,通过中间表实现多对多关联)
const tagNames = ['one', 'two', 'three', 'four', 'five'];
const postData = {
userId: 1,
title: 'Post title',
description: 'Post description',
};
const commentData = {
userId: 1,
text: 'Comment text',
};
await db.post.insert({
...postData,
comments: {
create: [commentData, commentData, commentData],
},
postTags: {
create: tagNames.map((tagName) => ({ tagName })),
},
});
各 ORM 支持情况说明
- Orchid ORM:支持通过单条语句完成嵌套对象的级联插入
- Prisma:唯一参与此项测试的其他 ORM,支持类似的嵌套插入语法
- Sequelize:由于实现复杂度高,未参与此项测试
- Kysely/Knex:作为查询构建器,不提供关系管理功能,未参与此项测试
为何 Orchid ORM 性能更优
Orchid ORM 进行了诸多细微的优化,并且为达成目标执行的查询数量更少。
Prisma 依托于 Rust 服务器,node.js 与 Rust 服务器之间的通信效率较低,而且在加载关联关系时会使用独立的查询。不过,在过去的几个月里,它已得到了显著的优化。
Sequelize 在尝试加载关联关系时,会生成庞大且低效的 SQL 查询。
像 Sequelize、TypeORM、MikroORM 等一些 ORM 会执行映射到类实例的操作:首先从数据库中加载数据,然后利用这些数据来构造类实例,并在实例化过程中执行某些逻辑。随后,在将数据编码为 JSON 以进行响应之前,类需要转换回简单的 JavaScript 对象。
现状与局限性
尽管已经编写了数千个测试,但仍可能存在一些漏洞。若您遇到问题,请提交问题报告。
目前,除了 PostgreSQL 之外,暂无计划支持其他数据库。PostgreSQL 是一个功能极为丰富的数据库,具备众多扩展,几乎能够满足所有需求。例如,PostGIS 地理信息扩展;可作为 ElasticSearch 替代品的扩展;可替代 SQS、RabbitMQ、BullMQ 等工具的消息队列扩展,以及更多其他功能。相较于耗费数月时间去支持功能有限的数据库,更令人期待的是进一步深入利用 PostgreSQL 生态系统,让这款对象关系映射(ORM)工具变得更加强大。
您可以在连接多个数据库的应用程序中使用 Orchid ORM,但它无法管理跨数据库的表关系。
在执行 create(创建)、update(更新)、delete(删除)操作的返回子句中,无法选择关联关系数据。
目前,已有支持各种实用扩展的计划正在推进中。不过,在这些扩展实现之前,若需使用相关功能,您需要借助原生 SQL 语句来完成。Orchid ORM 的设计允许您结合其现有功能,并在必要之处添加少量自定义 SQL 语句。
Orchid ORM 不支持范围数据库类型以及复合自定义类型。
对于 JSON 列的搜索功能,目前仅提供了一些非常基础的方法,后续还需要进一步完善。