【问题标题】:Would denormalizing my database in this particular way help performance?以这种特殊方式对我的数据库进行非规范化会有助于提高性能吗?
【发布时间】:2012-01-07 10:32:26
【问题描述】:

在我目前正在开发的网站上,会员可以收藏其他会员。然后,当成员进入他们的收藏页面时,他们可以看到他们一直收藏的所有成员。

我可以通过两种方式解决这个问题:

方法一:

每次用户收藏另一个用户时,我都会在favorites 表中输入一行,如下所示(索引为user_favoriting_id):

id | user_favorited_id | user_favoriting_id
-------------------------------------------

然后当他们加载“我的收藏夹”页面时,我在收藏夹表上进行选择以查找 user_favoriting_id 值等于当前登录用户的所有行。然后我使用 user_favorited_ids 构建单个 SELECT 语句并从单独的用户表中查找相应的用户。

方法#2:

每次用户收藏另一个用户时,我都会更新用户表中他们所在行的收藏字段,如下所示(尽管字段更多,索引是 id):

id | username | password | email | account_status | timestamp | favorites
--------------------------------------------------------------------------

我将在 favorites 字段中连接被收藏的用户的 ID,以便该列包含一个逗号分隔的字符串,如下所示:

10,44,67 等等...

然后像方法 #1 一样生成我的收藏夹页面,我将通过一次选择来获取所有收藏的用户。那部分是一样的。

我知道方法 #1 是标准化的方法,而且更漂亮。但我对这个特定项目的关注是可扩展性和性能高于一切。

如果我选择方法#2,它将减少在单独的收藏夹表中查找的次数,因为用户一旦登录就必须选择用户表。

而且我很确定在方法 #2 中使用 php 的explode 函数来拆分这些 CSV 值不会像方法 #1 在favorites 表上查找额外的数据库所花费的时间一样多,但以防万一我必须问:

从纯粹的性能角度来看,这些方法中哪一种更优化?

还请假设该网站每天将获得一万亿次页面浏览量。

【问题讨论】:

  • 在方法 2 中进行查找会非常慢(您必须解析出每个 ID 并单独查找),并且您不能对其进行普通连接,因此任何类型的报告可能已经结束了。所以您只想显示 ID,而不是对收藏夹进行有意义的描述?
  • 同意@Robert;但除此之外,很难看出“谁在喜欢我?”
  • @Robert Harvey 实际上我正在考虑构建一个类似WHERE id = $split_value 的字符串,并将其放在我正在通过爆炸的 CSV 数组运行的 foreach 循环中。这样我就可以将它附加到我将要构建的 SELECT sql 的末尾。这样,SQL 是在 php 中构建的,并且在执行之前是一个查询。
  • 我会采用第一种方法。它更简单、更快、更好的实践,如果您仍然需要,构建 ID 的 CSV 列表也相对简单。

标签: php mysql sql database denormalization


【解决方案1】:

加入需要时间,但在您有一些数据表明这是必要的之前,我不会进行更改。

归一化有很多好处;这不仅仅是一项学术活动。

将 ID 串联成一列是对规范化的令人发指的罪行。不要这样做。

您假设您的代码比为优化关系数据库所做的所有工作都要快。这是一个很大的错误。

确保您在参与 JOIN 的主键和外键上有索引。

当您遇到真正的性能问题时,分析您的应用;不要猜。

确保真正的问题不是您的应用。带回过多不必要的信息将比规范化架构更拖累性能。

【讨论】:

    【解决方案2】:

    从数据规范化、可维护性和数据完整性的角度来看,同时使用两者,一个(规范化方法)更可取,(以及出于其他原因) - 您应该始终强烈支持这种方法。

    但是,如果规范化方法对于读取性能来说是不可接受的,那么也没有理由不使用其他方法。通常,另一种非规范化方法会更好地提高读取性能。因此,使用第一个作为“主”来跟踪数据并确保数据完整性,然后在另一个结构中保留数据的非规范化“副本”以进行读取访问......从主更新副本任何时候它改变,(插入更新,删除)。

    但是衡量替代方法的性能,以确保它确实更快,并且有足够的余量证明其使用是合理的。

    【讨论】:

      【解决方案3】:

      您说可扩展性是一个问题。这似乎意味着方法 #2 对您不起作用,因为这限制了用户可以拥有的收藏夹的数量。 (例如,如果您有 100 万用户,那么大多数用户的 ID 都是五位数。您想让favorites 有多宽?如果是VARCHAR(1000),这意味着允许的收藏数少于 200。 )

      另外,您真的希望自己永远想知道哪些用户“收藏”了给定用户吗?您的方法 #2 可能没问题。如果你知道你会总是通过“favoriter”而不是“favoritee”来查找收藏夹,但否则它会完全崩溃。 (即使在这里,只有在您不希望查找除他/她的用户 ID 之外的“收藏夹”任何有意义的信息时才有意义;否则,如果您实际上查找“收藏夹”,那么您基本上是在做JOIN 的所有辛勤工作,只是消除了 MySQL 智能地执行 JOIN 的任何机会。)

      总体而言,最好从规范化等最佳实践开始,并仅在性能需要时才放弃它们。否则,看起来像是性能优化的东西可能会产生负面影响,迫使您编写非常不最优化的代码。

      【讨论】:

        【解决方案4】:

        据我所知,在 mysql 中使用 dernomalization 真的很简单。但是,如果您使用的不是 RDBMS,而是像 couchdb 或 mongoDB 这样的数据库,那么整个引擎就会以安全的方式处理数据。而且它真正可扩展,非关系型数据库将真正更快地为您工作..

        例如,对于优化使用 mysql 的 webapp 来说,唯一喜欢的方法是对表进行 dernomalize 然后给 php 一些工作,当然使用 HipHop 你会在那里得到一些非常大的优化,因为你卸载了 mysql 并加载了使用 HipHop 的 php 将被优化高达 50%!

        【讨论】:

          【解决方案5】:

          可能不会,但由于其他人已经引用的原因,它会完全搞砸您的数据库。

          请勿使用逗号分隔的 ID 列表模式。真是太糟糕了。

          我强烈怀疑您的网站上没有足够的用户来解决这个问题,因为除非您是 Facebook,否则您不太可能拥有超过 100 万的用户。这 100 万用户中的大多数不会选择任何人作为他们的最爱(因为大多数是不使用该功能的临时用户)。

          因此,您正在查看一个只有两列的非常小的表(例如,如果您的 1M 用户平均有 1 个最喜欢的用户,则最多 1M 行,尽管大多数人根本不使用该功能)。您可以通过使主键从您最常想要搜索的东西开始来潜在地改进 innodb 中的扫描,但是 - 得到这个 - 您仍然可以在另一个上添加二级索引,并获得合理的查找时间(实际上,非常快,因为该表将适合最小服务器上的内存!)

          【讨论】:

            猜你喜欢
            • 2011-01-21
            • 2011-02-15
            • 2012-03-12
            • 1970-01-01
            • 2014-06-06
            • 2010-10-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多