这是我目前的结构:
// 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
Hah :-)
..老实说,我甚至无法读取该数字(顺便说一下这个数字会每秒增长)。那么stackoverflow如何处理每个帖子的查看次数?是否总是会根据每个帖子的count(*)
计算viewed
?
答案 0 :(得分:0)
在您拥有数百万行之前,您不太可能需要分区,redis,nosql等。与此同时,让我们看看我们能做些什么。
让我们从解剖您的查询开始。我看到WHERE id=...
但没有LIMIT
或ORDER BY
。让我们添加到您的表格
INDEX(id, timestamp)
并使用
WHERE id = :id
ORDER BY timestamp DESC
LIMIT 10
任何索引都按索引排序。这就是你要找的10行彼此相邻。即使数据被推出缓存,也可能只有一个块来提供这10行。
但InnoDB中的二级索引中的“行”不包含满足SELECT *
的数据。索引“row”包含指向实际“数据”行的指针。因此,将有10次查找来获取它们。
至于观看次数,让我们以不同的方式实现:
CREATE TABLE ViewCounts (
post_id ...,
ct MEDIUMINT UNSIGNED NOT NULL,
PRIMARY KEY post_id
) ENGINE=InnoDB;
现在,给定post_id
,深入挖掘BTree以查找计数非常有效。 JOINing
这个表到另一个,我们用另外10个查找得到个别计数。
所以,你说,“为什么不把它们放在同一张桌子上”?原因是ViewCounts
变化如此频繁,以至于这些行为将与其他活动发生冲突。最好将它们分开。
即使我们打了几十个街区,但与扫描数百万行相比,这还不错。而且,这种数据有点“可缓存”。最近发布的帖子更频繁。热门用户访问频率更高。因此,100GB的数据可以充分缓存在10GB的RAM中。缩放就是“计算磁盘命中率”。