【问题标题】:Save dynamic graph structure for a web application保存 Web 应用程序的动态图结构
【发布时间】:2016-03-17 21:17:44
【问题描述】:

我一直在寻找这个问题的答案,但还没有找到令人满意的解决方案。

我正在尝试为 Web 应用程序存储动态无向图结构。它应该在彼此之间存储用户“订阅”,这可以非常频繁地更改。

传统的数据库解决方案毫无意义。 每个用户的 JSON 文件似乎也不是最好的解决方案,这与数据库解决方案并不理想的原因相同。

对于我的问题的最佳解决方案还有其他想法吗?

提前致谢!

【问题讨论】:

  • 此图的结构可能会有所帮助,您可以将 json 存储在数据库中
  • 本身没有任何基于图形的数据库,但根据您所描述的,关系数据库应该可以正常工作(我假设“关系”是您所说的“传统” ”)。有一个具有唯一用户的表,另一个表有两个外键指向第一个表上的用户 ID。这些是您的“订阅”或图形边缘。确保第二个表在所有列中都具有唯一性约束。至于构建图表,这将需要在您的应用程序代码中。但数据本身可以轻松融入关系模型。
  • 我不太明白你的意思,两个外键指向第一个表上的用户 ID。既然每个用户都可以有大量的订阅者,那么第二个表不会膨胀吗?
  • @eestrada 你能详细说明你的意思吗?第二个表中的列是否应该在每一行中都有一个列表?
  • @MoneyIsAMotivation 我很高兴扩展我的意思。我想我会在实际答案中这样做,因为评论可能不允许我需要的格式类型。此外,它将包含足够的信息,我相信它可以作为答案。

标签: javascript php mysql database web-applications


【解决方案1】:

您所说的结构类型在关系数据库中最有意义(我假设您所说的“传统”是指这种结构)。因为您有用户之间的订阅,所以这是一种关系,因此,关系数据库最有意义。关系数据库允许不同表之间的显式连接。

文档数据库(即保存 JSON 文档的数据库)对于这种类型的数据来说是一个非常糟糕的主意。文档数据库可以非常擅长某些事情,但是高度相互依赖的数据(例如某种订阅系统)是文档数据库的不良用途。我会在我们继续进行时解释更多。

您说您的图边是无向的,但您称它们为“订阅”这一事实告诉我它们实际上是有向的:一个用户订阅另一个用户。如果它是无方向的,它更像是在 Facebook 上加好友或在 LinkedIn 上连接:如果我和你是朋友,那么你必须和我成为朋友。在订阅系统(比如 Google+ 或 Twitter)中,即使我订阅了你,你也不一定需要订阅我。如果我们都订阅了对方,那么它实际上是两条有向边:一条是我给你的,一条是你给我的。

因此,最好的解决方案是至少有两个表:一个主“users”表和一个辅助“subscriptions”表。 “users”表将包含 uidnameemail 等列。 “subscriptions”表只有两列:subscribersubscription。两者都包含来自“users”表的uid 值,并且每对值在表中必须是唯一的。

你问过这么多订阅是否会“膨胀”。首先,您假设您将成为下一个 facebook,并且需要与数百万或数十亿用户打交道。不用担心,至少一开始不会有这个问题。其次,大多数关系数据库都是logarithmic in their performance 用于检索和插入记录,随着用户数量的增加,它可以很好地扩展。对于您期望从文档数据库或磁盘上的 JSON 文件获得的行为类型,您的行为将是linear time complexity,因为您需要遍历数据库中的每个文档确保您已检查所有订阅(线性行为比对数更糟糕),否则您将需要在所有记录中复制订阅者/订阅信息。第二种解决方案确实会变得臃肿,因为您正在复制大量数据,更重要的是,它冒着很容易失去同步的巨大风险。在这种情况下,不同步比您想象的要容易得多。

为了向您展示如何执行此操作,我将使用sqlite3 dialect of SQL。这是我最常做的原型,所以我最熟悉它。将其转换为 MySQL 或 PostgreSQL 之类的东西应该相当简单。以下是创建数据库的语句:

# since `uid` is the primary key, just pass it a
# null value on insertion and the database will 
# generate a unique integer and use that automatically.
# it might also be good to make more than just the uid unique,
# such as their email.
CREATE TABLE users (uid INTEGER PRIMARY KEY,
                    name TEXT,
                    email TEXT);

# we will use the uid for the foreign key reference since this should
# never change, even if the user changes their name or email.
CREATE TABLE subrs (subscriber INTEGER,
                    subscription INTEGER,

                    # make sure each entry of pairs is unique
                    CONSTRAINT uc_edges UNIQUE (subscriber,subscription),

                    # be sure subscribers can only be created for users that exist
                    CONSTRAINT fk_subr FOREIGN KEY (subscriber) REFERENCES users(uid),

                    # be sure subscription can only be created for users that exist
                    CONSTRAINT fk_subee FOREIGN KEY (subscription) REFERENCES users(uid)
                   ); 

这通常有一个很好的附加好处,即您无法删除订阅了他们的用户,除非您首先删除这些订阅。根据您选择的数据库 YMMV,请查看您选择的数据库的文档。几乎所有 SQL 数据库还支持使用外键的行为,即您无法使用尚不存在的外键值创建记录。使用 JSON 文件或文档数据库,很容易留下悬空订阅或让用户删除需要很长时间,因为您需要修改引用给定用户的每个用户文档。关系 SQL 数据库可以简化很多原本可以在代码中完成的事情。在您的应用程序代码中处理此逻辑会为您的数据处理带来更多错误和错误的机会。一点建议:您可以卸载到数据库的任何工作,您应该卸载到您的数据库。专业数据库比您的代码经过更好的测试,并且已经为您可能想要对数据执行的许多常见类型的操作提供了逻辑。

要查找用户的订阅,您可以执行如下查询:

SELECT * FROM subrs WHERE subscriber=some_uid;

要获取给定用户的所有订阅者,查询同样简单:

SELECT * FROM subrs WHERE subscription=some_uid;

删除用户记录只需大约三行:

DELETE FROM subrs WHERE subscription=some_uid;
DELETE FROM subrs WHERE subscriber=some_uid;
DELETE FROM users WHERE uid=some_uid;

在文档数据库中,您将有更多的应用程序代码来执行非常相似的操作,并且您的应用程序代码可能存在逻辑不良和破坏数据的风险。

TL;DR

使用关系 SQL 数据库。您可以在记录之间创建显式关系。因此,它不会像使用文档数据库那样容易让自己陷入困境(因为所有关系都只是隐含的)。像 MySQL 这样的 SQL 数据库也倾向于更好地扩展,无论是纵向(即具有更多用户记录)还是横向(即具有更多副本服务器)。

【讨论】:

  • 太棒了!我确信 JSON 的想法会一事无成,但是您对如何使用关系数据库实现它的解释确实帮助我了解了该怎么做。非常感谢您的帮助!
  • @MoneyIsAMotivation 没问题。我很高兴我能帮上忙:)。祝你的应用好运。
  • 我想问的最后一个问题;第二个表不会有如下值:user_1 -> user_x, user_1 -> user_y, user_1 -> user_z?即每个订阅的行条目?
  • @MoneyIsAMotivation 是的,这是正确的。每行将仅用于单个订阅。因此,当您运行 SELECT * FROM subrs WHERE subscriber=some_uid; 时,您会得到一组 (subscriber, subscription) 对。相反,您可以像这样查询:SELECT subscription FROM subrs WHERE subscriber=some_uid;,您将只取回订阅数组。无论如何,这可能会更好,因为它减少了 IO。
猜你喜欢
  • 2012-06-11
  • 1970-01-01
  • 2012-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-05
  • 2012-07-03
相关资源
最近更新 更多