代码之家  ›  专栏  ›  技术社区  ›  niaher

慢速更新vs慢速选择

  •  2
  • niaher  · 技术社区  · 15 年前

    这是一个关于权衡的问题。

    想象一下一个社交网络。每个用户都有一条状态消息,可以随时更改。每当他改变它,他所有的朋友都会通过一堵墙得到通知(就像在Facebook上一样)。

    为了使这项工作顺利进行。我们有3个表用户(id,name),朋友列表(userid,frienduserid),通知(?).

    现在假设每个用户的朋友列表中都有大约50个朋友。我面临的难题是如何实现通知表。


    第一选择

    CREATE TABLE Notifications
    (
    toUserId bigint NOT NULL,
    [identity] bigint IDENTITY(1,1) NOT NULL,
    fromUserId bigint NOT NULL,
    data varchar(256) NOT NULL,
    CONSTRAINT [PK_Notifications] PRIMARY KEY CLUSTERED (toUserId, [identity])
    )
    

    发送通知:

    -- Get all friends of @fromUserId.
    WITH Friends AS
       (SELECT FriendLists.friendUserId
     FROM FriendLists
     WHERE userId = @fromUserId)
    -- Send updates to all friends.
    SELECT
     friendUserId as toUserId,
     @fromUserId as fromUserId,
     @data as data
    INTO Notifications
    FROM Friends
    

    在这种情况下,对于每个状态更改,我们创建50个记录(假设50个朋友)。这很糟糕。然而,好的一点是,要检索特定用户的通知,它确实很快,因为我们在touserid上有一个聚集索引。

    第二选择

    CREATE TABLE Notifications
    (
    toUserId bigint NOT NULL,
    [identity] bigint IDENTITY(1,1) NOT NULL,
    fromUserId bigint NOT NULL,
    data varchar(256) NOT NULL,
    CONSTRAINT [PK_Notifications] PRIMARY KEY CLUSTERED ([identity])
    )
    CREATE NONCLUSTERED INDEX [IX_toUserId] ON Notifications (toUserId ASC)
    

    发送通知:

    -- Get all friends of @fromUserId.
    WITH Friends AS
       (SELECT FriendLists.friendUserId
     FROM FriendLists
     WHERE userId = @fromUserId)
    -- Send updates to all friends.
    INSERT INTO Notifications(toUserId, fromUserId, data)
        VALUES(friendUserId, @fromUserId, @data)
    

    在这里,每个状态更新只插入一条记录。这很好。缺点是通知的检索速度会变慢,因为记录不是由ToUserID聚集的。


    正在获取通知 两种方法相同:

    SELECT TOP(50) fromUserId, [identity], data
    FROM Notifications
    WHERE toUserId  = @toUserId
    

    你对这个有什么看法?

    3 回复  |  直到 15 年前
        1
  •  3
  •   Remus Rusanu    15 年前

    首先,与写作相比,阅读总是势不可挡,因为每一面“墙”被看到的次数要比它被更新的次数多得多。所以你最好快点读。

    第二,这些大型社交网站固有的问题之一是数据的分发(共享、分区,没有一个数据库能够存储所有帐户、所有朋友、所有通知),这意味着当一个新的通知挂在墙上时,必须通知朋友 其他 服务器。这意味着更新是异步的,并且无论如何都是基于消息传递的。

    所以我肯定会选择一个为阅读而优化的结构。

    我建议你浏览一下Facebook和MySpace等网站架构中的各种人所做的公开演讲,比如 this Christa Stelzmuller's one . 他们在设计中解释了很多思考和推理。

        2
  •  1
  •   Beep beep    15 年前

    与选择相比,更新速度非常慢…几个数量级。另外,随着站点规模的扩大,您将在内存中缓存所有提取,因此选择的速度将是微不足道的。

        3
  •  1
  •   Josh    15 年前

    在这种情况下,在(Touser,Identity)上创建聚集索引似乎是一个坏主意,因为聚集索引确实应该按升序插入。当然,SQL将负责保持表的排序,但这会带来很高的性能成本(这是您的问题所在),但一般来说,对于聚集索引,不建议提前知道没有特定顺序的插入。这是一个非常好的 three part article 关于聚集索引建议。

    我已经说过了,我将继续使用identity列作为聚集索引,并在touserid上创建一个非聚集索引,或者创建一个datetime列。通过包含日期时间列,可以更有效地查询最近的数据。

    对于缓慢的更新,社交网站上的状态更新是消息队列的理想情况。这样,您就可以根据需要对数据库进行调优,以加快读取速度,并且如果数据库对写入性能有影响,那么用户就不必忍受这种影响。从他们的角度来看,更新是即时的,即使可能需要一些时间来“坚持”。

    对于非常大的数据库,我将遵从SQL专家的意见,他们可以讨论分区策略(对于较新的数据,较小的更易于管理的表,对于较旧的数据,较大的/重索引的表)和复制解决方案。