Cozo 事务型关系型数据库开源项目

我要开发同款
匿名用户2023年01月06日
61阅读
开发技术Rust
所属分类数据库服务器、数据库相关
授权协议MPL

作品详情

Cozo是事务型关系型数据库,使用Datalog作为查询语言的高性能·关系型·可嵌入式·图数据库。

一个可嵌入的数据库;一个使用Datalog作为查询语句的数据库;一个专注于图数据、图算法的数据库;一个可进行历史穿梭查询的数据库;一个支持高性能、高并发的数据库。“可嵌入”是什么意思?

如果某个数据库能在不联网的手机上使用,那它大概就是嵌入式的。举例来说,SQLite是嵌入式的,而MySQL、Postgres、Oracle等不是(它们是客户端—服务器(CS)架构的数据库)。

如果数据库与你的主程序在同一进程中运行,那么它就是嵌入式数据库。与此相对,在使用客户端—服务器架构的数据库时,主程序需要通过特定的接口(通常是网络接口)访问数据库,而数据库也可能运行在另一台机器或独立的集群上。嵌入式数据库使用简单,资源占用少,并可以在更广泛的环境中使用。

Cozo同时也支持以客户端—服务器模式运行。因此,Cozo是一个可嵌入而不是仅仅是嵌入式的数据库。在客户端—服务器模式下,Cozo可以更充分地发挥服务器的性能。

“图数据”有什么用?

从本质上来说,数据一定是相互关联、自关联的,而这种关联的数学表达便是图(也叫网络)。只有考虑这些关联,才能更深入地洞察数据背后的逻辑。

大多数现有的图数据库强制要求按照属性图(propertygraph)的范式存储数据。与此相对,Cozo使用传统的关系数据模型。关系数据模型有存储逻辑简单、功能强劲等优点,并且处理图数据也毫无问题。更重要的是,数据的洞察常常需要挖掘隐含的关联,而关系数据模型作为关系代数(relationalalgebra)可以很好地处理此类问题。比较而言,因为其不构成一个代数,属性图模型仅仅能够将显性的图关系作为图数据处理,可组合性很弱。

“Datalog”好在哪儿?

Datalog1977年便出现了,它可表达所有的关系型查询,而它与SQL比起来的优势在于其对递归的表达。由于执行逻辑不同,Datalog对于递归的运行,通常比相应的SQL查询更快。Datalog的可组合性、模块性都很优秀,使用它,你可以逐层、清晰地表达所需的查询。

递归对于图查询尤其重要。Cozo使用的Datalog方言叫做CozoScript,其允许在一定条件下混合使用聚合查询与递归,从而进一步增强了Datalog的表达能力。同时,Cozo内置了图分析中常用的一些算法(如PageRank等),调用简单。

对Datalog有进一步了解以后,你会发现Datalog的规则类似于编程语言中的函数。规则的一大特点是其可组合性:将一个查询分解为多个渐进的规则可使查询更清晰、易维护,且不会有效率上的损失。与此相对的,复杂的SQL查询语句通常表达为多层嵌套的“select-from-where”,可读性、可维护性都不高。

历史穿梭?

在数据库中,“历史穿梭”的意思是记录数据的一切变化,以允许针对某一时刻的数据进行执行查询,用来窥探历史。

在某种意义上,这使数据库成为不可变数据库,因为没有数据会被真正删除。

每一项额外的功能都有其代价。如果不使用某个功能,理想的状态是不必为这个功能的代价埋单。在Cozo中,不是所有数据表都自动支持历史穿梭,这就把是否需要此功能、是否愿意支付代价的选择权交到了用户手里。

这个关于历史穿梭的小故事可能启发出一些历史穿梭的应用场景。

“高性能、高并发”,有多高?

我们在一台2020年的MacMini上,使用RocksDB持久性存储引擎(Cozo支持多种存储引擎)做了性能测试:

对一个有160万行的表进行查询:读、写、改的混合事务性查询可达到每秒10万次,而只读查询可达到每秒25万次。在此过程中,数据库使用的内存峰值仅为50MB。备份数据的速度为每秒约100万行,恢复速度为每秒约40万行。备份、恢复的速度不随表单数据增长而变慢。分析查询:扫描一个有160万行的表大约需要1秒(根据具体查询语句大约有上下2倍以内的差异)。查询所需时间与查询所涉及的行数大致成比例,而内存使用主要决定于返回集合的大小。对于一个有160万个顶点,3100万条边的图数据表,“两跳”图查询(如查询某人的朋友的朋友都有谁)可在1毫秒内完成。Pagerank算法速度:1万个顶点,12万条边:50毫秒以内;10个万顶点,170万条边:1秒以内;160万个顶点,3100万条边:30秒以内。

更多的细节参见此文。

学习Cozo

你得先安装一个数据库才能开始学,对吧?不一定:Cozo是“嵌入式”的,所以我们直接把它通过WASM嵌入到浏览器里了!打开这个页面,然后:

Cozo入门教程

当然也可以一步到位:先翻到后面了解如何在熟悉的环境里安装原生Cozo数据库,再开始学习。

一些示例

通过以下示例,可在正式开始学习之前对Cozo的查询先有个感性认识。

假设有个表,名为*route,含有两列,名为fr和to,其中数据为机场代码(如FRA是法兰克福机场的代码),且每行数据表示一个飞行航线。

从FRA可以不转机到达多少个机场:

?[count_unique(to)]:=*route{fr:'FRA',to}count_unique(to)310

从FRA出发,转机一次,可以到达多少个机场:

?[count_unique(to)]:=*route{fr:'FRA',to:'stop},*route{fr:stop,to}count_unique(to)2222

从FRA出发,转机任意次,可以到达多少个机场:

reachable[to]:=*route{fr:'FRA',to}reachable[to]:=reachable[stop],*route{fr:stop,to}?[count_unique(to)]:=reachable[to]count_unique(to)3462

从FRA出发,按所需的最少转机次数排序,到达哪两个机场需要最多的转机次数:

shortest_paths[to,shortest(path)]:=*route{fr:'FRA',to},path=['FRA',to]shortest_paths[to,shortest(path)]:=shortest_paths[stop,prev_path],*route{fr:stop,to},path=append(prev_path,to)?[to,path,p_len]:=shortest_paths[to,path],p_len=length(path):order-p_len:limit2topathp_lenYPO["FRA","YYZ","YTS","YMO","YFA","ZKE","YAT","YPO"]8BVI["FRA","AUH","BNE","ISA","BQL","BEU","BVI"]7

FRA和YPO这两个机场之间最短的路径以及其实际飞行里程是多少:

start[]<-[['FRA']]end[]<-[['YPO]]?[src,dst,distance,path]<~ShortestPathDijkstra(*route[],start[],end[])srcdstdistancepathFRAYPO4544.0["FRA","YUL","YVO","YKQ","YMO","YFA","ZKE","YAT","YPO"]

当查询语句有错时,Cozo会提供明确有用的错误信息:

?[x,Y]:=x=1,y=x+1eval::unbound_symb_in_head×Symbol'Y'inruleheadisunbound╭────1│?[x,Y]:=x=1,y=x+1·─╰────help:Notethatsymbolsoccurringonlyinnegatedpositionsarenotconsideredbound

更多信息请参见软件网址及文档。

声明:本文仅代表作者观点,不代表本站立场。如果侵犯到您的合法权益,请联系我们删除侵权资源!如果遇到资源链接失效,请您通过评论或工单的方式通知管理员。未经允许,不得转载,本站所有资源文章禁止商业使用运营!
下载安装【程序员客栈】APP
实时对接需求、及时收发消息、丰富的开放项目需求、随时随地查看项目状态

评论