哪个更高效:多个 MySQL 表还是一个大表?

Posted

技术标签:

【中文标题】哪个更高效:多个 MySQL 表还是一个大表?【英文标题】:Which is more efficient: Multiple MySQL tables or one large table? 【发布时间】:2010-11-10 15:12:08 【问题描述】:

我将各种用户详细信息存储在我的 mysql 数据库中。最初它是在各种表中设置的,这意味着数据与 UserIds 链接并通过有时复杂的调用输出以根据需要显示和操作数据。建立一个新系统,将所有这些表格组合成一个相关内容的大表格几乎是有意义的。

这是帮助还是阻碍? 调用、更新或搜索/操作时的速度注意事项?

这是我的一些表结构的示例:

users - UserId、用户名、电子邮件、加密密码、注册日期、ip user_details - cookie 数据、姓名、地址、联系方式、隶属关系、人口统计数据 user_activity - 贡献、最后在线、最后查看 user_settings - 个人资料显示设置 user_interests - 广告可定位变量 user_levels - 访问权限 user_stats - 命中、统计

编辑:到目前为止,我对所有答案都投了赞成票,它们都具有基本上回答我问题的元素。

大多数表具有 1:1 的关系,这是对它们进行非规范化的主要原因。

如果表格跨越 100 多列,而这些单元格的大部分可能保持为空,是否会出现问题?

【问题讨论】:

这个other question 可能也有帮助 【参考方案1】:

多个表格有以下帮助/案例:

(a) 如果不同的人要开发涉及不同表的应用程序,那么拆分它们是有意义的。

(b) 如果你想在数据收集的不同部分给不同的人不同的权限,拆分它们可能更方便。 (当然,您可以查看定义视图并适当地对其进行授权)。

(c) 为了将数据移动到不同的地方,尤其是在开发过程中,使用可以减小文件大小的表格可能是有意义的。

(d) 当您针对单个实体的特定数据收集开发应用程序时,较小的占用空间可能会给您带来舒适感。

(e) 有一种可能性:您认为的单值数据将来可能会变成真正的多值。例如到目前为止,信用额度是一个单值字段。但是明天,您可能决定将值更改为(日期从、日期到、信用值)。拆分表现在可能会派上用场。

我的投票将支持多个表 - 数据适当拆分。

祝你好运。

【讨论】:

@RohitKhatri :据我所知,在大多数情况下,拥有多个表会提高性能。 @HariHarker 感谢您的回答,但我发现这取决于您的访问模式。 直到最近我总是将所有数据存储在一个表中,但仔细想想,拆分数据在性能(当然取决于用例)、语义方面有很多优势(一些数据最好放在不同的表中)和发展。例如,我现在正在一个遗留系统之上开发一个定制的 ERP 系统。我不得不用额外的列来扩展旧的数据库表。我决定为新数据制作新表。一些新功能对遗留系统派上用场,现在我可以轻松集成它们,而无需重写太多旧查询【参考方案2】:

组合表格称为反规范化。

以创建维护地狱为代价,进行一些查询(这会产生大量JOINs)可能会(或可能不会)帮助运行得更快。

MySQL 只能使用JOIN 方法,即NESTED LOOPS

这意味着对于驱动表中的每条记录,MySQL 在循环中定位驱动表中的匹配记录。

定位记录是一项非常昂贵的操作,可能需要几十倍于纯记录扫描的时间。

将所有记录移到一张表中可以帮助您摆脱这种操作,但表本身会变大,并且表扫描需要更长的时间。

如果您在其他表中有很多记录,那么增加表扫描可能会超过按顺序扫描记录的好处。

另一方面,维护地狱是有保证的。

【讨论】:

如果您有 10000 个用户并且您正在使用正确设置了外键的数据库进行连接,那么您只需要通过执行 select * from users where name="bob" 之类的操作来进行密集查找.一旦有了 bob,您就可以使用索引来查找连接到 bob 的表,因为您使用的是 bob 的 ID,所以速度要快得多。无论您是在查询中进行联接还是查询 bob 然后单独查询表,都会发生这种情况。当然希望您的第二个查询是基于鲍勃的 id 而不是其他的。【参考方案3】:

它们都是 1:1 的关系吗?我的意思是,如果一个用户可能属于不同的用户级别,或者如果用户的兴趣在用户兴趣表中表示为几条记录,那么立即合并这些表是不可能的。

关于之前关于规范化的回答,必须说数据库规范化规则完全不顾性能,只看什么是整洁的数据库设计。这通常是您想要实现的目标,但有时主动去规范化以追求性能是有意义的。

总而言之,我想说问题归结为表中有多少字段,以及它们被访问的频率。如果用户活动通常不是很有趣,那么出于性能维护的原因,总是将它放在同一个记录上可能会很麻烦。如果某些数据(例如设置)经常被访问,但只是包含太多字段,则合并表也可能不方便。如果您只对性能提升感兴趣,您可能会考虑其他方法,例如将设置分开,但将它们保存在它们自己的会话变量中,这样您就不必经常查询数据库以获得它们。

【讨论】:

我完全不同意你的评论,即规范化只关注整洁而完全无视性能。在这两种情况下都需要权衡取舍,而非规范化实际上会使数据完整性面临风险。我想说数据库的规范化实际上提高了数据库的整体性能,而不是从非规范化的表中快速获得可忽略不计的性能提升。 鉴于讨论具体是关于 1:1 关系,拆分表不是规范化任务,对吧?如果没有重复信息,即使是单表也正常。 (好吧,它可能不满足 3NF 规范化,因此可以从第二个表中受益来解决这个问题,但这似乎不是 OP 所指的其他表。)【参考方案4】:

所有这些表都有1-to-1 关系吗?例如,每个用户行在user_statsuser_levels 中是否只有一个对应行?如果是这样,将它们组合到一个表中可能是有意义的。如果关系不是 1 to 1,那么将它们组合(非规范化)可能没有意义。

将它们放在单独的表中而不是在一张表中可能对性能影响不大,但除非您拥有数十万或数百万条用户记录。您将获得的唯一真正好处是通过组合查询来简化查询。

预计到达时间:

如果您的担心列过多,那么请考虑您通常一起使用哪些内容并将它们组合起来,剩下的则保留在一个单独的表中(或者如果需要,可以在几个单独的表中)。

如果您查看您使用数据的方式,我猜您会发现大约 80% 的查询使用了 20% 的数据,而其余 80% 的数据只是偶尔使用。将经常使用的 20% 合并到一张表中,将不经常使用的 80% 留在单独的表中,您可能会有一个很好的折衷方案。

【讨论】:

是的,每个表每个用户只有 1 行,只是为了省去管理大量重复数据的麻烦。这就是为什么我认为一张桌子适合。如果用户数据跨越多行,我希望这些表与主用户表分开。 如果每个表都有一对一的关系,那么一个表会更容易使用。在这种情况下,无需拆分表。拆分表意味着有超过 1 行,这可能会导致其他开发人员以这种方式对待它们。 非常有趣的想法将 80/20 应用于数据库表设计。让我也想到了 OOP(我主要是一名 Java 开发人员)类设计,并想知道那里是否同样有效(将 80% 的主要应用程序功能放在一个类中,其余的放在其他类中)。 @ZackMacomber - 不,类拆分应该基于引用位置。拆分为多个类的好处是围绕较小的功能单元绘制边界,以便更容易理解/测试/更改,并清楚该单元与其他功能单元交互的位置。目标是保持大多数连接(引用、调用)一个单元内,单元之间的连接很少。定义类实现的多个接口,每个用例具有不同的接口,这可能是实现拆分的有用的第一步。 @ToolmakerSteve 好主意 +1【参考方案5】:

创建一个庞大的表违背了关系数据库的原则。我不会将它们全部合并到一张表中。您将获得重复数据的多个实例。例如,如果您的用户有三个兴趣,那么您将有 3 行,其中包含相同的用户数据只是为了存储三个不同的兴趣。肯定会选择多重“标准化”表方法。请参阅 this Wiki 页面了解数据库规范化。

编辑: 我已经更新了我的答案,因为你已经更新了你的问题......我现在更同意我最初的回答......

这些细胞中有很大一部分是 可能保持空白

例如,如果某个用户没有任何兴趣,那么如果您进行规范化,那么您就不会在该用户的兴趣表中出现一行。如果您将所有内容都放在一张大表中,那么您将拥有仅包含 NULL 的列(显然很多列)。

我曾在一家电话公司工作,那里有大量表,获取数据可能需要多次连接。当从这些表中读取的性能至关重要时,创建的过程可以生成一个平面表(即非规范化表),不需要报告可能指向的连接、计算等。然后将它们与 SQL Server 代理结合使用,以特定时间间隔运行作业(即,某些统计信息的每周视图将每周运行一次,依此类推)。

【讨论】:

我喜欢这种方法,因为非规范化数据只是暂时存在,作为某个时刻的快照。没有插入/修改/删除问题 - 完成后将其丢弃。【参考方案6】:

为什么不使用与 Wordpress 相同的方法,即创建一个包含每个人都拥有的基本用户信息的用户表,然后添加一个“user_meta”表,该表基本上可以是与用户 ID 关联的任何键、值对。因此,如果您需要查找用户的所有元信息,您可以将其添加到您的查询中。如果不需要登录等操作,您也不必总是添加额外的查询。这种方法的好处还使您的表可以为您的用户添加新功能,例如存储他们的 twitter 句柄或每个个人兴趣。您也不必处理关联 ID 的迷宫,因为您有一个管理所有元数据的表,并且您会将其限制为只有一个关联而不是 50 个。

Wordpress 专门这样做是为了允许通过插件添加功能,从而使您的项目更具可扩展性,并且如果您需要添加新功能,则不需要对数据库进行全面检修。

【讨论】:

Wordpress wp_usermeta 表格呈几何增长。每个用户将 X 行添加到 wp_usermeta 表中,我们希望为该用户保留的每条元信息对应一行。如果您为每个用户保留 8 个自定义字段,这意味着 wp_usermeta 将是 users * 8 行长。这似乎会导致性能问题,但我不确定这是否是问题...... 如果您有数以万计的用户,我可以看到这会如何导致性能问题。基本上,数据库必须在用户元表中搜索 10000 * 8 个条目才能找到您要查找的条目。但是,如果您只在需要时查询元数据,我认为您的性能会更好。如果您总是要求元数据,即使您不需要它,那么您可能会遇到问题。如果您总是需要元数据,那么拆分表格可能不是最好的方法。 就在昨天,我们处理了一个加载所有用户的 WP 主题(使用 get_users())只是为了计算分页。一旦我们更正代码以使用 SELECT COUNT(…) 查询来代替分页,页面加载时间从 28 秒变为大约 400 毫秒。我仍然想知道与连接表或单个平面表相比性能如何……我在网络上找不到任何性能指标。 考虑到我之前的评论,拆分表格似乎仍然有效,除非出于某种原因,例如上面的分页示例,您需要选择所有用户。尽管如果您要检索所有元信息,您仍然会在 usermeta 表中有 80k 个条目。要搜索的内容很多。也许有人可以通过在两种实现上运行脚本并运行 100 次来获得平均值来测试什么是更好的方法,我可能会这样做。 我今天再次阅读了这篇文章,并意识到我关于 10000 * 8 条目的评论是正确的,但是数据库的工作方式应该使它基本上不是问题。如果出于某种原因您要获取所有 10000 个用户以及他们的元信息,这将是荒谬的。我想不出你想要这个的任何场景。由于外键和索引,数据库可以轻松地以闪电般的速度检索单个用户的元数据。假设您的数据库模型设置正确。【参考方案7】:

我认为这是“视情况而定”的情况之一。拥有多个表更干净,理论上可能更好。但是,当您必须连接 6-7 个表以获取有关单个用户的信息时,您可能会开始重新考虑这种方法。

【讨论】:

【参考方案8】:

我会说这取决于其他表格的真正含义。 一个 user_details 是否包含超过 1 个 / 用户等等。 什么级别的标准化最适合您的需求取决于您的需求。

如果您有一张具有良好索引的表,那可能会更快。但另一方面可能更难维护。

在我看来,您可以跳过 User_Details,因为它可能与用户是一对一的关系。 但其余的可能每个用户有很多行?

【讨论】:

以上是关于哪个更高效:多个 MySQL 表还是一个大表?的主要内容,如果未能解决你的问题,请参考以下文章

mysql把一个大表拆分多个表后,如何解决跨表查询效率问题

MySQL分区

MySQL:将大表拆分为分区或单独的表?

MySQL 分区介绍

哪个更快/更高效——大量的小 MySQL 查询或一个大的 PHP 数组?

mysql 怎么给一个表一次增加多个字段?