未來幾周,一個新的開源對象關系數據庫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."
},
...
]
},
...
]