作者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://webptt.com/cn.aspx?n=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
1F:推 tas72732002: 效能是最大的隐忧 08/05 20:36
2F:→ GALINE: 效能要测过,固定读三千笔资料会不会「太慢」要看情况而定 08/05 20:38
3F:→ GALINE: 当然情感上不喜欢,但是这会动 XD 08/05 20:39
4F:→ GALINE: 不过确定会越跑越慢的东西就必须避免,所以不能每次都扫 08/05 20:40
5F:→ GALINE: 整个 table, table 会变大... 08/05 20:40
6F:→ GALINE: 读最近一千笔太多的话,那至少也可以读进一百笔.. 08/05 20:41
※ 编辑: GALINE (61.218.242.230), 08/05/2015 20:42:40
7F:→ GALINE: 是说文章变动频率不高,可以写 memcache 纪录最近n笔id... 08/05 20:58
8F:推 gname: 推~重点是要解决数量膨涨与范围内的抓取资料,而不是任由 08/06 00:28
9F:→ gname: 膨涨再来解决效能问题 08/06 00:28