कभी-कभार धीमी क्वेरी के कारण?


16

हम Windows Server 2008 R2 पर MySQL 5.1 चला रहे हैं।

हम देर से अपने डेटाबेस पर कुछ डायग्नोस्टिक्स कर रहे हैं और कुछ परेशान करने वाली कलाकृतियाँ मिली हैं जिन्हें हम समझा नहीं सकते हैं । जब हमें कुछ समय लगने वाले प्रश्न (> 2000 मी।) में प्रवेश करने के लिए हमने कुछ कोड जोड़े। परिणाम आश्चर्यजनक थे (और संभवतः हमारे गतिरोध के लिए एक स्पष्टीकरण)।

कभी-कभी प्रश्न, जो आमतौर पर बहुत कम समय (<10ms) लेते हैं, 4 से 13 सेकंड से ले रहे हैं। स्पष्ट होने के लिए, ये ऐसे प्रश्न हैं जो लगातार चल रहे हैं (कई बार एक दूसरे) और इन क्वेरी समय स्पाइक्स से पीड़ित नहीं हैं।

हम किसी भी स्पष्ट गलतियों की तलाश में अपने अनुक्रमितों के माध्यम से चले गए हैं और बहुत ज्यादा भाग्य नहीं था।

अपडेट करें

लोगों की तालिका:

| people | CREATE TABLE `people` (
`people_id` bigint(20) NOT NULL AUTO_INCREMENT,
`company_id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`temp_password` varchar(10) DEFAULT NULL,
`reset_password_hash` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`phone` varchar(32) DEFAULT NULL,
`mobile` varchar(32) DEFAULT NULL,
`iphone_device_id` varchar(160) DEFAULT NULL,
`iphone_device_time` datetime DEFAULT NULL,
`last_checkin` datetime DEFAULT NULL,
`location_lat` double DEFAULT NULL,
`location_long` double DEFAULT NULL,
`gps_strength` smallint(6) DEFAULT NULL,
`picture_blob_id` bigint(20) DEFAULT NULL,
`authority` int(11) NOT NULL DEFAULT '0',
`active` tinyint(1) NOT NULL DEFAULT '1',
`date_created` datetime NOT NULL,
`last_login` datetime NOT NULL,
`panic_mode` tinyint(1) NOT NULL DEFAULT '0',
`battery_level` double DEFAULT NULL,
`battery_state` varchar(32) DEFAULT NULL,
PRIMARY KEY (`people_id`),
KEY `email` (`email`),
KEY `company_id` (`company_id`),
KEY `iphone_device_id` (`iphone_device_id`),
KEY `picture_blob_id` (`picture_blob_id`),
CONSTRAINT `people_ibfk_1` FOREIGN KEY (`company_id`) REFERENCES `companies` (`company_id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `people_ibfk_2` FOREIGN KEY (`picture_blob_id`) REFERENCES `blobs` (`blob_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4658 DEFAULT CHARSET=utf8 |

इंडेक्स:

+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table  | Non_unique | Key_name         | Seq_in_index | Column_name      | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| people |          0 | PRIMARY          |            1 | people_id        | A         |        3502 |     NULL | NULL   |      | BTREE      |         |
| people |          1 | email            |            1 | email            | A         |        3502 |     NULL | NULL   | YES  | BTREE      |         |
| people |          1 | company_id       |            1 | company_id       | A         |        3502 |     NULL | NULL   |      | BTREE      |         |
| people |          1 | iphone_device_id |            1 | iphone_device_id | A         |        3502 |     NULL | NULL   | YES  | BTREE      |         |
| people |          1 | picture_blob_id  |            1 | picture_blob_id  | A         |        3502 |     NULL | NULL   | YES  | BTREE      |         |
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+

हमारे पास सर्वर पर तालिका में ~ 5000 पंक्तियां हैं जो हमें परेशानी दे रही हैं।


1
ऐसा कुछ है जो आपने पिछले दो प्रश्नों में अभी तक नहीं दिखाया है। कृपया इस प्रश्न को तीन (3) सूत्र में जोड़ें: 1) लोगों को दिखाने के लिए बनाएं \ G 2) लोगों से पता चलता है; 3) चयनित COUNT (1) लोगों से;
रोलैंडमाइसीडीडीबीए

@RolandoMySQLDBA मैं कल जैसे ही काम करूँगा, मैं करूँगा। चीयर्स :)
RedBlueThing 11:14

मैंने अपना उत्तर अपडेट कर दिया। कृपया पढ़ें !!!
RolandoMySQLDBA

@RolandoMySQLDBA धन्यवाद :)। फिर भी इस सामान को पार्स करना। मैं आपको बताऊंगा कि हम कैसे जाते हैं।
RedBlueThing

जवाबों:


14

आपके पिछले दो प्रश्नों ( प्रश्न 1 , प्रश्न 2 ) में अद्यतन क्वेरी पंक्ति स्तर लॉकिंग के साथ प्राथमिक कुंजी द्वारा तालिका 'लोगों' को मार रही है। यह वही है जो मैंने 6 जून, 2011 10:03 बजे प्रश्न 1 में वापस कहा था

सभी लेनदेन PRIMARY कुंजी को पार कर रहे हैं। चूंकि PRIMARY InnoDB में संकुल सूचकांक है, PRIMARY कुंजी और पंक्ति स्वयं एक साथ हैं। इस प्रकार, एक पंक्ति और PRIMARY कुंजी का अनुरेखण एक और एक ही है। इसलिए, PRIMARY KEY पर कोई भी इंडेक्स लॉक एक पंक्ति स्तर लॉक है।

अभी तक कुछ और नहीं माना गया है जो कि अनुक्रमित करने के लिए धीमापन का कारण बन सकता है: इनबीडीबी में एनओएन-यूनिइक इंडेक्स का उपयोग। गैर-अद्वितीय अनुक्रमित का उपयोग करते हुए InnoDB में प्रत्येक अनुक्रमित खोज में गैर-अद्वितीय कुंजी से जुड़ी प्रत्येक पंक्ति की पंक्ति भी होती है। मूल पंक्ति मूल रूप से क्लस्टर इंडेक्स से निकलती है । गैर-अनूठे इंडेक्स को अपडेट करना जरूरी है कि अगर कोई टेबल्स ऐसा नहीं है तो क्लस्टर इंडेक्स ईवीएन के साथ बातचीत करें।

एक और बात के बारे में सोचने के लिए एक सूचकांक में BTREE नोड के प्रबंधन की प्रक्रिया है। कभी-कभी, इसे नोड्स के पृष्ठ विभाजन की आवश्यकता होती है। गैर-अनूठे अनुक्रमों के BTREE नोड में सभी प्रविष्टियों में गैर-अद्वितीय फ़ील्ड होते हैं, साथ ही क्लस्टर इंडेक्स के भीतर की पंक्ति। डेटा अखंडता को परेशान किए बिना ऐसे BTREE पृष्ठों के विभाजन को ठीक से कम करने के लिए, पंक्ति से जुड़ी पंक्ति को आंतरिक रूप से पंक्ति स्तर लॉक का अनुभव करना चाहिए।

यदि 'लोगों की तालिका' में बहुत सारे गैर-अनूठे इंडेक्स हैं, तो टेबलपेप में बड़ी संख्या में इंडेक्स पेजों के साथ-साथ समय-समय पर छोटी छोटी पंक्तियां आपके ऊपर छींकने के लिए तैयार रहें।

वहाँ एक और कारक है जो स्पष्ट नहीं है: कुंजी जनसंख्या

कभी-कभी जब कोई इंडेक्स पॉप्युलेट हो जाता है, तो इंडेक्स बनाने वाले प्रमुख मान समय के साथ लोप हो सकते हैं और MySQL क्वेरी ऑप्टिमाइज़र को की-लुक से स्विच करने, इंडेक्स स्कैन करने और अंत में फुल टेबल स्कैन करने का कारण बनता है। जब तक आप नए सूचकांक के साथ तालिका को नया स्वरूप नहीं देते हैं, तब तक आप नियंत्रण नहीं कर सकते जब तक कि lopsidedness ot कीज़ की भरपाई न हो जाए। कृपया 'लोगों की तालिका', 'लोगों की तालिका' की गिनती और 'लोगों की तालिका' के लिए शो इंडेक्स आउटपुट के लिए तालिका संरचना प्रदान करें

भले ही प्रश्न केवल प्राथमिक कुंजी का उपयोग करते हैं, गैर-अनूठे अनुक्रमित में कुंजी की lopsidedness अभी भी होने के लिए BTREE संतुलन और पृष्ठ विभाजन की आवश्यकता है। इस तरह के BTREE प्रबंधन रुक-रुक कर पंक्ति स्तर के ताले के कारण एक उल्लेखनीय मंदी का उत्पादन करेगा जो आपने होने का इरादा नहीं किया था।

UPDATE 2011-06-14 22:19

प्रश्न 1 से प्रश्न

UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>',
iphone_device_time = '2011-06-06 05:35:09', last_checkin = '2011-06-06 05:24:42',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125

UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>-<id>-<id>',
iphone_device_time = '2011-06-06 05:24:42', last_checkin = '2011-06-06 05:35:07',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125

घटनाओं में अनुक्रम चित्र

  1. PRIMARY KEY द्वारा पंक्ति खोजें
  2. पंक्ति और क्लस्टर इंडेक्स को लॉक करें
  3. सभी कॉलम अपडेट होने के लिए MVCC डेटा बनाएं
  4. चार कॉलम अनुक्रमित किए गए हैं (ईमेल, company_id, iphone_device_id, picture_blob_id)
  5. प्रत्येक सूचकांक के लिए BTREE प्रबंधन की आवश्यकता होती है
  6. समान लेन-देन स्थान के भीतर, 1-5 चरण एक ही पंक्ति पर दोहराए जाने की कोशिश कर रहे हैं, एक ही कॉलम को अपडेट कर रहे हैं (दोनों प्रश्नों में एक ही ईमेल करें, दोनों प्रश्नों में एक ही, company_id दोनों प्रश्नों में समान, iphone_devb_id अलग-अलग)

प्रश्न 2 से प्रश्न

UPDATE people SET iphone_device_id=NULL
WHERE iphone_device_id='iphone:<device_id_blah>' AND people_id<>666;

UPDATE people SET company_id = 444, name = 'Dad', password = '<pass>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@gmail.com',
phone = NULL, mobile = NULL, iphone_device_id = 'iphone:<device_id_blah>',
iphone_device_time = '2011-06-06 19:12:29', last_checkin = '2011-06-07 02:49:47',
location_lat = <lat>, location_long = <lng>, gps_strength = 66,
picture_blob_id = 1661,
authority = 1, active = 1, date_created = '2011-03-20 19:18:34',
last_login = '2011-06-07 11:15:01', panic_mode = 0, battery_level = 0.55,
battery_state = 'unplugged' WHERE people_id = 666;

ये दोनों प्रश्न और भी भ्रामक हैं क्योंकि पहली क्वेरी लोगों को छोड़कर सब कुछ अपडेट कर रही है। 666. सैकड़ों पंक्तियों को केवल पहली क्वेरी के साथ ही लॉक किया जा रहा है। दूसरी क्वेरी लोगों को अपडेट कर रही है_ 666 घटनाओं के 5 अनुक्रम को चलाने के लिए। पहली पंक्ति में लोगों की 6_ को छोड़कर शामिल प्रत्येक पंक्ति पर उन 5 अनुक्रमों को चलाया जा रहा है, लेकिन iphone_device_id के लिए सूचकांक दो अलग-अलग प्रश्नों के साथ एक इंटरसेप्ट कोर्स पर है। पहले-पहले-सर्व के आधार पर BTREE पृष्ठों में किसी का स्थान लॉक होगा।

टकराव के पाठ्यक्रम पर इन दो जोड़े प्रश्नों का सामना करने के लिए संभवतः एक ही अनुक्रमणिका के भीतर एक ही बीटीआरईई पृष्ठों को लॉक करना, इनोबीडी या एसीआईडी-अनुपालन आरडीबीएमएस के लिए एक गटर-रिंचिंग अनुभव हो सकता है। इस प्रकार, एक इंडेक्स स्लोडाउन प्रश्नों की इन जोड़ियों की नियति है जब तक कि आप यह गारंटी नहीं दे सकते कि क्वेरी AUTOCOMMIT = 1 के साथ चलती हैं या गंदे रीड्स की अनुमति देकर (हालांकि इन जैसे टकरावों को READ-COMMITTED बनाते हैं और READ-UNADMITED MVCC के लिए एक बुरा सपना है)।

UPDATE 2011-06-15 10:29

@RedBlueThing: प्रश्न 2 के प्रश्नों में, पहली क्वेरी एक श्रेणी क्वेरी है, इसलिए बहुत सारे पंक्ति लॉक प्राप्त किए जा रहे हैं। यह भी देखें कि दोनों क्वेरीज़ एक ही स्पेस आईडी 0 पेज नंबर 4611 एन बिट्स को लॉक करने की कोशिश कर रहे हैं। 152 को PRIMARY KEY, उर्फ ​​क्लस्टर इंडेक्स में लॉक किया जा रहा है।

यह सुनिश्चित करने के लिए कि आप जो ऐप चाहते हैं, वह बहुत कम से कम, आपके द्वारा अपेक्षित घटनाओं की श्रृंखला के आधार पर चल रहे हैं, दो अलग-अलग विकल्प हैं जो आपको प्रदान कर सकते हैं:

विकल्प 1) इस तालिका को MyISAM में परिवर्तित करें (कम से कम एक विकास सर्वर पर)। प्रत्येक अद्यतन, INSERT और DELETE पहले आओ, पहले पाओ के आधार पर एक पूर्ण टेबल लॉक लगाएंगे।

विकल्प 2) अनुक्रमिक अलगाव स्तर का उपयोग करने का प्रयास करें । जो SHARED मोड में सभी इच्छित पंक्तियों को लॉक कर देगा।

आपके द्वारा अपेक्षित घटनाओं का क्रम या तो टूट जाएगा या इन दो वैकल्पिक विकल्पों का उपयोग करके सफल होगा। यदि ये दोनों विकल्प विफल हो जाते हैं, तो आपको अपने ऐप को देखना होगा और अपने प्रश्नों के निष्पादन के क्रम को प्राथमिकता देनी होगी। एक बार जब आप उस प्राथमिकता को स्थापित कर लेते हैं, तो आप बस इन विकल्पों को पूर्ववत कर सकते हैं (विकल्प 1 के लिए, वापस InnoDB पर जाएं, विकल्प 2 के लिए, डिफ़ॉल्ट आइसोलेशन स्तर पर वापस जाएं [SERIALIZABLE का उपयोग बंद करें])।


@RolandoMySQLDBA मैंने आपके द्वारा पूछे गए विवरण के साथ हमारे प्रश्न को अपडेट किया।
RedBlueThing

@RolandoMySQLDBA इस पर एक और नज़र डालने के लिए धन्यवाद। मैं सोच रहा था, आप सवाल 2 के लिए टिप्पणी करते हैं, पहली क्वेरी सैकड़ों पंक्तियों को लॉक क्यों करेगी? क्या यह केवल डिवाइस आईडी से मेल खाती गैर 666 पंक्तियों को लॉक नहीं करेगा? (यानी एक पंक्ति)
RedBlueThing

@RolandoMySQLDBA प्रश्न 1 के आपके सुझाव के आधार पर, हमने अपनी ऑटोकॉमिट सेटिंग की जाँच की और पुष्टि की कि यह चालू है।
RedBlueThing

@ रोलैंडमाइसीडीडीबीए पहले सवाल (पंक्ति के सभी क्षेत्रों को अपडेट करने के अलावा) से प्रश्नों के साथ एक विशिष्ट समस्या है। ऐसा कुछ जो क्वेरी के लिए 13 सेकंड के निष्पादन समय की व्याख्या करेगा? मुझे यह समझ में आता है कि चार कॉलमों को अनुक्रमित करने के लिए कुछ ऐसा नहीं है जिसे आप सुझाएंगे, लेकिन क्या इससे वास्तव में इतना खराब प्रदर्शन होगा?
RedBlueThing

@RolandoMySQLDBA +1 और आपके सभी सुझावों के लिए धन्यवाद। हमने समस्या को हल करने के लिए आइसोलेशन स्तर को बदलना समाप्त नहीं किया। इसके बजाय हमने प्रश्न 2 के लिए आंशिक फ़ील्ड अपडेट किया और अपडेट पथ में एक क्वेरी को अनुकूलित किया। देखा! कोई और गतिरोध नहीं। :)
RedBlueThing 5

3

शो वारिब्लेस 'निर्दोष%' को पसंद करते हैं; - विशेष रूप से, यदि डेटा और इंडेक्स केवल बफर पूल के आकार तक नहीं पहुंचे हैं, तो आप डिस्क को पहले की तुलना में बहुत कठिन मार सकते हैं। I / O बड़ा प्रदर्शन हत्यारा है।

आपके अधिकांश क्षेत्र आवश्यकतानुसार दोगुने हैं। BIGINT (8 बाइट्स) अधिकांश आईडी के लिए रास्ता ओवरकिल है। 5000 पंक्तियों के लिए केवल एक SMALLINT UNSIGNED की आवश्यकता होती है (65K की सीमा, केवल 2 बाइट्स)। या सुरक्षा के लिए मार्जिन का उपयोग करें।

8 बाइट की कीमत पर DOUBLE आपको 16 महत्वपूर्ण अंक प्रदान करता है। क्या Battery_level में परिशुद्धता के 2 से अधिक महत्वपूर्ण अंक हैं? FLOAT 4 बाइट्स लेता है।

यहाँ मेरा कहना है कि "छोटा -> अधिक कैचीबल -> तेज"।

कृपया हमें धीमी क्वेरी दिखाएं; कम से कम कुछ ऐसे हैं जो अचानक धीमे हो गए हैं। हम उनके बिना केवल अनुमान लगा सकते हैं। धीमे से चालू करें और long_query_time = 1 सेट करें; ये सबसे धीमी क्वेरी खोजने में मदद करेंगे।

क्या आप "यौगिक" अनुक्रमित के लाभ को समझते हैं?

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.