作者GALINE (天真可愛CQD)
看板PHP
標題Re: [請益] 關於隨機文章問題
時間Wed Aug 5 19:53:13 2015
※ 引述《gname ((′口‵)↗︴<><...<><)》之銘言:
: : 但又擔心被cookie檔案被解開有安全信的顧慮,
其實我覺得 cookie 沒啥不好,至少這不算什麼機敏資料...吧?
除了綁電腦跟 cookie 有大小限制以外沒想到什麼問題
嗯..好吧,每次都跟 request 一起傳回 server 感覺很冗,量大時看起來討厭
然後電腦換手機就會破功
: 突然想到一個很 low 的方法...XD
: 我會在加一個欄位:read
: 內容是把讀過的文章ID記起來, 例如: 11,22,33 這樣
: 然後撈DB時就用 not in 去撈......XD
: 至於"無限擴充"我個人覺得不用想那麼遠,想像一下文章應該會有時效性,
: 總不可能我進站你撈一個10年前的文章給我看吧?
「一個欄位」去存,那就是逗點分隔。這樣沒辦法用 in 來處理
MySQL 的話大概會用 find_in_set() 之類的鬼東西來下
或是整串讀出來用 PHP 邏輯判斷
缺點?
- Code 不好看
- 資料不好看
- 看過一百篇文章就是一百個逗點分隔數字,聽起來就很討厭
- 欄位長度有上限,沒辦法無限擴充
- 會被有強迫症的人(例如我)抱怨資料沒有正規化
符合正規化而且不會爆欄位的做法是開一個 article_read table
id user_id article_id create_time
---------------------------------------------
1 1 11 {timestamp}
2 2 11 {timestamp}
3 1 22 {timestamp}
4 3 22 {timestamp}
然後下個 user_id + article_id 的 index
確保你能快速抓到某個使用者是否讀過某篇文章
不過資料筆數會是個小問題
一千個使用者一天看十篇文章,那就是一天一萬筆資料
一年就是三百六十五萬筆
於是
會員資料數: 1000 ...以上
文章資料數: 3650
讀取紀錄數:3650000
這數字看起來感覺不太爽,而且資料成長的速度會正比於你的活躍使用者數跟文章數
反過來說,這是能處理的量,只存數字的 table 也不會太胖
如果你的使用情境需要存那就存吧
更何況,你的網站不一定會成長到這麼多人用...[遠目]
至於「不要無限擴充」,這邊正好有個案例:PTT 的看板已讀
https://github.com/ptt/pttbbs/blob/master/docs/brc.txt <- 說明
https://github.com/ptt/pttbbs/blob/master/mbbsd/brc.c <- code
- 一年以上文章一律當已讀
- 一個看版紀錄 80 筆文章已讀紀錄
- 最多存 73 個看板的紀錄
- 儲存 expire_time,expire_time 之前的文章一律視為已讀
- 儲存 modified time,可以用來判斷推文
一句話說完就是「存最近N筆紀錄,多的丟掉,太舊的當成已讀」
這解決空間會持續膨脹的問題,只要資料長度不會無限增加
那麼放 cookie 還是放逗點分隔還是開專門 table 都是可行的方法
不過已讀未讀的判斷會有些問題
- 如果單純紀錄「最近一百筆」,那連看一百筆舊文章之後最新文章會從已讀變未讀
- PTT 的搞法我沒看懂(不會寫C了...),不過在八卦版連讀八十篇文章之後
會整個版的文章都變成已讀
反過來,如果你不喜歡這些靈異現象,或覺得使用者看到三年前的文章也該標未讀
那還是乖乖的開個 table 存完整的紀錄吧...
--
莉娜用魔法爆破進入屋內。
劫犯從另一個房間裡出現,大叫道︰「妳是誰!」
莉娜︰「我是個可疑的女人!」
劫犯無言以對。
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 61.218.242.230
※ 文章網址: https://www.ptt.cc/bbs/PHP/M.1438775603.A.E8D.html
回頭看到標題才發現不對,我一直在想已讀紀錄,你要的是隨機撈未讀.....
「施主,隨機不好弄啊,苦海無涯回頭是岸」....
講是這樣講,還是想一下可能的做法。
首先我是會限制要隨機的範圍,例如說「一年內的文章」或「最近一千篇文章」
如果我不限制範圍,那麼會變成我得拿「全部的文章」去比「全部的已讀紀錄」
這兩個都是會一直變大的東西,最好是不要每次開網頁都整份讀出來...
然後把「範圍內的文章 ID」扣掉「已經讀過的文章 ID」,最後隨機挑一筆
這邊實務可能像這樣
// 假設 query() 會回傳包含所有 id 的 array
// 先只讀 id 而不讀全部文章資料,DB 可以少讀不少東西,會比較快
$sql = 'SELECT id FROM article ORDER BY publish_time DESC LIMIT 1000'
$new_article_ids = query($sql);
$sql = 'SELECT id FROM record WHERE user=1 ORDER BY time DESC LIMIT 1000'
$read_ids = query($sql);
$candidate_ids = array_diff($new_article_ids, $read_ids);
$chosen_id = $candidate_ids[rand(0, count($candidate_ids) - 1)];
$article_data = getArticleData($chosen_id);
這邊會需要注意的問題有
- 讀取一年內/最近一千筆資料的時間會不會很久?
- 可能會,可能不會...不過有限制數量的話至少不會越來越久
- 萬一使用者全部都看過怎麼辦?
- 施主,這問題要問你自己....
還有一個方法是,如果你保證文章 id 連號
那麼可以先 SELECT max(id) FROM article,然後
$article = null;
$limit = 300; // 最多 try 300 次
for (; $limit > 0; $limit--) {
$candidate_id = rand(0, $max_id);
if (!isRead($candidate_id)) {
$article = getArticleData($candidate_id);
break;
}
}
if (null === $article) {
// 顯示錯誤訊息
} else {
// 顯示文章
}
※ 編輯: GALINE (61.218.242.230), 08/05/2015 20:34:36
推 tas72732002: 效能是最大的隱憂 08/05 20:36
→ GALINE: 效能要測過,固定讀三千筆資料會不會「太慢」要看情況而定 08/05 20:38
→ GALINE: 當然情感上不喜歡,但是這會動 XD 08/05 20:39
→ GALINE: 不過確定會越跑越慢的東西就必須避免,所以不能每次都掃 08/05 20:40
→ GALINE: 整個 table, table 會變大... 08/05 20:40
→ GALINE: 讀最近一千筆太多的話,那至少也可以讀進一百筆.. 08/05 20:41
※ 編輯: GALINE (61.218.242.230), 08/05/2015 20:42:40
→ GALINE: 是說文章變動頻率不高,可以寫 memcache 紀錄最近n筆id... 08/05 20:58
推 gname: 推~重點是要解決數量膨漲與範圍內的抓取資料,而不是任由 08/06 00:28
→ gname: 膨漲再來解決效能問題 08/06 00:28