MySQL - InnoDB के लिए सबसे आसान तरीका है


12

मेरे पास एक InnoDB तालिका है जिसे मैं बदलना चाहता हूं। तालिका में ~ 80M पंक्तियाँ हैं, और कुछ सूचकांकों को छोड़ दें।

मैं स्तंभों में से एक का नाम बदलना चाहता हूं और कुछ और सूचकांक जोड़ना चाहता हूं।

  • इसे करने का सबसे तेज़ तरीका क्या है (यह मानते हुए कि मैं डाउनटाइम भी भुगत सकता हूं - सर्वर अप्रयुक्त गुलाम है)?
  • एक "सादे" alter table, सबसे तेज़ समाधान है?

इस समय, मुझे परवाह है कि सभी की गति है :)


कृपया SHOW CREATE TABLE tblname\G, वह कॉलम दिखाएं जिसे बदलने की आवश्यकता है, कॉलम का डेटाटाइप और कॉलम का नया नाम।
रोलैंडमाइसीडीडीबीए

यहाँ यह है: pastie.org/3078349 जिस कॉलम का नाम बदलना है , वह है sent_atऔर इसे कुछ और सूचकांकों को जोड़ने के लिए
Ran

sent_at को क्या नाम बदलने की आवश्यकता है?
रोलैंडमाइसीडीडीबीए 12

चलो कहते हैं: new_sent_at
Ran

जवाबों:


14

एक निश्चित तालिका को गति देने का एक निश्चित तरीका अनावश्यक अनुक्रमित को दूर करना है

यहां तालिका के नए संस्करण को लोड करने के लिए प्रारंभिक चरण दिए गए हैं

CREATE TABLE s_relations_new LIKE s_relations;
#
# Drop Duplicate Indexes
#
ALTER TABLE s_relations_new
    DROP INDEX source_persona_index,
    DROP INDEX target_persona_index,
    DROP INDEX target_persona_relation_type_index
;

कृपया निम्नलिखित ध्यान दें:

  • मैंने source_persona_index को गिरा दिया क्योंकि यह 4 अन्य अनुक्रमितों में पहला स्तंभ है

    • unique_target_persona
    • unique_target_object
    • source_and_target_object_index
    • source_target_persona_index
  • मैंने target_persona_index को गिरा दिया क्योंकि यह 2 अन्य अनुक्रमितों में पहला स्तंभ है

    • target_persona_relation_type_index
    • target_persona_relation_type_message_id_index
  • मैंने target_persona_relation_type_index गिरा दिया क्योंकि पहले 2 कॉलम target_persona_relation_type_message_id_index में भी हैं

ठीक है कि अनावश्यक सूचकांक का ख्याल रखता है। क्या ऐसे कोई सूचकांक हैं जिनमें कम हृदयता है? यहाँ यह निर्धारित करने का तरीका है कि:

निम्न क्वेरी चलाएँ:

SELECT COUNT(DISTINCT sent_at)               FROM s_relations;
SELECT COUNT(DISTINCT message_id)            FROM s_relations;
SELECT COUNT(DISTINCT target_object_id)      FROM s_relations;

आपके प्रश्न के अनुसार, लगभग 80,000,000 पंक्तियाँ हैं। अंगूठे के एक नियम के रूप में, MySQL क्वेरी ऑप्टिमाइज़र एक सूचकांक का उपयोग नहीं करेगा यदि चयनित स्तंभों की कार्डिनैलिटी तालिका पंक्ति गणना का 5% से अधिक है। इस मामले में, यह 4,000,000 होगा।

  • अगर COUNT(DISTINCT sent_at)> 4,000,000 रु
    • फिर ALTER TABLE s_relations_new DROP INDEX sent_at_index;
  • अगर COUNT(DISTINCT message_id)> 4,000,000 रु
    • फिर ALTER TABLE s_relations_new DROP INDEX message_id_index;
  • अगर COUNT(DISTINCT target_object_id)> 4,000,000 रु
    • फिर ALTER TABLE s_relations_new DROP INDEX target_object_index;

एक बार उन इंडेक्स की उपयोगिता या बेकारता निर्धारित हो जाने के बाद, आप डेटा को पुनः लोड कर सकते हैं

#
# Change the Column Name
# Load the Table
#
ALTER TABLE s_relations_new CHANGE sent_at sent_at_new int(11) DEFAULT NULL;
INSERT INTO s_relations_new SELECT * FROM s_relations;

यह सही है? NOPE !!!

यदि आपकी वेबसाइट इस पूरे समय तक रही है, तो s_relations_new के लोडिंग के दौरान s_relations के विरुद्ध INSERT चल सकते हैं। आप उन लापता पंक्तियों को कैसे पुनः प्राप्त कर सकते हैं?

S_relations_new में अधिकतम आईडी ढूंढें और s_relations से उस आईडी के बाद सब कुछ संलग्न करें। यह सुनिश्चित करने के लिए कि तालिका केवल जमी हुई है और इस अद्यतन के लिए उपयोग की जाती है, आपके पास उन अंतिम पंक्तियों को प्राप्त करने के लिए थोड़ा डाउनटाइम होना चाहिए जिन्हें s_relation_new में डाला गया था। आप को क्या करना है यहां बताया गया है:

ओएस में, mysql को पुनरारंभ करें ताकि कोई और लॉग इन न कर सके लेकिन रूट @ localhost (टीसीपी / आईपी अक्षम करता है):

$ service mysql restart --skip-networking

अगला, mysql में लॉगिन करें और उन अंतिम पंक्तियों को लोड करें:

mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

फिर, सामान्य रूप से mysql को पुनरारंभ करें

$ service mysql restart

अब, यदि आप mysql को नीचे नहीं ले जा सकते हैं, तो आपको s_relations पर एक चारा-और-स्विच करना होगा। बस mysql में लॉगिन करें और निम्न कार्य करें:

mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations_old WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

कोशिश तो करो !!!

गुफा: एक बार जब आप इस ऑपरेशन से संतुष्ट हो जाते हैं, तो आप अपनी प्रारंभिक सुविधा में पुरानी तालिका को छोड़ सकते हैं:

mysql> DROP TABLE s_relations_old;

12

सही उत्तर आपके द्वारा उपयोग किए जा रहे MySQL इंजन के संस्करण पर निर्भर करता है।

यदि 5.6+ का उपयोग किया जाता है , तो सभी तालिका के डेटा को कॉपी किए बिना , नाम बदलने और सूचकांकों को जोड़ने / हटाने को ऑनलाइन किया जाता है ।

ALTER TABLEहमेशा की तरह उपयोग करें , यह ज्यादातर नाम और इंडेक्स ड्रॉप्स के लिए तात्कालिक होगा, और यथोचित रूप से इंडेक्स जोड़ के लिए तेजी से (जितनी जल्दी एक बार सभी तालिका पढ़ने के लिए)।

यदि 5.1+ का उपयोग किया जाता है, और InnoDB प्लगइन सक्षम है, तो सूचकांकों को जोड़ना / निकालना ऑनलाइन भी होगा। नाम बदलने के बारे में निश्चित नहीं है।

यदि पुराने संस्करण का उपयोग किया जा रहा है, तब ALTER TABLEभी सबसे तेज़ है - लेकिन शायद बहुत ही धीमी गति से होगा क्योंकि आपके सभी डेटा को हुड के नीचे एक अस्थायी तालिका में फिर से डाला जाएगा।

अंत में, मिथक डिबंकिंग का समय। दुर्भाग्य से मेरे पास जवाबों पर टिप्पणी करने के लिए यहां पर्याप्त कर्म नहीं हैं, लेकिन मुझे लगता है कि सबसे अधिक वोट दिए गए उत्तर को सही करना महत्वपूर्ण है। यह गलत है :

अंगूठे के एक नियम के रूप में, MySQL क्वेरी ऑप्टिमाइज़र एक सूचकांक का उपयोग नहीं करेगा यदि चयनित स्तंभों की कार्डिनैलिटी अधिक है जो तालिका पंक्ति गणना का 5% है

यह वास्तव में दूसरा रास्ता है

कुछ पंक्तियों का चयन करने के लिए संकेतक उपयोगी होते हैं , इसलिए यह महत्वपूर्ण है कि उनके पास उच्च कार्डिनैलिटी है, जिसका अर्थ है एक ही मूल्य के साथ कई अलग-अलग मूल्य और सांख्यिकीय रूप से कुछ पंक्तियाँ।


InnoDB प्लगइन दस्तावेज़ से लिंक (प्रतिनिधि सीमा के कारण पेस्ट नहीं किया जा सकता)।
मेज़िस

2
MySQL 5.5 पर मुझे RENAME TABLEतत्काल (जैसा कि अपेक्षित था) मिला, लेकिन CHANGE COLUMNप्राथमिक कुंजी का नाम बदलने के लिए एक पूर्ण प्रतिलिपि ... 7 घंटे! संभवतः केवल इसलिए कि यह प्राथमिक कुंजी थी? अच्छा नही।
केसीडी डे

2

मुझे मारिया डीबी 10.1.12 के साथ भी यही समस्या थी, फिर मैंने डॉक्यूमेंटेशन पढ़ने के बाद पाया कि ऑपरेशन "इन-प्लेस" करने का एक विकल्प है, जो टेबल की कॉपी को खत्म कर देता है। इस विकल्प के साथ परिवर्तन तालिका बहुत तेज है। मेरे मामले में यह था:

alter table user add column (resettoken varchar(256),
  resettoken_date date, resettoken_count int), algorithm=inplace;

यह बहुत तेज है। एल्गोरिथम विकल्प के बिना यह कभी भी समाप्त नहीं होगा।

https://mariadb.com/kb/en/mariadb/alter-table/


0

कॉलम का नाम बदलने के लिए,

ALTER TABLE tablename CHANGE columnname newcolumnname datatype;

ठीक होना चाहिए और कोई डाउनटाइम नहीं ले जाना चाहिए।

इंडेक्स के लिए क्रिएट इंडेक्स स्टेटमेंट टेबल को लॉक कर देगा। यदि यह एक अप्रयुक्त दास है जैसा आपने उल्लेख किया है, तो यह कोई समस्या नहीं है।

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

BEGIN TRAN;
ALTER TABLE RENAME tablename tablenameold;
ALTER TABLE RENAME newtablename tablename;
DROP TABLE tablenameold;
COMMIT TRAN;

यह अस्थायी रूप से दो बार अंतरिक्ष का उपयोग करने की कीमत पर डाउनटाइम को कम करेगा।


1
MySQL में DDL ट्रांसेक्शनल नहीं है। प्रत्येक DDL स्टेटमेंट एक COMMIT चलाता है। मैंने इसके बारे में लिखा है: dba.stackexchange.com/a/36799/877
RolandoMySQLDBA

0

मुझे भी यह समस्या है और मैंने इस एसक्यूएल का उपयोग किया है:

/*on créé la table COPY SANS les nouveaux champs et SANS les FKs */
CREATE TABLE IF NOT EXISTS prestations_copy LIKE prestations;

/* on supprime les FKs de la table actuelle */
ALTER TABLE `prestations`
DROP FOREIGN KEY `fk_prestations_pres_promos`,
DROP FOREIGN KEY `fk_prestations_activites`;

/* on remet les FKs sur la table copy */
ALTER TABLE prestations_copy 
    ADD CONSTRAINT `fk_prestations_activites` FOREIGN KEY (`act_id`) REFERENCES `activites` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION,
    ADD CONSTRAINT `fk_prestations_pres_promos` FOREIGN KEY (`presp_id`) REFERENCES `pres_promos` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION;

/* On fait le transfert des données de la table actuelle vers la copy, ATTENTION: il faut le même nombre de colonnes */
INSERT INTO prestations_copy
SELECT * FROM prestations;

/* On modifie notre table copy de la façon que l'on souhaite */
ALTER TABLE `prestations_copy`
    ADD COLUMN `seo_mot_clef` VARCHAR(50) NULL;

/* on supprime la table actuelle et renome la copy avec le bon nom de table */
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE prestations;
RENAME TABLE prestations_copy TO prestations;
SET FOREIGN_KEY_CHECKS=1;   

मुझे उम्मीद है कि यह किसी की मदद कर सकता है

सादर,

मर्जी

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