如何改进此 PHP/MySQL 新闻源?

2022-08-30 08:37:08

让我马上说,我知道这不是最好的解决方案。我知道这是笨拙的,并且是一个功能的黑客。但这就是我在这里的原因!

这个问题/工作建立在与Facebook新闻提要的创建者安德鲁·博斯沃思(Andrew Bosworth)关于Quora的一些讨论之上

我正在构建某种新闻提要。它仅内置于 和 中。PHPMySQL

alt text


The MySQL

源的关系模型由两个表组成。一个表用作活动日志;实际上,它被命名为.另一个表是 。这些表几乎完全相同。activity_lognewsfeed

日志的架构activity_log(uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)

...并且源的架构为 。newsfeed(uid INT(11), poster_uid INT(11), activity ENUM, activity_id INT(11), title TEXT, date TIMESTAMP)

每当用户执行与新闻源相关的操作(例如提出问题)时,它将立即记录到活动日志中


生成新闻源

然后每隔X分钟(目前5分钟,将更改为15-30分钟后),我运行一个执行以下脚本的cron作业。此脚本循环遍历数据库中的所有用户,查找该用户的所有朋友的所有活动,然后将这些活动写入新闻源。

目前,剔除活动(调用 )的 (调用 ) 出于性能*原因而强加。*不是说我知道我在说什么。SQLActivityLog::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'];

}

改进新闻源

现在原谅我对开发新闻源的最佳实践的有限理解,但我理解我用来成为所谓的“写入扇出”的有限版本的方法,从某种意义上说,我正在运行cron工作作为中间步骤,而不是直接写入用户的新闻源。但这与拉动模型非常不同,因为用户的新闻源不是在加载时编译的,而是定期编译的。

这是一个大问题,可能值得大量来回,但我认为它可以作为像我这样的新开发人员需要进行的许多重要对话的试金石。我只是试图弄清楚我做错了什么,我该如何改进,或者我应该如何从头开始尝试不同的方法。

关于这个模型,让我烦恼的另一件事是,它基于新近度而不是相关性来工作。如果有人能建议如何改进这一点以使其具有工作相关性,我会全神贯注。我正在使用Directed Edge的API来生成推荐,但对于像新闻提要这样的东西,推荐器似乎不起作用(因为以前没有收藏过任何东西!


答案 1

真的很酷的问题。实际上,我自己正在实现这样的东西。所以,我要大声思考一下。

以下是我在脑海中看到的有关您当前实现的缺陷:

  1. 您正在处理所有用户的所有朋友,但由于同一组人具有相似的朋友,因此您最终将多次处理相同的用户。

  2. 如果我的一个朋友发布了一些内容,它最多5分钟不会显示在我的新闻提要上。而它应该立即出现,对吧?

  3. 我们正在为用户阅读整个新闻源。难道我们不需要自上次处理日志以来获取新活动吗?

  4. 这不能很好地扩展。

新闻源看起来与活动日志完全相同的数据,我会坚持使用那个活动日志表。

如果跨数据库对活动日志进行分片,则可以更轻松地进行缩放。如果您愿意,也可以对用户进行分片,但即使您在一个表中有1000万条用户记录,mysql也应该可以进行读取。因此,每当您查找用户时,您都知道要从哪个分片访问用户的日志。如果您经常存档较旧的日志,并且只维护一组新的日志,则不必进行分片。或者甚至可能根本没有。如果您经过适度的调整,您可以在MySQL中管理数百万条记录。

我会为您的用户表甚至日志本身使用memcached。Memcached允许缓存最大为1mb的条目,如果您在组织密钥时很聪明,则可以从缓存中检索所有最新的日志。

就架构而言,这将是更多的工作,但它将允许您实时工作并在将来向外扩展...特别是当您希望用户开始评论每个帖子时。;)

你看过这篇文章吗?

http://bret.appspot.com/entry/how-friendfeed-uses-mysql


答案 2

之间可以使用用户标志和缓存。比方说,为用户设置一个新字段作为last_activity。每当用户输入任何活动时,请更新此字段。保持一个标志,直到你获取提要的时间,让我们说它feed_updated_on。

现在更新函数$user->getAllUsers();以仅返回last_activity时间晚于feed_updated_on的用户。这将排除:)没有任何活动日志的所有用户。类似的过程为用户朋友。

您还可以使用缓存,如 memcache 或文件级缓存。

或者使用一些nosql DB将所有源存储为一个文档。


推荐