問題
- ActiveRecord でランダムに3件取ってくるようなコードに
order("RANDOM()").limit(3)
を使うのはいかがなものか(性能的に)という議論- https://twitter.com/joker1007/status/669552773214572545
- 『SQLアンチパターン』の「ランダムセレクション」
- 代わりに OFFSET を使った実装を作って、性能を比較してみる
RANDOM() を使った実装
User.order("RANDOM()").limit(3)
OFFSET を使った実装
[*0..(User.count - 1)].
sample(3).
map { |offset| User.offset(offset).limit(1) }.
reduce(User.all) { |all_user, user| all_user.where(id: user) }.
where_values.
reduce(:or).
tap { |condition| break User.where(condition) }
性能比較
件数 | RANDOM() | OFFSET(n) |
---|---|---|
100 件の場合 | 0.6ms | 1.1ms |
10_000_000 件の場合 | 2314.7ms | 1470.2ms |
300_000_000 件の場合 | 139793.4ms | 215905.9ms |
結論
- テーブル件数が少なすぎたり多すぎたりする場合は
RANDOM()
の方が速い -
RANDOM()
でも仕方ない場合も多いのではないか