我一直在寻找这个问题的答案,但是还没有找到令人满意的解决方案。
我正在尝试为Web应用程序存储动态无向图结构。它应该在彼此之间存储用户“订阅”,这可能会经常发生变化。
传统的数据库解决方案毫无意义。 每个用户的JSON文件似乎也不是最佳解决方案,原因与数据库解决方案不理想的原因相同。
为我的问题找到最佳解决方案的其他想法?
提前致谢!
答案 0 :(得分:2)
您所谈论的结构类型在关系数据库中最有意义(我认为这是“传统”的意思)。因为您在用户之间有订阅,所以这是一种关系,因此,关系数据库最有意义。关系数据库允许不同表之间的显式连接。
文档数据库(即包含JSON文档的数据库)对于此类数据来说是个非常糟糕的主意。文档数据库在某些方面可以非常好,但是高度相互依赖的数据(如某种订阅系统)是对文档数据库的不良使用。我们会继续解释。
你说你的图形边缘是无向的,但是你称之为“订阅”的事实告诉我它们实际上是有针对性的:一个用户订阅另一个用户。如果它是无向的,那就更像是在facebook上与某人建立联系或在LinkedIn上联系:如果我是你的朋友,那么你必须成为我的朋友。在订阅系统(例如Google+或Twitter)中,即使我订阅了您,您也不一定需要订阅我。如果我们彼此订阅,那么它实际上是两个有针对性的边缘:一个来自你,另一个来自你。
因此,最佳解决方案是至少有两个表:主要的“users
”表和次要的“subscriptions
”表。 “users
”表格中包含uid
,name
,email
等列。 “subscriptions
”表格只有两列:subscriber
和subscription
。两者都保留“uid
”表中的users
值,并且每对值在表格中必须是唯一的。
你问过这么多订阅是否会“膨胀”。首先,您假设您将成为下一个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数据库也倾向于更好地扩展,包括垂直(即具有更多用户记录)和水平(即具有更多副本服务器)。