4

PHP と MySQL を使用して、構築しようとしているフォーラム システムがあります。私が知りたいのは、ユーザーがフォーラムのエントリを読んだときに、他の誰かが投稿するまで、そのユーザーがどのフォーラムにいても、そのユーザーにのみ既読として表示されるように設定するにはどうすればよいかということです。

現在、スレッドごとに、PostID を持つテーブルがあり、それを投稿した UserID、リンク先の ThreadID、実際の投稿 (テキストとして)、投稿された日付/時刻があります。

各フォーラムのスレッド リストには、threadID (主キー)、ThreadName、所属する ForumID、NumPosts、NumViews、LastPostDateTime、および CreateDateTime があります。何か助けはありますか?

4

7 に答える 7

8

従来の解決策は、次のような結合テーブルです。

CREATE TABLE topicviews (
    userid INTEGER NOT NULL,
    topicid INTEGER NOT NULL,
    lastread TIMESTAMP NOT NULL,
    PRIMARY KEY (userid, topicid),
    FOREIGN KEY (userid) REFERENCES users(id),
    FOREIGN KEY (topicid) REFERENCES topics(id)
);

トピックが読み取られるたびにlastreadが更新されます。トピックのリストを表示するときに、topics.lastupdatedが> topicviews.lastreadの場合、新しい投稿があります。

従来の解決策はごみであり、データベースを破壊します!しないでください!

最初の問題は、すべてのトピックビューに書き込むと、忙しいフォーラム、特にテーブルレベルのロックしかないMyISAMテーブルで、データベースサーバーがすぐにひざまずくということです。(MyISAMテーブルを使用しないでください。全文検索を除くすべてに、InnoDBを使用してください)。

トピックで実際に新しいメッセージが読み取られているときに、最後の読み取り時刻をわざわざ書き込むだけで、この状況を少し改善できます。topic.lastupdated <topicviews.lastreadの場合、値を更新しても何も得られません。それでも、頻繁に使用されるフォーラムでは、これは負担になる可能性があります。

2番目の問題は組み合わせ爆発です。トピックごとにユーザーごとに1行がすぐに追加されます。つまり、ユーザー数が1,000、トピック数が1,000であり、保存するトピックビュー行が100万になる可能性があります。

ユーザーごとに記憶されるトピックの数を制限することで、この状況を少し改善できます。たとえば、特定の年齢より古くなったトピックをビューテーブルから削除し、すべての古いトピックが「既読」であると想定することができます。これには通常、バックグラウンドでクリーンアップタスクを実行する必要があります。

その他のそれほど集中的でないアプローチには、次のものがあります。

  • フォーラムごとに最後の読み取り時間を1つだけ保存します
  • サイト全体でユーザーごとに1回の最終訪問時間のみを保存します。これは、ユーザーの前回の訪問(セッション)以降に更新されたもののみを「新規」として表示します。
  • 最終読み取り情報はまったく保存されませんが、トピックのURL自体に最終更新時刻が含まれます。ユーザーのブラウザが最近トピックを見た場合、URLを記憶し、訪問済みとしてマークします。次に、CSSを使用して、訪問したリンクを「新しいメッセージを含まないトピック」としてスタイル設定できます。
于 2009-02-05T02:31:49.477 に答える
3

ユーザーがそのスレッドを読み取ったときに、別のテーブルUserID、threadID、LastReadDateTimeに格納されている可能性があります。

if (LastPostDateTime > LastReadDateTime) you got an unread post.

悲しいことに、オーバーヘッドが大きく、読み取りのたびに書き込みが発生します。

于 2009-02-05T02:13:46.750 に答える
2

ここでの一般的な考え方は正しいですが、スケーラビリティの問題に対する明らかな解決策が見落とされています。

@bobince: 2 つ目の問題は、組み合わせ爆発です。トピックごとにユーザーごとに 1 つの行がすぐに追加されます。たった 1,000 人のユーザーと 1,000 のトピックで、潜在的に 100 万の topicview 行を保存する必要があります。

誰かがそのスレッドを見たことがない場合は、「topicviews」テーブルにレコードを保存する必要はありません。null が返された場合、または last_read 時間が < last_post 時間である場合、トピックを未読の投稿があるものとして表示するだけです。これにより、その「百万」行がおそらく桁違いに削減されます。

@gortok:それを行う方法はたくさんありますが、ユーザーがサイトにアクセスするにつれて、それぞれが指数関数的に大きくなります.

この場合、n 回の投稿または n 週間後にフォーラムをアーカイブし、ロックするときに「topicviews」テーブルをクリーンアップします。

私の最初の提案は明らかであり、欠点はありません。私の 2 番目は、アーカイブされたトピックの使いやすさを低下させますが、それは迅速なフォーラムに支払う小さな代償です。遅いフォーラムは、読んだり投稿したりするのが苦痛です。

でも正直?おそらく、スケーラビリティについて心配する必要はありません。100 万行でさえ、実際にはそれほど多くありません。

于 2009-02-05T04:04:17.560 に答える
1

ユーザーのブラウザの機能を使用して、リンクの最後のpostidを次のようにスレッドに追加することができます: "thread.php?id = 12&lastpost = 122"

CSSでa:visitedを利用することにより、ユーザーがすでに読んだ投稿と、読んでいない投稿を表示することができます。

于 2009-02-06T13:18:52.007 に答える
1

これを行う簡単な方法はありません。それを行う方法はたくさんありますが、ユーザーがサイトにアクセスすると、それぞれが指数関数的に大きくなります。パフォーマンスを維持しながら実行できる最善の方法は、タイムスタンプを設定し、最後の訪問以降に更新されたフォーラムを「未読」としてマークすることです。

于 2009-02-05T02:07:39.760 に答える
0

ボビンスにはたくさんの良い提案があります。その他のいくつかの潜在的な最適化:

新しい「これは新しいですか?」と書いてください。memcached および MySQL の「ARCHIVE」テーブルへの情報。バッチ ジョブは、「実際の」テーブルを更新できます。

ユーザーごとに、「$date まですべて既読」フラグを保持します (「すべて既読にする」がクリックされた場合)。

何か新しいものが投稿されたら、すべての「既読」フラグをクリアします。これにより、「フラグ」の数が抑えられ、テーブルは (topic_id, user_id) だけになり、タイムスタンプがなくなります。

于 2009-02-05T09:21:01.177 に答える