youtube每個影片都有顯示多少人看過,有的到好幾千萬的數字都有
很好奇,到底是怎麼做的
假設環境是php+MySQL
就我知道的方法有以下的方式:
方法一
資料庫使用innodb,而影片table有一個Count的欄位,資料型態使用unsigned int
每次當使用者load一次這個頁面時,就做
update 影片 set Count=Count+1 where VideoIndex='xxx';
使用innodb可以有row lock的功能,而不會用到table lock
方法二
把Count存到一個.txt的檔案
一樣每次讀取影片時,去抓取txt然後+1
(抓取寫入有包含lock的功能設計)
方法三
每次load影片時,就insert一筆資料到 (VideoLogTable)
裡面基本包含[index, VideoID, UserIP]等資訊
然後當要算出某一個Video的點選次數時
就使用
select count(*) as Number from VideoLogTable where VideoID='xxx';
方法四
去計算http_access_log
計算某一個page被access幾次
上面這幾中方式裡面,目前我選擇「方法一」,但是不知道像youtube那種
大量存取的網站,是怎麼做到這個「計數器」的功能的?
方法一有個好處,就是不會有一大堆的紀錄
不過壞處是,全部人都要針對同一筆資料去做update
方法二感覺還滿差的
方法三的好處是,不會有wait的狀況,因為全部都是做insert
但是壞處是,假設有幾百萬人瀏覽過某一篇文章,那不就會有一堆記錄
而且如果全部的資料都只在一個表單,那應該很容易就暴表
如果針對不同影片就擁有不同表單,那資料庫的table也會暴表
也不知道使用count(*)時,會不會因為資料筆樹太多而影響速度
方法四,看起來不用去對資料庫作操作會減少資料庫的負擔
但是access_log的檔案也會因為太大而影響效率,而且單檔也會有4GB的限制
到時候也是會有一堆access_log
想問一下,大型網站是用什麼樣的方式解決這個小問題的呢?
大型網站,基本上就是用你的第一種方法。
因為資料庫有 access queue,可以同時接受多方存取,而且會依序執行,不會 block 住,也不會有 race condition 問題。
方法三也差不多,也是藉資料庫的能力去解決 concurrent access 問題,不過大型網站通常是為了記錄每一次存取的來源,才會記這些 log,如果是計數,不太可能用算 Log 的方式,因為實在太多。
方法二不用考慮了。沒有處理 concurrent access 問題,只用 lock 的話,若同時 100 個人存取,只會記 1 次,其它 99 個全部 drop。或者其它 99 個得 block 住,不管哪一種都不優。我用過,結果完全失控。
方法四也不必考慮。你不可能留著所有的 access log 來計算。
轉眼間大哥您就變成「高手1級了」~
看到你帳號頭像上大大的「MVP」就肅然起敬
這幾天我不時的在想這個問題,一直在想說,如果可以把計數器變成
像是php的 $count++; 該有多好~
腦子裡面又蹦出先暫時讓user的$count++數值放在cache裡面
先讓大家對cache的資料暫時去做+1的動作
接著可能每5分鐘,有一個php在去撈cahce裡面的資訊然後更新資料庫
就想說這樣的方式,可避免掉每個人來web都「馬上」要做一次update
缺點當然是那個數值沒有辦法即時顯現,可能會delay五分鐘
當然user在存取cache的資料的時候,也會有race condition的狀況
不過又想到,這邊也可以搭配MySQL把要存取的人insert到某一個表單,
再度透過另外一個php去抓這個表單,然後更新cache的資料
轉了一圈,發現其實也可以省下cache那段,總結的方式是
user 讀取某一個page時,insert一筆資料到「TempOperateTable」
然後系統會透過每五分鐘執行一個php
去把「TempOperateTable」裡面的資料做處理,更新Video的Count
等於說,那個「TempOperateTable」也是一個另類的queue,記錄下有哪些筆數想要去做Count的Update。
會有以上的想法是因為,方法一的優點在於不用記錄一大堆東西,方法三的缺點會有一堆記錄檔,但是不會有Block的現象
所以就把兩個方法結合起來,變成一種Delay Update Count的方式
目的就是減少方法一太過於「即時」且「頻繁」的去存取某一筆資料
這大概是我的想法,不知道這方法ok不ok?
我就喜歡回答像 sunz5010 這樣會自己思考解決之道的邦友。自己思考過才學得到東西。
你的想法很好。不過你的解法,其實資料庫都幫你思考過了。資料庫的存在,就是想幫 programmer 解決這些枝微末節的地方,讓 programmer 真的就把這個計數功能當做是 $count++ 一樣來看待就好了。
資料庫設計者一定思考過大量 client 去存取同一筆資料的問題,這是它的專業所在,所以它會處理這種問題。你做了一個 TempOperateTable 來定時算數量,對資料庫不見得是減輕負擔的工作,它得多管理一個 TABLE,而且你還把原本的設計複雜化了。
我的建議,還是第一種就好。就是單純的 update 影片 set Count=Count+1。
或者你可以利用 MySQL 的語法,加上 LOW_PRIORITY 關鍵字,變成:
update LOW_PRIORITY 影片 set Count=Count+1
讓 select 先取,由資料庫自己找空檔去做 update 的工作。你完全不必花腦筋在替它擔心它會不會太忙。