这是我目前的结构:
// posts
+----+--------+----------+-----------+------------+
| id | title | content | author_id | date_time |
+----+--------+----------+-----------+------------+
| 1 | title1 | content1 | 435 | 1468111492 |
| 2 | title2 | content2 | 657 | 1468113910 |
| 3 | title3 | content3 | 712 | 1468113791 |
+----+--------+----------+-----------+------------+
// viewed
+----+---------------+---------+------------+
| id | user_id_or_ip | post_id | date_tiem |
+----+---------------+---------+------------+
| 1 | 324 | 1 | 1468111493 |
| 2 | 546 | 3 | 1468111661 |
| 3 | 135.54.12.1 | 1 | 1468111691 |
| 5 | 75 | 1 | 1468112342 |
| 6 | 56.26.32.1 | 2 | 1468113190 |
| 7 | 56.26.32.1 | 3 | 1468113194 |
| 5 | 75 | 2 | 1468112612 |
+----+---------------+---------+------------+
这是我的查询:
SELECT p.*,
(SELECT count(*) FROM viewed WHERE post_id = :id) AS total_viewed
FROM posts p
WHERE id = :id
目前,我面临着一个巨大的viewed
表日期。那么我的表结构(或数据库设计)有什么问题吗?换句话说,我该如何改进它?
像 stackoverflow 这样的网站有将近 1200 万个帖子。每个帖子都有 (平均) 500 次查看。所以 viewed
的行数应该是:
12000000 * 500 = 6,000,000,000 rows
哈 :-)
..老实说,我什至看不懂那个数字(顺便说一句,这个数字每秒都会增长)。那么 stackoverflow 如何处理每个帖子的查看次数?它会始终根据每个帖子显示的 viewed
计算 count(*)
吗?
最佳答案
除非您有数百万行,否则您不太可能需要分区、redis、nosql 等。同时,让我们看看我们可以用您已有的东西做些什么。
让我们从剖析您的查询开始。我看到 WHERE id=...
但没有看到 LIMIT
或 ORDER BY
。让我们添加到您的表中
INDEX(id, timestamp)
和使用
WHERE id = :id
ORDER BY timestamp DESC
LIMIT 10
任何索引都按索引的内容排序。那就是您要查找的 10 行彼此相邻。即使数据被推出缓存,也可能只有一个 block 来提供这 10 行。
但是InnoDB 中二级索引中的“行”不包含满足SELECT *
的数据。索引“行”包含指向实际“数据”行的指针。因此,将进行 10 次查找。
至于观看次数,让我们以不同的方式实现:
CREATE TABLE ViewCounts (
post_id ...,
ct MEDIUMINT UNSIGNED NOT NULL,
PRIMARY KEY post_id
) ENGINE=InnoDB;
现在,给定一个 post_id
,向下钻取 BTree 以找到计数是非常有效的。 JOINing
这个表到另一个表,我们通过另外 10 次查找获得个人计数。
那么,您说,“为什么不将它们放在同一张表中”?原因是 ViewCounts
变化如此频繁,以至于这些操作会与 Postings 上的其他事件发生冲突。最好将它们分开。
即使我们命中了几十个 block ,与扫描数百万行相比也不错。而且,这种数据有点“可缓存”。最近的帖子被更频繁地访问。受欢迎的用户访问频率更高。因此,10GB 的 RAM 中可以充分缓存 100GB 的数据。缩放就是“计算磁盘命中率”。
关于mysql - 如何为我的网站帖子实现查看系统?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38287659/