哪个更高效:多个 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】:组合表格称为反规范化。
以创建维护地狱为代价,进行一些查询(这会产生大量JOIN
s)可能会(或可能不会)帮助运行得更快。
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_stats
或user_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 专门这样做是为了允许通过插件添加功能,从而使您的项目更具可扩展性,并且如果您需要添加新功能,则不需要对数据库进行全面检修。
【讨论】:
Wordpresswp_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 表还是一个大表?的主要内容,如果未能解决你的问题,请参考以下文章