Saya sedang mengerjakan sebuah proyek ( Rails 3.0.15, ruby 1.9.3-p125-perf ) di mana db berada di localhost dan tabel pengguna memiliki catatan lebih dari 100 ribu .
Menggunakan
dipesan oleh RAND ()
cukup lambat
User.order ("RAND (id)"). Pertama
menjadi
PILIH users
. * DARI users
PESANAN OLEH RAND (id) LIMIT 1
dan membutuhkan 8 hingga 12 detik untuk merespons !!
Log rel:
Beban Pengguna (11030.8ms) PILIH users
. * DARI users
ORDER OLEH RAND () LIMIT 1
dari mysql jelaskan
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
| 1 | SIMPLE | users | ALL | NULL | NULL | NULL | NULL | 110165 | Using temporary; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
Anda dapat melihat bahwa tidak ada indeks yang digunakan ( possible_keys = NULL ), sebuah tabel sementara dibuat dan pass tambahan diperlukan untuk mengambil nilai yang diinginkan ( ekstra = Menggunakan sementara; Menggunakan filesort ).
Di sisi lain, dengan memisahkan kueri dalam dua bagian dan menggunakan Ruby, kami memiliki peningkatan yang wajar dalam waktu respons.
users = User.scoped.select(:id);nil
User.find( users.first( Random.rand( users.length )).last )
(; nihil untuk penggunaan konsol)
Log rel:
User Load (25.2ms) SELECT id FROM users
User Load (0.2ms) SELECT
users
. * DARI users
MANA users
. id
= 106854 BATAS 1
dan mysql menjelaskan mengapa:
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| 1 | SIMPLE | users | index | NULL | index_users_on_user_type | 2 | NULL | 110165 | Using index |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
kita sekarang dapat menggunakan hanya indeks dan kunci utama dan melakukan pekerjaan sekitar 500 kali lebih cepat!
MEMPERBARUI:
seperti yang ditunjukkan oleh icantbecool dalam komentar solusi di atas memiliki kelemahan jika ada catatan yang dihapus dalam tabel.
Solusi yang bisa dilakukan
users_count = User.count
User.scoped.limit(1).offset(rand(users_count)).first
yang diterjemahkan menjadi dua pertanyaan
SELECT COUNT(*) FROM `users`
SELECT `users`.* FROM `users` LIMIT 1 OFFSET 148794
dan berjalan sekitar 500ms.