使用图数据库而不是SQL的好处
#sql #database #memgraph #graphdatabase

与传统关系数据库相比,图形数据库提供了一种更快,更直观的方式来建模和查询数据。在previous article中,我们讨论了图数据库和关系数据库,它们的优势和劣势以及哪些数据库类型最适合某些用例。

选择合适的工具来加速发展,确保灵活性以及随着应用程序的发展而避免操作复杂性。在本教程中,您将完成构建简单的客户订单管理应用程序的过程,以了解图形方法如何与SQL One相比,以数据建模,查询和开发灵活性进行比较。一旦获得了图形数据库的操作,请随时检查Memgraph compares to Neo4j的方式,以帮助您选择满足需求的最佳图形分析平台。

先决条件

要跟随,您需要安装:

  • Memgraph Platform:一个图形应用程序平台,其中包括ATABASE,命令行界面 mgConsole ,这是一种用于导入数据,开发,调试和配置文件数据库查询并可视化查询结果 memgraph Lab <实验<>的集成开发环境< /strong>和 mage - 图形算法和模块库。

要安装MEMGRAPH平台并进行设置,请按照Installation指南上的 Docker安装说明。

用SQL建模数据

要使用 sql实施建模您的客户订单管理系统,您将需要三个表: Customer Purchase Product 。您还应该预先为他们定义一个结构,在更复杂的现实世界中,这将意味着与您的团队进行大量会议和讨论,以弄清CustomerPurchaseProduct的样子。

完成此操作后,下一步就是连接您的桌子。许多客户有很多购买,这意味着一张多到多的桌子。这不是问题,许多ORM会为您处理。您将需要另一个多到许多桌子来连接购买和产品。包含ProductPurchase可以具有数量。同样,这不是问题,您只需要揭露自己的多个人,并添加一个字段即可。

在这一点上,您有一个架构,将其部署到数据库中,对您喜欢的ORM了解了更多信息,并诅咒了几次,因为它不是100%直观的,并且正在推出它。太好了!

SQL schema

用图建模数据

使用图形对客户订单管理系统进行建模要快得多。首先,诸如Memgraph之类的属性图数据库,没有严格的模式,因此您不必预先定义它。您需要区分客户,
购买和产品。这些将是带有适当标签的图表中的节点(顶点)。然后,您需要将它们连接并在这些连接上添加属性,这也很简单。

您可以处理多一对吗?当然,图数据库支持快速任意关系。让我们制作一个布局图。它不像关系模式那样明确,但在语义上更清晰,而且通常更直观。

Graph schema

让我们写一些 cypher 查询,以便您可以填充数据库。与SQL不同,实际上您必须事先定义数据库模式,在图形数据库中,插入本身定义了结构。我们不会详细解释查询,请随时查看我们的quick-start on Cypher。让我们从创建CustomerProduct节点开始:

CREATE (customer:Customer {name: "John Doe", id: 0}) 
CREATE (product:Product {brand: "Shooz", name: "X3D Ultra Turbo Pro 3.1", 
                         cost: 399.95, id: 0})
RETURN customer.id, product.id;

注意您尚未添加所有属性。您不需要预先了解整个数据结构,就可以根据需要进行修改。如果您在数据库前构建某种Web API,则输入查询甚至不必预定义。以下是创建Query Generator在Python中的外观(略有不同的Cypher语法以促进属性图的生成):

def make_create_query(id, label, properties):
    """
    Generates a Cypher query for creating a node with the given
    label and properties. Properties map strings (names) to primitive
    values (ints, floats, bools, strings).
    """

    return "CREATE (node:{1} {{{2}}}) " \
        "SET node.id = {0} RETURN id".format(
        id, label, ", ".join("{}: {!r}".format(*kv) for kv in properties.items()))

使用这样的功能,您可以轻松地创建带有从HTTP URL提取的属性的节点。如果您的CustomerProduct实体的结构更改,则不必修改DB模式或后端服务器(好的,您可能需要验证您获得的键值对)。零停机时间重新部署归结为切换到新的前端。很酷!

现在让我们处理创建订单并将客户与产品联系起来。假设您有有关Customer是谁以及他们的Purchase的信息(由应用程序的前端处理),则Cypher查询看起来像这样:

MATCH (customer:Customer {id: 42}),
    (p1:Product {id: 123}),
    (p2:Product {id: 654}),
    (p3:Product {id: 12})

CREATE  (customer)-[:PURCHASED]->(purchase:Purchase {id: 0}),
    (purchase)-[:HAS {quantity: 3}]->(p1),
    (purchase)-[:HAS {quantity: 5}]->(p2),
    (purchase)-[:HAS {quantity: 1}]->(p3);

这很简单。首先,您是MATCH已知的CustomerProducts,然后创建与它们连接的Purchase。动态生成查询(购买中不同产品的数量各不相同)。

您可以看到,数据模型的密码实现简单易扩展。使用SQL,肯定还有更多的工作要做。有五个表可以使用其完整的模式来定义。如果要避免编写RAW SQL,则还需要知道并依靠第三方工具。就我个人而言,我使用了一些关系Orms(最多的Sqlalchemy),尽管它们的帮助仍然并不是很有趣。

用Cypher查询数据

一旦开始获取一些数据,您显然需要将其删除。假设您想将所有产品购买特定客户购买。在SQL中写入这将是不愉快的,因为它需要五个连接。一个好的ORM工具可能会使它大大较小,更易于处理。使用Cypher非常容易:

MATCH (:Customer {id: 42})-[:PURCHASED]->(purchase)-[purchase_product:HAS]->(product)
RETURN purchase.year, purchase_product.quantity, product.id;

毕竟,Cypher语言是为了轻松查询连接的数据而设计的。将查询扩展到包含任意过滤器可以通过应用程序后端中的查询生成来完成,类似于创建查询。虽然不是完全琐碎的,但这是可行的。公平地说,SQL ORM可能应该使其变得更容易。这是一个普遍且简单的要求,可以很好地处理。

现在,假设您想在给定的时间段内找到十个支出的客户(为了简单起见,假设一个日历年)。 SQL查询再次很麻烦,并且ORM会有所帮助。使用Cypher,它是快速而直观的:

MATCH (customer:Customer)-[:PURCHASED]->(purchase {year: 2017})-[purchase_product:HAS]->(product)
WITH customer, sum(purchase_product.quantity * product.price) AS yearly_expenditure
ORDER BY yearly_expenditure DESC
RETURN customer, yearly_expenditure LIMIT 10;

瞧!您只是写了它,绝对没有加入。

重构数据模型

数据库模式在整个系统的一生中很少保持不变。假设,由于业务扩展,您商店中提供的各种产品突然大大增加了。现在,您应该存储许多具有大量不兼容属性的产品类型(跑鞋和手机都是产品)。

使用SQL数据库,有几种解决此问题的方法,但它们都不令人愉快。 ORM通常尝试将OOP类层次结构映射到数据库列,但是基本的存储限制使其变得更加挑剔。同样,需要预先定义数据结构,这意味着许多迭代性讨论,重构,测试和重新部署。当然,这一切都需要使用零停机时间来完成。即使在玩具示例上,这种对关系数据库的干预既不是微不足道也不是快速执行的。在现实世界的情况下,它的尖头避免了(迅速获得“技术债务”标签)。

使用Memgraph之类的属性图,它很简单。每个节点都可以具有任意数量的标签,因此产品同时也可以是特定类型。它甚至可以属于多种类型,这一切都归结为添加标签。这是完成的方式:

MATCH (product:Product) WHERE product.brand in ["Nike", "Adidas"] 
SET product:TrainerShoe;

有一些查询,您可以标记产品,使其易于区分。查询可能很简单,可以在生产数据库上执行,但是您应该先在克隆上进行测试。由于没有严格的模式,因此系统可以自然地与业务逻辑一起发展。

随着系统的增长,您可能需要将许多其他重构引入系统。您可能需要将产品相互关联,介绍一个子概念,并引入新领域。这些大多数在图中比在关系数据库中更容易处理。

就个人而言,我相信它们的所有将更容易处理。属性图非常灵活,无论是用于数据结构还是连接。与关系数据库相比,许多概念更容易自然表达。我认为,在不断发展的系统时,属性图会击败关系。

图数据库性能

随着时间的流逝,系统中活动数据的数量将增加。该量表可能会变化很大,除了最大的(想想Google)量表外,您可能会期望现代数据库能够提供良好的可预测性能。因此,让我们测试哪种规模可以备忘录。我们正在使用强的笔记本电脑(带有16GB RAM的4个核心i7)运行。

创建数据集

这里有一些密码查询,可以帮助您设置模拟数据集。首先,您需要创建一些索引:

CREATE INDEX ON :Customer(id)
CREATE INDEX ON :Product(id)

接下来,保持简单,让我们以相同名称创建一万个客户:

UNWIND range(0, 9999) AS ignored
CREATE (:Customer {id: counter('Customer.id', 0), name: "John Doe"})

然后,让我们创建1万种产品。让我们添加随机价格,以便您可以进行分析查询:

UNWIND range(0, 9999) AS ignored
CREATE (:Product {id: counter('Product.id', 0), 
                  name: "Awesome product", 
                  price: rand() * 100})

最后,让我们从随机客户到随机产品生成1万000。为了保持简单,每次购买包含三个随机数量的产品:

UNWIND range(0, 99999) AS ignored
WITH tointeger(rand() * 10000) AS c_id,
    tointeger(rand() * 10000) AS p1_id,
    tointeger(rand() * 10000) AS p2_id,
    tointeger(rand() * 10000) AS p3_id
MATCH (customer:Customer {id: c_id}),
      (p1:Product {id: p1_id}),
      (p2:Product {id: p2_id}),
      (p3:Product {id: p3_id})
CREATE  (purchase:Purchase {id: counter('Purchase.id', 0),
                            year: 2000 + tointeger(rand() * 18)}),
    (customer)-[:PURCHASED]->(purchase),
    (purchase)-[:HAS {quantity: 1 + tointeger(rand() * 5)}]->(p1),
    (purchase)-[:HAS {quantity: 1 + tointeger(rand() * 5)}]->(p2),
    (purchase)-[:HAS {quantity: 1 + tointeger(rand() * 5)}]->(p3);

几秒钟后,您的模拟数据集准备好进行测试。它包含 1.2万节点 40万关系。 Memgraph正在使用 350MB的RAM ,这不是问题。让我们看看查找客户购买的查询如何。

MATCH (:Customer {id: 42})-[:PURCHASED]->(purchase)-[purchase_product:HAS]->(product)
RETURN purchase.year, purchase_product.quantity, product.id;

0.1毫秒

该分析查询如何获得去年的前10个支出?

MATCH (customer:Customer)-[:PURCHASED]->(purchase {year: 2017})-[purchase_product:HAS]->(product)
WITH customer, sum(purchase_product.quantity * product.price) AS yearly_expenditure
ORDER BY yearly_expenditure DESC
RETURN customer, yearly_expenditure LIMIT 10;

这个围绕 106毫秒。对于在事务数据库上执行的分析查询来说还不错。当存在平行插入时,衡量性能也很重要,但这是另一个时间的任务。

我们还尝试更大的规模。这意味着 1万客户 1万产品。这已经是大生意了。让我们将它们与百万美元购买(每天几乎每天购买三年)。在给定量表上生成新数据集需要更长的时间(大约一分钟)。设置后,Memgraph使用 2GB的RAM 。获取客户购买的所有产品仍然只需几毫秒。 2017年前10名支出的分析查询量 782毫秒,这也是合理的。

请注意,上面的所有查询在MEMGRAPH中的单个线程中执行,并且测试机在满载时远不及。另外,别忘了测试机是一台个人笔记本电脑,在后台运行其他东西。

结论:臭名昭著的图形用例

在这篇文章中,您了解了图数据库如何帮助您与传统的SQL方法相比,以更快,更直观的方式建模,查询和扩展数据。

您经常遇到问题:什么是图形数据库的用例?

通常,您开始考虑跨越大量连接(关系,边缘)的查询或找到从点A点到B的最短路径。我相信这是一种非常有限和简单的心态。

如果问题是:您需要轻巧,快速,可扩展,最初灵活且对重构友好型解决方案?

答案:谁不?

图形数据库are used by almost every industry as well,因此找出如何在自己的中使用它,以及如何与emgraph进行与neo4j进行比较,以找到一个完美的工具来处理您的数据。

Read more about Neo4j and Memgraph on memgraph.com