Re: One Response to “「フレンド・タイムライン処理の原理と実践」を追試してみた”

http://sklave.jp/logs/2008/06/10/re_friend_timeline_on_mysql/

コメントに書いたことと、あとワーストケースのパフォーマンスの問題かなぁ、と思いました。

単純なクエリは、実際には、

SELECT message.* FROM message INNER JOIN (
  SELECT message.id FROM message INNER JOIN follower
  ON message.user_id=follower.follower_id
  WHERE follower.user_id=1
  ORDER BY message.id DESC LIMIT 20
) as t1 USING (id);

のような形になるのでしょうが、このケース () 内の JOIN において、message テーブルを優先するにせよ、follower テーブルを優先するにせよ、遅いパターンが出てきます。

奥のテストデータは、ヘビーユーザとライトユーザを表現するために、ある程度の偏りを持つようにして生成してあるのですが、最もデータ量とフォロワーの多いユーザ (ID=1) についてのタイムライン取得処理は、上のクエリだと、 6.3 タイムライン/秒 という速度に落ち込みます。

単にこの問題に対処するのであれば、MySQLオプティマイザが follower テーブルを優先しているのを、STRAIGHT_JOIN コマンドを使って message テーブルを優先するように指示してしまえば、とても高速なクエリになります。しかし今度は、フォロワーの少ないユーザーのタイムライン取得処理が (message テーブルの降順でのアクセスでデータを発見するまで時間がかかるようになるため) 重たくなってしまいます。

残念ながら MySQL は、指定されたユーザ ID を基準に、どちらのインデックスを優先するか自動で判断してくれません。というより、それはインデックス単位のカーディナリティではカバーできない種類の最適化になるので、開発者がコードで工夫せざるを得ないたぐいのことになります。

というわけで、スケーラビリティが問題になるようなケースでは、複雑であっても、データサイズに依存せず、極端に重い処理が発生しないアルゴリズムのほうがいいのかな、と思っています。