【发布时间】:2013-02-18 15:13:37
【问题描述】:
希望你一切都好。
我需要这个数据库的帮助:
这是一个存储投票的数据库。用户选择他们喜欢的音轨,然后投票给他们。他们可以投票“赞成”或“反对”。非常简单。但是,当涉及到计算统计数据时,它就变得毛茸茸了。
元
这是一个键值样式表,用于存储最常用的统计信息(只是某种缓存):
mysql> SELECT * FROM Meta;
+-------------+-------+
| Key | Value |
+-------------+-------+
| TRACK_COUNT | 2620 |
| VOTE_COUNT | 3821 |
| USER_COUNT | 371 |
+-------------+-------+
投票
投票表本身保存投票。这里唯一有趣的字段是Type,它的值意味着:
-
0- 应用程序投票,用户使用 UI 为曲目投票 -
1- 导入投票(来自外部服务) -
2- 合并投票。实际上与 Imported Vote 相同,但它实际上做了一个注释,该用户已经使用外部服务为该曲目投票,现在他正在使用应用程序重复自己。
跟踪
轨道本身包含总统计数据。来自外部服务 (LikesRP) 的喜欢、不喜欢、喜欢的数量、来自外部服务 (DislikesRP) 的不喜欢、喜欢/不喜欢的调整。
应用
该应用需要获得以下投票:
- 过去 7 天内投票最多的 5 首曲目
- 过去 7 天内投票最多的 5 首曲目
- 过去 7 天内投票最多的 5 首曲目,其中的投票来自外部服务 (
Vote.Type = 1) - 上个月投票最多的 100 首曲目
为了获得投票最多的 100 首曲目,我使用以下查询:
SELECT
T.Hash,
T.Title,
T.Artist,
COALESCE(X.VotesTotal, 0) + T.LikesAdjust as VotesAdjusted
FROM (
SELECT
V.TrackHash,
SUM(V.Vote) AS VotesTotal
FROM
Vote V
WHERE
V.CreatedAt > NOW() - INTERVAL 1 MONTH AND V.Vote = 'up'
GROUP BY
V.TrackHash
ORDER BY
VotesTotal DESC
) X
RIGHT JOIN Track T
ON T.Hash = X.TrackHash
ORDER BY
VotesAdjusted DESC
LIMIT 0, 100;
这个查询工作正常,它支持调整(客户想要调整列表中的轨道位置)。几乎相同的查询用于获得 5 个最上/下投票的曲目。任务 #3 的查询是这样的:
SELECT
T.Hash,
T.Title,
T.Artist,
COALESCE(X.VotesTotal, 1) as VotesTotal
FROM (
SELECT
V.TrackHash,
SUM(V.Vote) AS VotesTotal
FROM
Vote V
WHERE
V.Type = '1' AND
V.CreatedAt > NOW() - INTERVAL 1 WEEK AND
V.Vote = 'up'
GROUP BY
V.TrackHash
ORDER BY
VotesTotal DESC
) X
RIGHT JOIN Track T
ON T.Hash = X.TrackHash
ORDER BY
VotesTotal DESC
LIMIT 0, 5;
问题是第一个查询大约需要 2 秒才能执行,而我们的投票数少于 4k。到年底,这个数字将是大约 20 万张选票,这很可能会扼杀这个数据库。所以我正在想办法解决这个难题。
现在我想到了这些问题:
- 我是否把数据库设计弄错了?我的意思是,它会更好吗?
- 我的查询有误吗?
- 还有什么我可以改进的吗?
我做的第一件事是缓存。但是,好的,这彻底解决了问题。但我对 SQL 相关的解决方案很好奇(总是倾向于完美)。
我的第二个想法是将这些计算值放入Meta 表中,并在投票过程中更改它们。但是我的时间很短,只是尝试一下。顺便说一句,这值得吗?或者,企业级应用如何解决这些问题?
谢谢。
编辑
我不敢相信我忘记了包含索引。他们在这里:
mysql> SHOW INDEXES IN Vote;
+-------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Vote | 0 | UNIQUE_UserId_TrackHash | 1 | UserId | A | 890 | NULL | NULL | | BTREE | |
| Vote | 0 | UNIQUE_UserId_TrackHash | 2 | TrackHash | A | 4450 | NULL | NULL | | BTREE | |
| Vote | 1 | INDEX_TrackHash | 1 | TrackHash | A | 4450 | NULL | NULL | | BTREE | |
| Vote | 1 | INDEX_CreatedAt | 1 | CreatedAt | A | 1483 | NULL | NULL | | BTREE | |
| Vote | 1 | UserId | 1 | UserId | A | 1483 | NULL | NULL | | BTREE | |
+-------+------------+-------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
mysql> SHOW INDEXES IN Track;
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Track | 0 | PRIMARY | 1 | Hash | A | 2678 | NULL | NULL | | BTREE | |
| Track | 1 | INDEX_Likes | 1 | Likes | A | 66 | NULL | NULL | | BTREE | |
| Track | 1 | INDEX_Dislikes | 1 | Dislikes | A | 27 | NULL | NULL | | BTREE | |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
【问题讨论】:
-
是的,我会照你说的做:当有人投票时,修改缓存表,而不是从头开始重新计算所有统计信息。如果您时间紧迫,请按计划离线进行长时间查询,并缓存结果 - 因此如果需要十分钟,那么在短期内您将不得不忍受最多十分钟前的结果。无论如何,这对于这个数据集来说可能不是问题,这取决于数据必须有多活跃。
-
您的投票表上有哪些索引?如果您的表被正确索引,200k 记录并不是很多。
-
@GarethD,好吧,从第二个示例执行查询确实需要 2 秒。不确定它是否会扩展,由于缺乏知识,我仍在进行适当的测试。还是谢谢。
标签: mysql sql sql-optimization