让我直截了当地说,我知道这不是最好的解决方案。我知道这是一个笨拙的功能和一个hack。但这就是我在这里的原因!
这个问题/工作建立在Facebook新闻提要的创建者some discussion on Quora with Andrew Bosworth的基础上。
我正在构建一个新闻提要。它完全是用PHP
和MySQL
构建的。
The MySQL
提要的关系模型由两个表组成。一个表用作活动日志;实际上,它被命名为activity_log
。另一个表是newsfeed
。这些表格几乎完全相同。
日志的模式为activity_log(uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)
...and提要的模式是newsfeed(uid INT(11), poster_uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)
。
任何时候用户做了一些与新闻提要相关的事情,例如问一个问题,它会立即被记录到活动日志中。
生成新闻提要
然后每X分钟运行一次(此时为5分钟,将在15-30分钟后更改为15-30分钟),我运行一个作业来执行下面的脚本。此脚本遍历数据库中的所有用户,查找该用户的所有朋友的所有活动,然后将这些活动写入新闻提要。
目前,出于性能*原因,剔除活动的SQL
(在ActivityLog::getUsersActivity()
中称为)有一个LIMIT 100
。*我不知道我在说什么。
<?php
$user = new User();
$activityLog = new ActivityLog();
$friend = new Friend();
$newsFeed = new NewsFeed();
// Get all the users
$usersArray = $user->getAllUsers();
foreach($usersArray as $userArray) {
$uid = $userArray['uid'];
// Get the user's friends
$friendsJSON = $friend->getFriends($uid);
$friendsArray = json_decode($friendsJSON, true);
// Get the activity of each friend
foreach($friendsArray as $friendArray) {
$array = $activityLog->getUsersActivity($friendArray['fid2']);
// Only write if the user has activity
if(!empty($array)) {
// Add each piece of activity to the news feed
foreach($array as $news) {
$newsFeed->addNews($uid, $friendArray['fid2'], $news['activity'], $news['activity_id'], $news['title'], $news['time']);
}
}
}
}
显示新闻提要
在客户机代码中,当获取用户的新闻提要时,我执行如下操作:
$feedArray = $newsFeed->getUsersFeedWithLimitAndOffset($uid, 25, 0);
foreach($feedArray as $feedItem) {
// Use a switch to determine the activity type here, and display based on type
// e.g. User Name asked A Question
// where "A Question" == $feedItem['title'];
}
改进新闻提要
现在请原谅我对开发新闻提要的最佳实践的有限理解,但我理解我正在使用的方法是write上所谓的cron扇出的有限版本,在某种意义上说,我正在运行一个作业作为中间步骤,而不是直接写入用户的新闻提要。但这与拉模型有很大不同,因为用户的新闻提要不是在加载时编译的,而是定期编译的。
这是一个很大的问题,可能值得反复讨论,但我认为它可以作为像我这样的新开发人员需要进行的许多重要对话的试金石。我只是想找出我做错了什么,我如何改进,或者我应该如何从头开始,尝试一种不同的方法。
这个模型另一个困扰我的地方是,它是基于新近而不是相关性工作的。如果有人能建议如何改进这一点以提高相关性,我将洗耳恭听。我正在使用Directed Edge的API来生成推荐,但是对于像新闻提要这样的东西,推荐器似乎不起作用(因为以前什么都没有!)。
发布于 2011-06-30 15:44:26
这个问题真的很酷。实际上,我自己正在实现这样的东西。所以,我将会大声地思考一下。
以下是我在你当前的实现中看到的缺陷:
新闻提要看起来与活动日志的数据完全相同,我会坚持使用那个活动日志表。
如果您跨数据库共享您的活动日志,它将使您更容易扩展。如果您愿意,也可以对用户进行分片,但即使在一个表中有1000万条用户记录,mysql也可以进行读取。因此,无论何时查找用户,您都知道要从哪个分片访问用户的日志。如果您经常对较旧的日志进行归档,并且只维护一组新的日志,那么您就不需要进行那么多的切分。或者甚至根本就不是。如果您的调整得当,您可以在MySQL中管理数百万条记录。
我将利用memcached来存储用户表,甚至日志本身。Memcached允许缓存大小高达1mb的缓存条目,如果您在组织密钥方面很聪明,那么您可能会从缓存中检索所有最新的日志。
就架构而言,这将是更多的工作,但当您希望用户开始对每个帖子进行评论时,它将允许您在future...especially中实时工作和向外扩展。;)
你看到这篇文章了吗?
发布于 2011-06-26 20:24:18
之间可以使用用户标志和缓存。假设有一个新的user字段作为last_activity。每当用户输入任何活动时,请更新此字段。保留一个标记,直到你拿到提要的时间,让我们说它是feed_updated_on。
现在更新函数$user->getAllUsers();,只返回last_activity时间晚于feed_updated_on的用户。这将排除所有没有任何活动日志的用户:)。对于用户的朋友来说,类似的过程。
您还可以使用缓存,如memcache或文件级缓存。
或者使用一些nosql DB将所有提要存储为一个文档。
发布于 2011-06-30 22:22:43
我正试着自己创建一个Facebook风格的新闻提要。我没有创建另一个表来记录用户的活动,而是从帖子、评论等的联合中计算出了“边缘”。
通过一点数学,我使用指数衰减模型来计算‘边缘’,时间流逝是自变量,考虑到每个帖子的评论,点赞等的数量来表达lambda常量。边缘一开始会快速减小,但几天后会逐渐变平到几乎为0(但永远不会达到0)
在显示提要时,使用RAND()将每条边相乘。边缘较高的帖子将更频繁地出现
这样,更受欢迎的帖子在更长时间内出现在新闻提要中的概率更高。
https://stackoverflow.com/questions/4162020
复制相似问题