यह एक बहुत ही रोचक प्रश्न है। इसके अनुकूलन के दौरान आप बहुत सी नई जानकारी खोज और समझ सकते हैं कि MySQL कैसे काम करता है। मुझे यकीन नहीं है कि मेरे पास एक बार में सब कुछ विवरण में लिखने का समय होगा, लेकिन मैं धीरे-धीरे अपडेट कर सकता हूं।
यह धीमा क्यों है
मूल रूप से दो परिदृश्य हैं:एक त्वरित और एक धीमा .
एक त्वरित . में परिदृश्य आप एक मेज पर कुछ पूर्वनिर्धारित क्रम में चल रहे हैं और शायद एक ही समय में जल्दी से अन्य तालिकाओं से प्रत्येक पंक्ति के लिए आईडी द्वारा कुछ डेटा प्राप्त करें। इस मामले में जैसे ही आपके पास आपके LIMIT क्लॉज द्वारा निर्दिष्ट पर्याप्त पंक्तियाँ होती हैं, आप चलना बंद कर देते हैं। आदेश कहाँ से आता है? आपके टेबल पर मौजूद बी-ट्री इंडेक्स या सबक्वेरी में सेट किए गए परिणाम के क्रम से।
एक धीमे . में परिदृश्य में आपके पास वह पूर्वनिर्धारित क्रम नहीं है, और MySQL को सभी डेटा को एक अस्थायी तालिका में रखना है, तालिका को किसी फ़ील्ड पर सॉर्ट करना है और n वापस करना है आपके LIMIT क्लॉज से पंक्तियाँ। यदि उस अस्थायी तालिका में आपके द्वारा डाली गई कोई भी फ़ील्ड TEXT (VARCHAR नहीं) प्रकार की है, तो MySQL उस तालिका को RAM में रखने की कोशिश भी नहीं करता है और डिस्क पर फ़्लश और सॉर्ट करता है (इसलिए अतिरिक्त IO प्रसंस्करण)।
ठीक करने वाली पहली चीज़
ऐसी कई स्थितियां हैं जब आप एक इंडेक्स नहीं बना सकते हैं जो आपको इसके आदेश का पालन करने की अनुमति देगा (उदाहरण के लिए, जब आप विभिन्न तालिकाओं से कॉलम द्वारा ऑर्डर करते हैं), तो ऐसी परिस्थितियों में अंगूठे का नियम डेटा को कम करना है जो MySQL डालेगा अस्थायी तालिका में। आप यह कैसे कर सकते हैं? आप सबक्वेरी में पंक्तियों के केवल पहचानकर्ता का चयन करते हैं और आपके पास आईडी होने के बाद, आप सामग्री को लाने के लिए आईडी को तालिका और अन्य तालिकाओं में शामिल करते हैं। यानी आप ऑर्डर के साथ एक छोटी सी टेबल बनाते हैं और फिर त्वरित परिदृश्य का उपयोग करते हैं। (यह सामान्य रूप से SQL से थोड़ा विपरीत है, लेकिन SQL के प्रत्येक फ्लेवर के पास इस तरह से प्रश्नों को अनुकूलित करने का अपना साधन है)।
संयोग से, आपका SELECT -- everything is ok here
मजाकिया लग रहा है, क्योंकि यह पहली जगह है जहां यह ठीक नहीं है।
SELECT p.*
, u.name user_name, u.status user_status
, c.name city_name, t.name town_name, d.name dist_name
, pm.meta_name, pm.meta_email, pm.meta_phone
, (SELECT concat("{",
'"id":"', pc.id, '",',
'"content":"', replace(pc.content, '"', '\\"'), '",',
'"date":"', pc.date, '",',
'"user_id":"', pcu.id, '",',
'"user_name":"', pcu.name, '"}"') last_comment_json
FROM post_comments pc
LEFT JOIN users pcu ON (pcu.id = pc.user_id)
WHERE pc.post_id = p.id
ORDER BY pc.id DESC LIMIT 1) AS last_comment
FROM (
SELECT id
FROM posts p
WHERE p.status = 'published'
ORDER BY
(CASE WHEN p.created_at >= unix_timestamp(now() - INTERVAL p.reputation DAY)
THEN +p.reputation ELSE NULL END) DESC,
p.id DESC
LIMIT 0,10
) ids
JOIN posts p ON ids.id = p.id -- mind the join for the p data
LEFT JOIN users u ON (u.id = p.user_id)
LEFT JOIN citys c ON (c.id = p.city_id)
LEFT JOIN towns t ON (t.id = p.town_id)
LEFT JOIN dists d ON (d.id = p.dist_id)
LEFT JOIN post_metas pm ON (pm.post_id = p.id)
;
यह पहला कदम है, लेकिन अब भी आप देख सकते हैं कि आपको इन बेकार LEFT JOINS और json serialisations को उन पंक्तियों के लिए बनाने की आवश्यकता नहीं है जिनकी आपको आवश्यकता नहीं है। (मैंने GROUP BY p.id
छोड़ दिया है , क्योंकि मैं नहीं देखता कि किस LEFT JOIN का परिणाम कई पंक्तियों में हो सकता है, आप कोई एकत्रीकरण नहीं करते हैं)।
अभी इस बारे में लिखना है:
- अनुक्रमणिका
- केस क्लॉज को फिर से तैयार करें (यूनियन ऑल का उपयोग करें)
- शायद किसी इंडेक्स को ज़बरदस्ती करना