超越PostgreSQL?下一代对象关系数据库EdgeDB
未来几周,一个新的开源对象关系数据库EdgeDB将发布其首个公开预览版。先让我们了解一下构建EdgeDB的原因。
动机
数据库一直是,并将永远是任何技术堆栈的决定性部分。在过去的十年中,数据库市场发生了很多变化。在10年前,没有MongoDB,没有云数据库,甚至云本身也是一个相对较新的概念。今天,有大量的数据库解决方案使用非关系数据模型,承诺使数据可扩展和对开发人员友好。
但显然后半部分变得难以捉摸。NoSQL数据库的文档使初始无模式原型设计阶段变得简单,但是随着时间的推移往往会增加技术负累。保持总体数据和模式一致性以及对无模式数据执行复杂分析要比传统RDBMS难得多。
如果有人为他们的新项目选择数据库解决方案,他们很可能会选择一个关系数据库。因为可预测的性能,强大的查询语言以及一致性保证是所有RDBMS在市场上占主导地位的原因,并且在很大程度上不受质疑。
然而,关系数据库是建立在一个数十年前的模型之上,并且越来越不适合迅速转变的软件开发领域。当然,PostgreSQL正在不断地进行改进,提供诸如高级JSON支持,查询并行化和JIT编译器等功能,以进一步提高复杂查询的性能。但是,应用程序与关系数据库的接口方式没有发生有意义的变化。我们仍然使用缓慢的ORM,与模式迁移较劲,并编写糟糕的临时SQL查询。
对象关系
大多数软件工程师不会考虑表格。我们的编程语言是围绕类型和对象等更高级别的抽象构建的。如果你有两个对象A和B,并且想引用另一个对象,那么你只需写Ab = B。为了在关系数据库中做同样的事情,你需要处理外键,连接,有时还需要处理中间表,本质上直接在低得多的抽象层次上工作。
关系数据库和现代编程语言之间的这种不一致已为人所知,甚至有一个名称:面向对象编程与关系型数据库间的不一致(object-relational impedance mismatch)。这就是ORM如此受欢迎的原因,以及为什么对GraphQL等技术有如此多的兴趣。这些解决方案的问题在于他们试图弥合与应用方面的差距,要么牺牲底层数据库的表现力,要么实施过度复杂的映射,这在很大程度上破坏了目的。虽然业界似乎痴迷于解决规模问题,但并没有认真尝试解决数据库方面的对象关系不匹配问题。
EdgeDB
EdgeDB是下一代对象关系数据库。它实现了一个对象图模型,而不是关系模型。在这个模型中,数据被描述和存储为强类型对象和关系,或者它们之间的链接。对象和链接可以保存属性:一组命名的标量值。这个模型有时被称为属性图模型。
EdgeDB不是图数据库:数据使用关系数据库技术存储和查询,并且需要严格的模式。
EdgeDB不是一个文档数据库,但插入,修改和查询层次化的文档类数据是微不足道的。
EdgeDB不是传统的对象数据库。尽管“object-relational”中有“object”这个词,但它不是OOP持久性或封装的实现。它具有表达式查询语言EdgeQL,其目标是匹配并超越现代SQL功能,如子查询,高级聚合和窗口函数。
EdgeDB基于PostgreSQL,并继承了它的所有优势:可靠性,ACID合规性和性能。
一个例子
为了让我们对EdgeDB有一个小小的了解,我们来定义一个简单的模式,用我们的模式DSL来描述一个基本的类似GitHub的应用程序:
# Define a string enumerated type for
# pull request status.
scalar type pr_status extending str:
constraint enum('Open', 'Closed', 'Merged')
# Pull request object type definition.
type PullRequest:
required property title -> str
required property status -> pr_status:
default := 'Open'
# Pull request "author" as a to-one
# link to a User object.
required link author -> User
# Many-to-many relationship with
# different User objects.
link assignees -> User:
cardinality := '**'
type User:
required property name -> str
link followees -> User:
cardinality := '**'
现在让我们来看看EdgeQL中的一个简单查询:
SELECT User {
id,
name,
followees: {
id,
name
}
}
FILTER
User.name = 'Alice';
查询将返回名称为“Alice”的所有用户以及她关注的所有用户的列表。数据可以以客户端编程语言中的丰富对象或JSON的形式返回。以下是可比较的SQL查询的样子:
SELECT
users.id,
users.name,
array_agg(followees.id) AS followee_ids,
array_agg(followees.name) AS followee_names
FROM
users
LEFT JOIN user_followees ON
user_followees.user_id = users.id
LEFT JOIN users AS followees ON
followees.id = user_followees.followee_id
WHERE
users.name = 'Alice'
GROUP BY
users.id, users.name;
请注意,此SQL查询效率不高。有经验的开发人员会重写它以使用子查询。另外,要将查询的结果作为JSON,需要更多的努力。
下面是一个稍微更高级的EdgeQL查询示例,它显示了聚合和反向链接导航。
SELECT User {
name,
# Count the number of users who follow this user
# by traversing the "followees" link *backwards*,
# as denoted by '.<'
followed_by_count := count(User.<followees),
# Count the number of users that this user is
# following by traversing the "followees" link
# *forwards*, as denoted by '.>' or simply '.'
follow_count := count(User.followees),
# Get a set of open pull requests that this
# user is the assignee of.
open_prs := User.<assignee[IS PullRequest] {
title
} FILTER .status = 'Open'
};
示例JSON输出:
[
{
"name": "Alice",
"followed_by_count": 101,
"follow_count": 45,
"open_prs": [
{
"title": "Implement support for window functions."
},
...
]
},
...
]