1. ホーム
  2. php

このPHP/MySQLニュースフィードを改善するにはどうすればよいですか?

2023-11-23 08:49:04

質問

これが最善の解決策でないことは承知していると言うことから始めましょう。不器用で、機能をハックするものであることは承知しています。 しかし、だからこそ私はここにいるのです!

この質問/作業は QuoraでのAndrew Bosworthとの議論 とのQuoraでの議論を基にしたものです。

ニュースフィードを作っています のようなものを作っています。それはもっぱら PHPMySQL .

<イグ


MySQL

フィードのリレーショナルモデルは2つのテーブルで構成されています。1つのテーブルはアクティビティログとして機能し、実際、このテーブルの名前は activity_log . もうひとつのテーブルは newsfeed . これらのテーブルはほぼ同じです。

スキーマ 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ジョブを実行する を実行し、以下のスクリプトを実行します。このスクリプトはデータベース内のすべてのユーザーをループし、そのユーザーのすべての友人のすべてのアクティビティを見つけ、そしてそれらのアクティビティをニュースフィードに書き込みます。

現時点では 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'];

}


ニュースフィードの改善

ニュースフィードを開発するためのベストプラクティスについての私の限られた理解をお許しください。しかし私は、私が使っているアプローチは、いわゆる 書き込み時のファンアウト の限定版であり、ユーザーのニュースフィードに直接書き込むのではなく、中間段階としてcronジョブを実行しているという意味で、限定的であると理解しています。しかしこれは、ユーザーのニュースフィードがロード時にコンパイルされるのではなく、定期的にコンパイルされるという意味で、プルモデルとは非常に異なっています。

これは、おそらく多くの行き違いに値する大きな質問ですが、私のような新しい開発者が持つ必要のある多くの重要な会話の試金石になることができると思います。私は、何が間違っているのか、どのように改善できるのか、あるいは、どのようにゼロから始めて別のアプローチを試すべきかを理解しようとしているところです。

このモデルについて悩ましいもう1つの点は、関連性よりも再帰性に基づいて動作することです。このモデルをどのように改良して関連性を持たせるか、どなたかご提案いただければ幸いです。私はレコメンデーションを生成するためにDirected EdgeのAPIを使用していますが、ニュースフィードのようなものではレコメンダーは機能しないようです(以前は何もお気に入りされていなかったからです!)。

どうすれば解決できますか?

本当にクールな質問ですね。実は私自身、このようなものを実装している最中なんです。だから、少し声を出して考えてみます。

あなたの現在の実装について、私の頭の中にある欠点は以下の通りです。

  1. すべてのユーザーの友達を処理していますが、同じグループの人々が同じような友達を持っているという事実のために、同じユーザーを何度も処理することになります。

  2. 私の友人の 1 人が何かを投稿しても、ニュース フィードに表示されるのはせいぜい 5 分間です。一方、すぐに表示されるはずですよね?

  3. 私たちはユーザーのニュースフィード全体を読んでいます。前回ログを解析したとき以降の新しいアクティビティを取得する必要があるだけではありませんか?

  4. これはそれほどうまくスケールしません。

ニュースフィードはアクティビティログと全く同じデータのように見えますが、私ならその1つのアクティビティログテーブルにこだわります。

アクティビティログをデータベース間でシャードすれば、簡単にスケーリングできるようになります。必要に応じてユーザーをシャードすることもできますが、1 つのテーブルに 1,000 万のユーザー レコードがある場合でも、mysql は問題なく読み取りを行うことができます。ユーザーを調べるときはいつでも、どのシャードからそのユーザーのログにアクセスすればいいかがわかるわけです。古いログを頻繁にアーカイブして、新しいログだけを維持するようにすれば、それほどシャード化する必要はないでしょう。あるいは、まったく必要ないかもしれません。中程度のチューニングであれば、MySQL で何百万ものレコードを管理することができます。

私は、ユーザーテーブルと、おそらくログ自体にさえも、memcachedを活用します。Memcached は、最大 1MB のサイズのキャッシュエントリを許可し、キーを整理するのが賢明であれば、キャッシュから最新のログをすべて取得できる可能性があります。

これはアーキテクチャに関する限り、より多くの作業を必要としますが、リアルタイムでの作業と将来のスケールアウトを可能にします...特に、ユーザーが コメント を開始したい場合は特にそうです)。

この記事を見ましたか?

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