मैं एक परियोजना ( रेल 3.0.15, रूबी 1.9.3-p125-perf ) पर काम कर रहा हूं, जहां db लोकलहोस्ट में है और उपयोगकर्ता तालिका में 100K से अधिक रिकॉर्ड है ।
का उपयोग करते हुए
रैंड द्वारा आदेश ()
काफी धीमा है
User.order ( "रैंड (आईडी)")। पहले
हो जाता है
चुनें users
। * users
रैंड (आईडी) सीमा 1 से आदेश
और प्रतिक्रिया देने के लिए 8 से 12 सेकंड लगते हैं !!
रेल लॉग:
उपयोगकर्ता लोड (11030.8ms) का चयन करें users
। users
रैंड द्वारा आदेश से () सीमा 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 |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
आप देख सकते हैं कि किसी भी इंडेक्स का उपयोग नहीं किया गया है ( संभव_की = NULL ), एक अस्थायी तालिका बनाई गई है और वांछित मान प्राप्त करने के लिए एक अतिरिक्त पास की आवश्यकता है ( अतिरिक्त = अस्थायी का उपयोग करके; फाइल का उपयोग करके )।
दूसरी ओर, क्वेरी को दो भागों में विभाजित करके और रूबी का उपयोग करके, प्रतिक्रिया समय में हमारे पास एक उचित सुधार है।
users = User.scoped.select(:id);nil
User.find( users.first( Random.rand( users.length )).last )
(; कंसोल उपयोग के लिए शून्य)
रेल लॉग:
उपयोगकर्ता लोड (25.2ms) users
उपयोगकर्ता लोड से आईडी का चयन करें (0.2ms) का चयन करें
users
। users
जहां से users
। id
= 106854 सीमा 1
और 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 में चलता है।