为视图计数创建单独的表是一个好习惯吗?

Doj*_*Dev 2 mysql database-design best-practices relations

我创建了一个blog表,其中有一个名为的字段views_count,但我听说更新views_count每个页面视图上的字段很麻烦。所以我现在创建了一个单独的视图计数表,如下所示:

views:
id,
blog_id,
ip_address,
counter
Run Code Online (Sandbox Code Playgroud)

现在我将独特的访问存储在views表中。当我在视图表中保存记录时,我也会更新blog字段views_count字段,那么这是一个好方法吗?或者有更好的选择吗?

完整创建架构:

CREATE TABLE `video_blog` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `category_id` int(11) UNSIGNED DEFAULT NULL,
  `title` varchar(255) NOT NULL,
  `sub_title` varchar(255) DEFAULT NULL,
  `slug` varchar(255) NOT NULL,
  `video_embed_code` text,
  `video_thumbnail` varchar(255) DEFAULT NULL,
  `video_thumbnail_alt` varchar(255) DEFAULT NULL,
  `description` text,
  `views` int(11) UNSIGNED NOT NULL,
  `is_active` tinyint(1) UNSIGNED NOT NULL DEFAULT '1',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
);

-- Table structure for table `video_blog_category`

CREATE TABLE `video_blog_category` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `description` varchar(255) DEFAULT NULL,
  `meta_title` varchar(255) DEFAULT NULL,
  `meta_description` varchar(255) DEFAULT NULL,
  `order_by` int(11) UNSIGNED DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
);

-- Table structure for table `video_blog_views_tracker`

CREATE TABLE `video_blog_views_tracker` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `video_blog_id` int(11) UNSIGNED DEFAULT NULL,
  `user_ip_address` varchar(255) DEFAULT NULL,
  `counter` int(11) UNSIGNED DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
);
Run Code Online (Sandbox Code Playgroud)

注意:我们的网站博客每天都有数百万访问者。所以新表会经常更新。

Ric*_*mes 5

每秒仅 10 次更新,这不是问题。

当你达到100/秒,并且仍然使用HDD时,我们可以进一步讨论。或者使用 SDD 时为 1000/秒。

是的,在更高的速率下,违反了教科书原则,并将视图计数器放在单独的表中(只有 , countpluspage_id作为 PK page_id)。原因是为了避免与主表的非计数器访问发生冲突。

如果您像“谁在何时查看什么”的表格一样跟踪每个“视图”,那么问题会变得更加混乱。一方面,INSERTs该表中有(同样,10/秒不是问题)。另一方面,SELECT COUNT(*) ...也会有极端——数到 100 没问题,但数到 100 万就可以了。

“喜欢”也有类似的问题。

对于更极端的流量,您需要收集更新/插入,合并它们,然后应用它们。这可能会让您再获得 10 倍的加速,但代价是增加一些复杂性以及更新计数器时延迟几秒钟。

但是,到那时,您将无法满足单个服务器的需求,并且将需要其他解决方案来解决您的所有问题。分片可能是下一级别设计的一部分。

对于任何从小规模开始并逐渐变得庞大的系统,您必须经常进行重大的重新设计。对于你(今天)来说,将柜台移出还为时过早。然而,这样做可能会(暂时)阻止下一次重大重新设计。

重新散列

计划A:( 全部合一)

CREATE TABLE Blog (
    id INT UNSIGNED AUTO_INCREMENT,
    lots of meta info -- title, etc
    view_ct INT UNSIGNED NOT NULL DEFAULT '0',
    PRIMARY KEY (id)
);
Run Code Online (Sandbox Code Playgroud)

Plan B:( 只拆出柜台)

CREATE TABLE Blog (
    id INT UNSIGNED AUTO_INCREMENT,
    lots of meta info -- title, etc
    PRIMARY KEY (id)
);
CREATE TABLE BlogViews (
    blog_id INT UNSIGNED,   -- not A_I; for joining to Blog
    view_ct INT UNSIGNED NOT NULL DEFAULT '0',
    ts TIMESTAMP NOT NULL,   -- optional -- time of last viewing??
    PRIMARY KEY(blog_id)
);
Run Code Online (Sandbox Code Playgroud)

A计划的讨论:

  • 更简单
  • 对于“低流量”网站来说已经足够了——比如每秒 10 次浏览。

B的优点:

  • 需要JOIN,但仅当同时需要 meta 和 count 时才需要。这JOIN不是一个大负担
  • 更新计数命中BlogViews,从而不会干扰任何需要元信息的查询,尤其UPDATEs是此类查询。
  • 繁忙网站所需——例如每秒 1000 次浏览的峰值加载。

何时使用 C:

  • 数千次观看/秒。
  • C 涉及收集观点、整合它们,然后更新类似于 Plan B 的结构。
  • 这进一步隔离了BlogsBlogViews免受干扰。
  • 观看次数可能会稍微延迟(秒)。
  • (更多细节可以在别处讨论)

计划 A2、B2、C2、D2:

  • 这些是对其他计划的修改,其中您可以跟踪“谁”“何时”查看博客。
  • SELECT COUNT(*)这不仅仅是需要担心的SELECT view_ct
  • SELECT COUNT(*)如果你有 100 万次观看,成本可能会很高。
  • 这些扩展最好用“汇总表”设计概念来处理,我在这里介绍了这一点。

计划 E(既然已经给出了实际的模式,我将其称为 E):

为了video_blog_views_tracker,摆脱id,并拥有

PRIMARY KEY(video_blog_id, user_ip_address)  -- should be unique
Run Code Online (Sandbox Code Playgroud)

这对于计数器查询来说应该是最佳的:

SELECT SUM(counter) FROM video_blog_views_tracker
    WHERE video_blog_id = ?
Run Code Online (Sandbox Code Playgroud)

是的,可以video_blog.views通过TRIGGERCRON 作业将其引入。但在确定需要之前我不会这样做。