ฉันกำลังทำงานในโปรเจ็กต์ ( Rails 3.0.15, Ruby 1.9.3-p125-perf ) โดยที่ db อยู่ในlocalhostและตารางผู้ใช้มีเร็กคอร์ดมากกว่า100Kเล็กน้อย
การใช้
สั่งซื้อโดย RAND ()
ค่อนข้างช้า
User.order ("RAND (id)") ก่อน
กลายเป็น
SELECT users
. * FROM users
ORDER BY RAND (id) LIMIT 1
และใช้เวลา8ถึง12 วินาทีในการตอบกลับ !!
บันทึกราง:
User Load (11030.8ms) SELECT users
. * FROM users
ORDER BY RAND () LIMIT 1
จากคำอธิบายของ mysql
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
| 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 |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
คุณจะเห็นว่าไม่มีการใช้ดัชนี ( possible_keys = NULL ) ตารางชั่วคราวจะถูกสร้างขึ้นและต้องใช้รหัสผ่านพิเศษเพื่อดึงค่าที่ต้องการ ( extra = ใช้ชั่วคราวการใช้ filesort )
ในทางกลับกันการแบ่งคำค้นหาออกเป็นสองส่วนและใช้ Ruby เรามีเวลาตอบสนองที่ดีขึ้นตามสมควร
users = User.scoped.select(:id);nil
User.find( users.first( Random.rand( users.length )).last )
(ไม่มีสำหรับการใช้คอนโซล)
บันทึกราง:
โหลดผู้ใช้ (25.2ms) ID เลือกจากusers
ผู้ใช้ไฟฟ้า (0.2ms) SELECT
users
. * FROM WHEREusers
= 106854 LIMIT 1users
id
และ mysql อธิบายว่าทำไม:
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| 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 | |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
ตอนนี้เราสามารถใช้ดัชนีและคีย์หลักเท่านั้นและทำงานได้เร็วขึ้นประมาณ 500 เท่า!
อัพเดท:
ตามที่ icantbecool ชี้ให้เห็นในความคิดเห็นวิธีแก้ปัญหาข้างต้นมีข้อบกพร่องหากมีการลบบันทึกในตาราง
วิธีแก้ปัญหาที่สามารถทำได้
users_count = User.count
User.scoped.limit(1).offset(rand(users_count)).first
ซึ่งแปลเป็นสองแบบสอบถาม
SELECT COUNT(*) FROM `users`
SELECT `users`.* FROM `users` LIMIT 1 OFFSET 148794
และทำงานในประมาณ 500ms