我正在设计一个庞大的数据库,在几个月内将拥有大量数据。数据库将在 MySQL 中投影,所有表格将使用 InnoDB引擎。所以,从现在开始,我一直在努力开发出最好的模型。那么,让我们开始吧!
表的简化模式如下所示:
(table)
+--------------------------------------------------------------+
| users |
+--------------------------------------------------------------+
| PK user_id int unsigned auto_increment |
| (all other columns with user data) |
+--------------------------------------------------------------+
(table)
+--------------------------------------------------------------+
| products |
+--------------------------------------------------------------+
| PK product_id int unsigned auto_increment |
| (all other columns with product data) |
+--------------------------------------------------------------+
(table) user’s favorite products
+-----------------------------------------------------------+
| user_favproducts |
+-----------------------------------------------------------+
| PK FK user int unsigned (on delete cascade) |
| PK FK product int unsigned (on delete cascade) |
+-----------------------------------------------------------+
用户只能收藏一次产品,这就是用户和产品成为user_favproducts主键的原因。如果用户删除了帐户,他们所喜爱的所有产品也将被删除;如果产品被删除,他们所有的收藏夹都将被删除。
让我们假设一个场景,其中 user_favproducts 拥有数百万行,包含许多用户和许多产品。因此,我尝试过考虑获得最佳性能的最佳解决方案:想象一下,每次用户打开个人资料时,都会向用户显示相应个人资料中最喜欢的产品数量。
简单:使用SQL获取计数,如下所示:
SELECT count(product) WHERE user = 1;
对于InnoDB引擎,我不确定当一个表有数百万行或更多行时它是如何工作的。
每次显示个人资料时,也会显示喜爱的产品数量。这意味着我每次都必须执行上面的SQL。考虑到表现,我不知道是否是更好的方式。
将喜爱产品的数量存储在元数据表中,如下所示:
(table)
+----------------------------------------------------------+
| user_metadata |
+----------------------------------------------------------+
| PK FK user int unsigned |
| favproducts int unsigned |
| last_update timestamp on update current_timestamp |
+----------------------------------------------------------+
为了更新此表,我考虑了三种选择:
安排将定期运行脚本的cron作业任务。然后这个脚本将更新计数,如下所示:
* Get count
result = SELECT count(product) WHERE user = 1;
* Update count
UPDATE user_metadata SET favproducts = result WHERE user = 1;
在user_favproducts中发生新插入时,始终触发,将使用SQL更新user_metadata,如下所示:
UPDATE user_metadata SET favproducts = favproducts + 1 WHERE user = 1;
不使用触发器,而是使用上面相同的sql,但这次它将由脚本执行,这是执行插入的相同脚本。
我的问题是:从上面采用哪种方法可以最好地计算出有多少用户喜欢的产品?
答案 0 :(得分:0)
备选方案A将是最慢的。只有少数记录可能并不重要,但在计算数百万条记录时肯定需要一段时间。
替代品B.2和B.3几乎相同。但是使用B.2将为您节省一次往返数据库。使用cron作业(如B.1中)不是一种选择。用户希望立即查看其操作的结果。
因此,从性能perspekcive,表“用户收藏产品”的插入和删除操作的触发器应该是最快的。
答案 1 :(得分:0)
最好的方法是一个相当主观的问题。
首先,"巨大"人类的数字不一定是巨大的"关系数据库的数字。只要您可以使用索引进行搜索,数百万行就不是问题。因此,只要用户列上有索引,您的简化示例SELECT count(product) WHERE user = 1;
几乎肯定会以毫秒为单位返回。
您正在考虑的替代方案 - 将最喜欢的产品数量添加到用户表中 - 通常称为非规范化。非规范化存在许多问题 - 一旦你有两个事实来源,你必须确保你保持同步,否则你最终会遇到搞笑的错误。 Cron的工作意味着有两个真理来源不同意的时候;触发器很难维护,并且经常会引入自己的性能问题。
我的一般方法是一点努力,但如果你关注表现,那么这是保持理智的唯一方法......
首先,弄清楚你的数据大小是什么,创建一个测试环境,在该环境中创建最规范化的模式,并用测试数据填充它(有像DbMonster这样的工具可以帮助解决这个问题)。 p>
获得测试环境后,找出应用程序需要运行的查询,响应时间目标是什么,并测试查询。您可以使用单元测试框架。
优化这些查询。把很多精力放在那里 - 这是你花费的最佳时间。
如果您确实无法使查询符合目标响应时间,请查看是否可以使用硬件解决问题。内存,RAM和SSD比开发人员时间便宜得多。
如果这不起作用,请考虑非规范化。如果您这样做,请编写单元测试以保护自己免受错误或意外的副作用。例如,如果访问用户很慢,那么简化的触发器会减慢插入user_favproducts
表的速度。
无论您做什么,在您知道并且可以衡量您遇到问题之前,请不要优化性能。可维护性影响可能非常严重,并且比快速,错误的应用程序正常工作更快,更快速地制作缓慢,正确的应用程序。
答案 2 :(得分:0)
我不确定它是否是一种优化方法,但肯定是通过改变逻辑来改进。 如上所述,您的两种选择都有一些优点和缺点。
每次从db计数都不好时,它会超载该表。
将元数据放在一个单独的表中并更新它总是在多个应用程序使用相同的东西之前是不好的。
每次使用count更新表都会产生与cron相同的影响,即使使用cron进行更新,也可能需要一段时间来更新大表的所有内容。在那段时间内你无法显示更新
我的建议是,在登录后计算一次收藏,并保持会话,每次用户喜欢会话的产品增量计数以及插入表格(如你所做)并显示来自会话的价值。
答案 3 :(得分:0)
从性能的角度来看,替代B.1 将是最好的,因为它不会影响用户,但有时它不会是最新的。
如果您希望始终更新数据,请使用备选B.2 (触发更新或删除),但添加/删除收藏夹时可能会很慢(取决于数据计数)