如何优化这个MySQL表?

时间:2010-06-05 13:14:45

标签: php mysql database optimization

这是即将开展的项目。我有两张桌子 - 第一张用于保存照片,第二张用于跟踪照片的等级

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中的一些额外代码可以分割排名/时间(和排序),但这看起来还不错。

这是优化表格性能的正确方法吗?你会推荐什么?

9 个答案:

答案 0 :(得分:7)

第一个。周期。

实际上你会失去更多。存储在int列中的时间戳将仅占用4个字节的空间。

虽然以字符串格式存储的相同时间戳将占用10个字节。

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

您的第一个设计对于关系数据库是正确的。关键列中的冗余更为可取,因为它为您验证和查询排名提供了更大的灵活性。您可以在SQL中进行排序,计数,平均等操作,而无需编写任何PHP代码,以便从星期日开始以六种方式拆分字符串。

听起来你想使用像CouchDB或MongoDB这样的非SQL数据库。这些允许您在照片的记录中存储半结构化排名列表,然后有效地查询排名。需要注意的是,您并不真正知道排名格式正确,就像使用SQL一样。

答案 3 :(得分:1)

我认为通过“缓存”current_rank中的最后一个排名,可以很好地避免数据库“点击”超过正常状态(一遍又一遍地查询排名表)。如果它很少被查询(分析/报告你说过),那么排名正在大幅增长并不是真正重要的事情,从未更新但只是在最后插入记录:即使是一个非常轻的框也没有问题在该表中有数百万行。

您可以选择在磁盘上的不同位置进行大量更新,这可能会导致性能下降。

当然,如果你需要所有的旧数据,并且总是通过photo_id,你可以计划一个预定的运行到另一个表rankings_old,可能有一个月结束时的photo_id,年,月,排名(包括时间戳),所以检索旧数据很容易,但rankings_old或排名不需要更新,只在表格的末尾插入。

从我这里拿走它:纯日志记录表中的数百万条记录应该绝对没问题。

答案 4 :(得分:1)

标准化数据或非标准化数据。你会发现成千上万的文章。 :)

这实际上取决于您的需求。

如果您只想以性能(速度或RAM消耗或......)构建数据库,那么您应该只信任这些数字。为此,您必须使用预期数据“volume”来分析查询(您可以使用您编写的某些脚本生成数据)。要分析您的查询,请了解如何阅读以下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/
  3. 如果你想建立规范化的学术美:只需按照书籍和一般的建议。 :)

答案 5 :(得分:1)

在两个选项中 - 就像我之前所说的每个人 - 它必须是选项1。

您应该关注的是应用程序本身的瓶颈。用户是经常参考历史数据,还是只为少数精选用户出现?如果答案是每个人都能看到排名的历史数据,那么选项1就足够了。如果您不打算经常引用历史排名,那么您可以创建第三个“归档”表,在更新排名之前,您可以将原始排名表的行复制到归档表。这可以确保在要调用的主表上行数保持最小。

请记住,如果您正在更新行,并且有成千上万的行,那么在您的代码(PHP / Python /等)中获取结果可能会更有成效,截断表并插入结果而不是更新它是一排一排,因为这可能是一个潜在的瓶颈。

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

永远不要忘记索引。

希望有所帮助。

答案 6 :(得分:0)

您声明等级仅与图像相关联,在这种情况下,您只需要表1并持续更新等级。表2仅存储不必要的数据。这种方法的缺点是用户无法改变他的投票。

答案 7 :(得分:0)

你说第二个表用于分析/统计,所以它实际上不是需要存储在db中的东西。我的建议是摆脱第二个表并使用日志记录工具记录排名变化。

答案 8 :(得分:0)

如果你有100万张照片投票,你的第二个设计是非常危险的。 PHP可以处理吗?

使用第一个设计,您可以在数据库级别上进行所有数学运算,这将为您返回一个小result set