misskey icon indicating copy to clipboard operation
misskey copied to clipboard

ノート数が多いインスタンスで、フォローが少ないとホームタイムラインの読み込みでクエリタイムアウトになる

Open syuilo opened this issue 3 years ago • 13 comments

💡 Summary

🥰 Expected Behavior

🤬 Actual Behavior

📝 Steps to Reproduce

📌 Environment

Misskey version: Your OS: Your browser:

syuilo avatar Nov 20 '22 06:11 syuilo

インデックス張ってあるのに何故だろう

syuilo avatar Nov 20 '22 06:11 syuilo

リストやローカルもめっちゃ遅い感じが?

tamaina avatar Nov 22 '22 12:11 tamaina

日付順にソートするのに時間かかってるとか(適当)

tamaina avatar Nov 22 '22 12:11 tamaina

  • フォローが少ない(厳密にはフォローしているユーザーの書き込みが少ない)ユーザーのホーム
  • リモートの投稿割合がかなり高いサーバー(所謂お一人様など)のローカル

みたいな、最新から数えていってタイムラインに表示すべき投稿を埋めるまでかなりの数をシークする必要があるケースで遅くなっている気がする(つまりシーケンシャルスキャンしている→インデックスが効いてない)

acid-chicken avatar Nov 25 '22 11:11 acid-chicken

ミュートなりブロックなりでインデックスが効かなくなる説

acid-chicken avatar Nov 25 '22 11:11 acid-chicken

例えば一ヶ月以内の投稿しか対象にしないようにしたら改善するかな

syuilo avatar Dec 10 '22 08:12 syuilo

例えば一ヶ月以内の投稿しか対象にしないようにしたら改善するかな

効果なかった

syuilo avatar Jan 01 '23 09:01 syuilo

MastodonとかはRedisにタイムラインを保持している(DBの検索はやらないとかなんとか)

tamaina avatar Jan 26 '23 11:01 tamaina

Issueはこちら https://github.com/misskey-dev/misskey/issues/9325

syuilo avatar Jan 26 '23 11:01 syuilo

あるんだ

tamaina avatar Jan 26 '23 11:01 tamaina

でもシークが問題なら、あまり参照されない古い投稿を取り出すのも遅いんじゃね?

tamaina avatar Jan 26 '23 11:01 tamaina

いやノートを検査しまくらないから早くなるか

tamaina avatar Jan 26 '23 11:01 tamaina

ユーザーのファイル付き投稿一覧を取得するのもしんどかったりする

tamaina avatar Feb 01 '23 07:02 tamaina

https://github.com/misskey-dev/misskey/blob/5af8b77d287f006031238293c29d8d5cea1cd4a1/packages/backend/src/server/api/endpoints/notes/timeline.ts#L74-L80

L78 と L79 が OR で繋がっているのでインデックスが使えていないのだと思います。

この部分を SQL で書くと

WHERE
  "note"."createdAt" > $1 AND
  (
    "note"."userId" = $2 OR
    "note"."userId" IN (SELECT
        "following"."followeeId" AS "following_followeeId"
      FROM
        "following" "following"
      WHERE
        "following"."followerId" = $3)
  )

のようになりますが、これを OR を使わない形に変える、例えば UNION を使って

WHERE
  "note"."createdAt" > $1 AND
  (
    "note"."userId" IN (SELECT
        "following"."followeeId" AS "following_followeeId"
      FROM
        "following" "following"
      WHERE
        "following"."followerId" = $3
      UNION ALL
      SELECT $2)
  )

とすると速くなります。

ですがまだ数秒かかってしまう感じ(ちゃんと調べてませんが、多分サブクエリが複数回呼ばれてその分遅くなっている?)なので、この部分を内部結合にして

FROM
  "note" "note"
  INNER JOIN (SELECT
        "following"."followeeId" "id"
      FROM
        "following" "following"
      WHERE
        "following"."followerId" = $3
      UNION ALL
      SELECT $2) "followeeOrMe" ON
    "followeeOrMe"."id"="note"."userId"

(略)

WHERE
  "note"."createdAt" > $1

とすると一瞬で返るようになります(内部結合にしたことで1回だけ呼ばれるようになるはず)。

ただ TypeORM では UNION がサポートされていないっぽい。

xianonn avatar Feb 11 '23 13:02 xianonn

あまり詳しくなくて恐縮なのですが、フォローがあるユーザーはIN句で自身もまとめて検索するのはどうでしょうか? L77~L79の範囲

if (hasFollowing){
    //IN句で自身ID、フォローIDからノート取得
}else{
    //自身IDからノート取得
}

冗長かもしれませんがORでインデックスが外れることを考えるとこっちでも良いのではと思いました Brackets の書き方がわからず申し訳ない…

Ryokusa avatar Feb 22 '23 09:02 Ryokusa

フォローがあるユーザーはIN句で自身もまとめて検索、のところは上で書いたやつの 1 つ目の案になりますね。

UNION は TypeORM で対応しておらずクエリ直書きの力技になってしまうので他の方法を考えているところ。 2 つ目の案の followeeOrMe の部分を VIEW で作ってしまえば普通のテーブルのように扱える(TypeORM が VIEW に対応していれば。未確認)が、これだけのために VIEW を作るのもなんだか。

1 クエリになっているのを崩さないように考えていたが、先にフォロイーを取得してしまって 2 クエリにすれば解決しそうなのでやってみる。

xianonn avatar Feb 26 '23 06:02 xianonn