【发布时间】:2011-11-13 00:41:53
【问题描述】:
我有一个网站,里面有很多用户,还有很多“节点”(内容)。每个节点都可以下载,除了相关的特定节点 ID 之外,每个下载都有一个与之关联的“许可证”(因此用户可以下载节点 5 用于“商业用途”或“个人用途”等),如以及每个许可证的价格。
我的目标是以这样一种方式跟踪下载,以便我能够:
- 获取给定节点 ID 和许可证 ID 在给定时间段内的下载次数(上个月节点 5 出于“商业用途”下载了多少次?)。
- 获取给定节点 ID 和许可证 ID 的总下载次数。
- 获取给定 node_id 的下载次数,无论许可证如何(“商业用途”和“个人用途”的所有下载相结合)。
- 获取满足给定价格标准(即价格 = 0 或价格 > 0)的给定用户已下载的节点 ID(和相应的许可证 ID)。
如果优化无关紧要,存储的数据微不足道,但我的问题是对可能很容易增长到数百万行的表进行规范化/优化。具体来说,假设:
- 下载量千万级。
- 节点数达数十万。
- 用户数以万计。
我对任何“真正的”mysql 工作都很陌生,所以我感谢你的帮助,并指出我在哪里很愚蠢。到目前为止,这是我所得到的:
all_downloads 表
+-------------+---------+------------+---------+-----------+-------+
| download_id | node_id | license_id | user_id | timestamp | price |
+-------------+---------+------------+---------+-----------+-------+
download_id 是该表的唯一键。这个表是个问题,因为它可能有数千万行。
downloads_counted 表
不是通过查询 all_downloads 表来计算给定节点和许可证的下载总数,而是在 cron 运行期间对下载进行计数,并将这些数字单独存储在 downloads_counted 表中:
+---------------------------------------------------------------------------+
| node_id | license_id | downloads_total | downloads_month | downloads_week |
+---------------------------------------------------------------------------+
许可证 id 情况是新的(以前只有一个许可证,因此数据库中没有跟踪许可证),所以我现在只是想弄清楚如何使用它。过去,node_id 是该表的唯一键。我假设我现在应该做的是将 node_id 和 license_id 组合成一个唯一的主键。还是将 node_id 作为该表的唯一键,并获取给定 node_id 的所有行,然后在 php 中解析结果(分离或组合每个特定许可证的下载)是否也一样好?拥有一个没有唯一键的表是否符合最佳实践?
在任何情况下,我认为这个表大部分都可以,因为它不应该增长到超过 1 或 200 万行。
为给定用户返回下载的问题
这是我需要帮助的主要领域。我考虑过将 user_id 设置为 all_downloads 表中的键,并简单地查询包含给定 user_id 的所有行。但我担心从长远来看查询这张表,因为它从一开始就非常大,很容易增长到几千万行。
我考虑过创建一个如下所示的 user_downloads 表:
+---------------------+
| user_id | downloads |
+---------------------+
其中下载将是 node_ids 的序列化数组以及相关的许可证 ID 和价格,如下所示(5 是 node_id,将是顶级 node_ids 数组中的索引):
downloads = array('5' = array(license = array('personal', 'commercial'), price = 25))
我意识到将数据数组存储在单个单元格中被认为是一种不好的做法,而且我不确定它是否会提高性能,因为对于给定的用户,下载数组很容易增长到数千个。但是,我不确定如何创建另一个表结构,它比我的 all_downloads 表更有效地获取给定用户的下载。
非常感谢任何和所有帮助!
======================================
Bill Karwin 回答的后续问题:
不幸的是,时间戳将是一个存储在 int(11),而不是日期时间(符合 Drupal 标准)。我 假设这并没有真正改变优化 立场?
node_id/license_id/user_id(您对集群主键的想法)是 不保证是唯一的,因为用户可以根据需要多次下载同一许可证下的同一节点。这个 是我为每一行拥有唯一的 download_id 的主要原因...... 有一个特殊的原因有一个 download_id 会损害性能吗?或者将主键设为download_id/node_id/license_id/user_id的集群是否可以接受?还是将 download_id 作为复合键的第一部分会失去它的用处?
您认为拥有 downloads_counted 表是否仍然有意义,或者这会被认为是多余的?我的想法是它仍然有助于提高性能,因为下载计数(总下载量、本周、本月等)将在网站上经常出现非常,并且 downloads_counted 表会比 all_downloads 表少一到两个数量级的行。
我对 downloads_counted 表的想法:
CREATE TABLE downloads_counted (
node_id INT UNSIGNED NOT NULL,
license_id INT UNSIGNED NOT NULL,
downloads_total INT UNSIGNED NOT NULL,
downloads_month INT UNSIGNED NOT NULL,
downloads_week INT UNSIGNED NOT NULL,
downloads_day INT UNSIGNED NOT NULL,
PRIMARY KEY (node_id, license_id),
KEY (node_id)
) ENGINE=InnoDB;
node_id 上的辅助键用于获取给定 node_id 的所有许可证的所有下载...但是,如果 node_id 已经是复合主键的第一部分,这个键是否多余?
【问题讨论】:
标签: mysql database database-design