為視圖計數創建單獨的表是一種好習慣嗎?
我創建了一個
blog
表,其中有一個名為的欄位,views_count
但我聽說views_count
在每個頁面視圖上更新該欄位很費力。所以我現在創建了一個單獨的視圖計數表,如下所示:views: id, blog_id, ip_address, counter
現在我將唯一訪問儲存在
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`) );
**注意:**我們的網站部落格每天吸引數百萬訪問者。所以新表會經常更新。
每秒只有 10 次更新,這不是問題。
當你達到 100/sec 並且仍在使用 HDD 時,我們可以進一步討論。或 1000/秒,使用 SDD。
在更高的速率下,是的,違反教科書原則並將視圖計數器放在單獨的表中(只有
count
, 加上page_id
PKpage_id
)。原因是為了避免與對主表的非計數器訪問發生衝突。如果您在“誰查看了什麼,何時查看”的表格中跟踪每個“視圖”,那麼問題就會變得更加混亂。一方面,有
INSERTs
進入那個表(同樣,10/sec 不是問題)。另一方面,SELECT COUNT(*) ...
會出現極端情況——數 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) );
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) );
方案 A 的討論:
- 更簡單
- 對於“低流量”網站來說已經足夠了——比如每秒 10 次瀏覽。
B的優點:
- Needs
JOIN
,但僅當同時需要 meta 和 count 時。這JOIN
不是一個很大的負擔。- 僅 更新計數命中
BlogViews
,從而不會干擾任何只需要元資訊的查詢,尤其是UPDATEs
這樣的查詢。- 繁忙的網站需要 - 比如說 1000 次查看/秒的峰值載入。
何時使用 C:
- 千次觀看/秒。
- C 涉及收集視圖、合併它們,然後更新像 B 計劃這樣的結構。
Blogs
這進一步將兩者與BlogViews
乾擾隔離開來。- 查看計數可能會稍微延遲(秒)。
- (更多細節可以在別處討論)
計劃 A2、B2、C2、D2:
- 這些是對其他計劃的修改,您可以在其中跟踪“誰”在“何時”查看部落格。
SELECT COUNT(*)
這不僅僅需要擔心SELECT view_ct
。SELECT COUNT(*)
如果您有一百萬次觀看,可能會很昂貴。- 這些擴展最好使用“匯總表”設計概念來處理,我將在此處介紹。
計劃 E(現在已經給出了實際的模式,我將其稱為 E):
為
video_blog_views_tracker
,擺脫id
,擁有PRIMARY KEY(video_blog_id, user_ip_address) -- should be unique
這對於計數器查詢應該是最佳的:
SELECT SUM(counter) FROM video_blog_views_tracker WHERE video_blog_id = ?
是的,可以
video_blog.views
通過一個TRIGGER
或 CRON 作業將其滾動到。但在確定需要之前,我不會這樣做。