如何优化这个MySQL表?

6

这是一个即将进行的项目。我有两个表格 - 第一个表格跟踪照片,第二个表格跟踪照片的排名。

Photos:
+-------+-----------+------------------+ 
| id    | photo     | current_rank     |
+-------+-----------+------------------+ 
| 1     | apple     | 5                |
| 2     | orange    | 9                |
+-------+-----------+------------------+

照片排名会定期变化,以下是跟踪它的表格:

Ranks:
+-------+-----------+----------+-------------+ 
| id    | photo_id  | ranks    | timestamp   |
+-------+-----------+----------+-------------+
| 1     | 1         | 8        | *           |
| 2     | 2         | 2        | *           |
| 3     | 1         | 3        | *           |
| 4     | 1         | 7        | *           |
| 5     | 1         | 5        | *           |
| 6     | 2         | 9        | *           |
+-------+-----------+----------+-------------+ * = current timestamp

每个等级都被跟踪以进行报告/分析。 [编辑] 用户将随时访问统计数据。 我与一位在这个领域有经验的人交谈,他告诉我像上面那样存储等级是正确的方式。但我还不太确定。
问题在于数据冗余。将会有成千上万的照片。最近照片的排名每小时变化多次(几分钟内),但老照片的排名变化较少。按此速度,表格将在几个月内拥有数百万条记录。由于我没有处理大型数据库的经验,这让我有点紧张。
我想到了这个:
Ranks:
+-------+-----------+--------------------+
| id    | photo_id  | ranks              |
+-------+-----------+--------------------+
| 1     | 1         | 8:*,3:*,7:*,5:*    |
| 2     | 2         | 2:*,9:*            |
+-------+-----------+--------------------+ * = current timestamp

这意味着需要在PHP中编写一些额外的代码来分割等级/时间(以及排序),但我认为这看起来不错。

这是否是优化表格性能的正确方法?你有什么建议吗?


排名是否与其他任何东西相关联?比如给出排名的用户?如果有关系,这种关系有多重要?也就是说,您需要存储用户的排名吗? - Ravi Vyas
排名与其他任何内容都没有关联,只与照片有关。它是由计算机算法计算和更改的! :) - Yeti
1
@Col:你的评论总是让我微笑! :) 实际上,这些ID看起来更像是这样:** 4606886418 **,现在大约有200万张照片。每张照片的排名会变化50多次。因此,尽管可能不算太大,但也不能称为小。 - Yeti
@Col:也许不是,但我正在学习!谢谢。 - Yeti
1
@lost_in_code,不用担心col的评论。他/她喜欢虐待初学者。祝你好运 ;) - jasmine
显示剩余3条评论
9个回答

7
第一个。就是这样。
实际上您会损失更多。 存储在int列中的时间戳仅占用4个字节的空间。
而以字符串格式存储的相同时间戳将需要10个字节。

我忘了提到时间戳不是Unix时间戳,而是日期时间。这两者一样吗? - Yeti
@为你的作业提供帮助:http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html - Your Common Sense

2

我建议你坚持第一种方法。在第二种方法中,将有大量数据存储在行中,随着时间的推移,它会变得越来越多!也就是说,如果一张照片获得了成千上万的排名。

第一种方法也更易于维护,也就是说,如果你想删除一个排名。


1
实际上,排名表中的数据从未更新,因为它用于显示统计信息。 - Yeti

2

对于关系型数据库,您的第一个设计是正确的。主键列中的冗余更可取,因为它可以让您在验证和查询排名时拥有更多的灵活性。您可以在SQL中进行排序、计数、平均值等操作,而无需编写任何PHP代码来将字符串分成六个部分。

听起来您想使用像CouchDB或MongoDB这样的非SQL数据库。这些数据库可以让您直接在照片记录中存储半结构化的排名列表,并且能够高效地查询排名。但需要注意的是,与SQL不同,您并不确切知道排名是否以正确的格式存储。


1

我认为通过在current_rank中缓存最后一个rank,可以很好地避免规范化的数据库“命中”(重复查询排名表)。如果ranks表很少被查询(如你所说的分析/报告),几乎不更新,只是在末尾插入记录,那么即使它的大小增长得非常快,即使有数百万行数据,也不会有任何问题。

相比之下,你的替代方案会要求在磁盘上的不同位置进行大量更新,可能导致性能下降。

当然,如果你需要所有旧数据,并且总是按photo_id查询,你可以计划定期将其存储到另一个名为rankings_old的表中,该表可能包括photo_id、year、month、rankings(包括时间戳)等信息。这样,检索旧数据仍然很容易,但在rankings_old或rankings中不需要进行更新,只需在表的末尾插入新记录。

相信我的经验:日志表中有数百万条记录绝对不会有任何问题。


我的错,没有提到日志对照片所有者可用(他们可以查看排名统计信息)。因此,可能会有很多查询。 - Yeti
那么这就不是很明确了,但我仍然认为第一个解决方案更好(更新性能是关键,更新不应该锁定选择)。我可能会使用每日排名_old而不是每月排名。 - Wrikken

1

规范化数据或非规范化数据。你会发现有成千上万的文章讨论这个问题。 :)

这真的取决于你的需求。

如果你只想根据性能(速度或RAM消耗等)构建数据库,那么你应该只相信数字。为了做到这一点,你必须使用预期数据“量”(你可以用自己编写的脚本生成数据)来分析你的查询。要分析你的查询,请学习如何阅读以下2个查询的结果:

  • EXPLAIN extended...
  • SHOW STATUS

然后学习如何改进这些数字(mysql设置、数据结构、硬件等)。

作为一个入门者,我真的建议阅读这两篇优秀的文章:

  1. http://www.xaprb.com/blog/2006/10/12/how-to-profile-a-query-in-mysql/
  2. http://ajohnstone.com/archives/mysql-php-performance-optimization-tips/

如果你想为学术美感而进行规范化的构建:只需遵循书籍和一般建议即可。 :)


1

在这两个选项中,就像之前所有人说的那样,必须选择选项1。

你真正需要关注的是应用程序本身的瓶颈。用户是否经常参考历史数据,或者只有少数几个用户才能看到?如果每个人都可以看到排名的历史数据,那么选项1就足够好了。如果你不经常参考历史排名,那么你可以创建第三个“归档”表,在更新排名之前,将原始排名表的行复制到归档表中。这确保了被调用的主表上的行数保持最小。

记住,如果你正在更新行,并且有成千上万的行,那么在代码(PHP/Python等)中获取结果,截断表并插入结果可能比逐行更新更有成效,因为那可能会成为潜在的瓶颈。

你可能还想查一下分片(水平分区)- http://en.wikipedia.org/wiki/Shard_%28database_architecture%29

永远不要忘记良好地建立索引。

希望这有所帮助。


0

你说排名只与图片有关,那么你只需要表格1,并实时更新排名。表格2只存储不必要的数据。这种方法的缺点是用户无法更改他的投票。


0

您提到第二个表格是用于分析/统计数据的,所以实际上不需要将其存储在数据库中。我的建议是取消第二个表格,并使用日志记录工具来记录排名变化。


我忘记提到用户可以访问第二个表中的数据了。已经编辑过问题了。 - Yeti

0

如果你有一百万张照片投票,那么你的第二个设计非常危险。PHP能处理这个吗?

使用第一个设计,你可以在数据库层面上进行所有的数学计算,从而得到一个较小的结果集


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接