RE:NODE

ბაზები11 წუთის საკითხავი

MySQL-ის გადატანა ახალ ჰოსტზე: dump, იმპორტი, გადართვა

გადაიტანე MySQL ბაზა ახალ სერვერზე: ინვენტარიზაცია, ვერსიების შემოწმება, dump და იმპორტი, მომხმარებლები და definer-ები, მონაცემების შემოწმება და გადართვა მცირე შეფერხებით.

0 მკითხველი

MySQL ბაზის ახალ ჰოსტზე გადატანა ოთხი სამუშაოა: შეადგინე ინვენტარი იმისა, რასაც გადაიტან, დააკოპირე მონაცემები ლოგიკური dump-ით, ხელახლა შექმენი ის, რასაც dump არ ატარებს (მომხმარებლები, grant-ები, ზოგჯერ definer-ები და დროის სარტყლები), შემდეგ გადართე აპლიკაცია მოკლე ფანჯრის განმავლობაში, როცა ძველ სერვერზე არაფერი წერს. რამდენიმე გიგაბაიტამდე ბაზებისთვის მთელი გადატანა ათიდან ოცდაათ წუთამდე დაგეგმილ შეფერხებაში ეტევა, და ბრძანებები ჩვეულებრივი mysqldump და mysql-ია. რისკი კოპირებაში არ არის - ის იმ რამეებშია, რომლებიც ვერ შენიშნე, სანამ აპლიკაცია ახალ სერვერზე არ მიმართე. ეს სახელმძღვანელო ისეა დალაგებული, რომ ისინი პირველად შენიშნო.

სამიზნე მთელ ტექსტში MySQL 8.4 LTS-ია. წყარო შეიძლება იყოს MySQL 5.7, 8.0, 8.4 ან სხვა ოჯახის სერვერი; განსხვავებები იქ არის განხილული, სადაც მნიშვნელოვანია.

ჯერ ინვენტარიზაცია გააკეთე#

გაუშვი ეს ძველ სერვერზე, სანამ რამეს დაგეგმავ. ისინი პასუხობს კითხვებს, რომლებიც წყვეტს, როგორ წარიმართება გადატანა.

sql
-- Size per database: decides dump method and downtimeSELECT table_schema,       ROUND(SUM(data_length + index_length) / 1024 / 1024) AS mbFROM information_schema.tablesGROUP BY table_schema ORDER BY mb DESC;-- Anything not InnoDB: MyISAM is not covered by --single-transactionSELECT table_schema, table_name, engine FROM information_schema.tablesWHERE engine <> 'InnoDB' AND table_schema NOT IN  ('mysql', 'sys', 'information_schema', 'performance_schema');-- Stored code and its definersSELECT routine_schema, routine_name, routine_type, definer FROM information_schema.routinesWHERE routine_schema = 'appdb';SELECT trigger_name, definer FROM information_schema.triggers WHERE trigger_schema = 'appdb';SELECT table_name, definer FROM information_schema.views WHERE table_schema = 'appdb';SELECT event_name, definer, status FROM information_schema.events WHERE event_schema = 'appdb';-- Version, modes and settings that change behaviourSELECT @@version, @@sql_mode, @@lower_case_table_names, @@time_zone,       @@character_set_server, @@collation_server;

ჩაიწერე: ჯამური ზომა, ნებისმიერი არა-InnoDB ცხრილი (ჯერ გადაიყვანე ALTER TABLE t ENGINE=InnoDB-ით, ან შეეგუე lock-იან dump-ს), ყოველი definer, რომელიც აპლიკაციის მომხმარებელი არ არის, არსებობს თუ არა event-ები და ჩართულია თუ არა, და წყაროს ვერსია და sql_mode. ასევე ჩამოწერე ყველა კლიენტი, რომელიც უკავშირდება - არა მხოლოდ მთავარი აპლიკაცია, არამედ cron ამოცანები, worker-ები, ანგარიშების ინსტრუმენტები, დავიწყებული ადმინ პანელი. SELECT user, host, COUNT(*) FROM information_schema.processlist GROUP BY user, host ერთი დღის განმავლობაში მათ უმეტესობას იჭერს; თითოეულს გადართვისას კავშირის პარამეტრების შეცვლა სჭირდება.

ვერსიისა და თავსებადობის შემოწმება#

წყაროMySQL 8.4-შიყურადღება მიაქციე
MySQL 8.4მარტივიdefiner-ები, მომხმარებლები
MySQL 8.0მარტივიmysql_native_password ანგარიშები, წაშლილი ოფციები
MySQL 5.7ლოგიკური dump-ით ჩვეულებრივ კარგადახალი რეზერვირებული სიტყვები, ნულოვანი თარიღები, utf8mb3 ნაგულისხმევები
სხვა MySQL-თავსებადი სერვერებიშემოწმება სჭირდებასერვერზე სპეციფიკური სინტაქსი, ტიპები და collation-ები

ლოგიკური dump ახალ ვერსიაში ბევრად უფრო საიმედოდ იტვირთება, ვიდრე ფიზიკური ასლი, და ვერსიებს შორის ნახტომებს გამოტოვებს: 5.7-ის dump-ის ჩატვირთვა პირდაპირ 8.4-ში შეიძლება, მაშინ როცა 5.7-იდან ადგილზე განახლება ჯერ 8.0-ზე უნდა გაიაროს. 5.7-იდან ჩვეულებრივი პრობლემები:

  • რეზერვირებული სიტყვები. MySQL 8-მა დაარეზერვა ისეთი სიტყვები, როგორიცაა RANK, GROUPS, ROWS, LEAD, LAG, SYSTEM, CUME_DIST და MEMBER. rank-ად სახელდებული სვეტი კარგად იტვირთება (dump იდენტიფიკატორებს ციტირებს), მაგრამ აპლიკაციის არაციტირებული query-ები ტყდება. მოძებნე კოდში.
  • ნულოვანი თარიღები. 0000-00-00 მნიშვნელობები DATE და DATETIME სვეტებში 8.x-ის ნაგულისხმევი sql_mode-ით უარყოფილია. ჯერ წყაროზე შეცვალე ისინი NULL-ით, ან ჩატვირთე session-ის ისეთი sql_mode-ით, რომელიც მათ უშვებს, და შემდეგ გაასწორე.
  • `GROUP BY` query-ები. ONLY_FULL_GROUP_BY 5.7-დან ნაგულისხმევად ჩართულია, მაგრამ ბევრმა ძველმა აპლიკაციამ ის გამორთო. თუ წყაროს sql_mode-ში ის არ არის, აპლიკაციის query-ები ახალ სერვერზე შეიძლება ჩავარდეს, სანამ არ გასწორდება ან session-ის რეჟიმი იმავენაირად არ დაყენდება.

სხვა ოჯახის სერვერიდან, მაგალითად ძველი fork-იდან, გადატანა ამატებს collation-ის სახელებს, რომლებიც სამიზნემ არ იცის (Unknown collation შეცდომები), ტიპებს, რომლებიც MySQL-ს არ აქვს, და სინტაქსს ისეთი ფუნქციებისთვის, როგორიცაა sequence-ები ან სისტემურად ვერსიონირებული ცხრილები. რეალური გადატანის დაგეგმვამდე შეამოწმე dump სატესტო MySQL 8.4 სერვერზე. MySQL 8.4 LTS: რა შეიცვალა 8.0-იდან ცვლილებებს ჩამოთვლის, ხოლო MySQL Shell-ის util.checkForServerUpgrade() მათ კონკრეტული სერვერისთვის აცნობებს.

მონაცემების dump#

ბაზების უმეტესობისთვის - ერთი ბრძანება მანქანაზე, რომელიც ძველ სერვერამდე აღწევს:

bash
$ mysqldump -h old-host -P 3306 -u root -p \    --single-transaction --routines --events --triggers \    --set-gtid-purged=OFF --no-tablespaces --hex-blob \    appdb | zstd -T0 > appdb.sql.zst

შენიშნე, რა არ არის აქ: --databases. მის გარეშე dump არ შეიცავს CREATE DATABASE ან USE ბრძანებას, ასე რომ მისი ჩატვირთვა სხვა სახელის მქონე ბაზაში შეგიძლია. ეს მნიშვნელოვანია ჰოსტინგის სერვერზე, სადაც ბაზა შენთვის საკუთარი სახელით შეიქმნა. --databases appdb-ით dump ნებისმიერ შემთხვევაში შექმნიდა appdb-ს და მასზე გადაერთვებოდა.

--set-gtid-purged=OFF და --no-tablespaces აჩერებს ორ შეცდომას, რომლებიც ყველაზე ხშირად არღვევს არა-superuser-ის მიერ ჩატვირთვას. --hex-blob ბინარულ სვეტებს გზაში ნებისმიერი ტექსტური დამუშავების გადატანის საშუალებას აძლევს. თითოეული flag ახსნილია mysqldump-ით backup-სა და აღდგენაში.

ბაზებისთვის, რომლებიც იმდენად დიდია, რომ ერთნაკადიანი dump და ჩატვირთვა შენს ფანჯარაში არ ეტევა - დაახლოებით 20 GB-ზე მეტი, index-ების მიხედვით - MySQL Shell-ის util.dumpSchemas() და util.loadDump() პარალელურად მუშაობს და გზაში definer-ების მოშორება და თავსებადობის გავრცელებული პრობლემების გასწორება შეუძლია. loadDump-ს სამიზნეზე ჩართული local_infile სჭირდება; მასზე დაყრდნობამდე ეს შეამოწმე.

მომხმარებლები, grant-ები და definer-ები#

ერთი ბაზის dump ატარებს ცხრილებს, მონაცემებს, view-ებს, routine-ებს, trigger-ებსა და event-ებს. ანგარიშებს არ ატარებს. ხელახლა შექმენი ისინი ახალ სერვერზე, ნაცვლად mysql სისტემური სქემის dump-ისა, რომელიც წყაროს ვერსიაზეა მიბმული და 8.4 სერვერში ჩასატვირთი არ არის.

sql
-- On the old server: print each account and its grantsSHOW CREATE USER 'app'@'%';SHOW GRANTS FOR 'app'@'%';

SHOW CREATE USER ანგარიშს მისი პაროლის hash-ით ბეჭდავს, ასე რომ ანგარიშის ხელახლა შექმნა იმავე პაროლით შეიძლება მისი ცოდნის გარეშე - ერთი გამონაკლისით. mysql_native_password-ზე მყოფი ანგარიშები ამ plugin-ის hash-ს ატარებს, ხოლო MySQL 8.4-ზე plugin ნაგულისხმევად გამორთულია, ასე რომ ხელახლა შექმნილი ანგარიში ვერ შევა. მის ნაცვლად დააყენე ახალი პაროლი ნაგულისხმევი caching_sha2_password-ით, რაც ასევე კარგი მომენტია იმ მონაცემების როტაციისთვის, რომლებიც შეიძლება წლების განმავლობაში ძველ კონფიგურაციის ფაილებში იდო. MySQL-ის მომხმარებლები და პრივილეგიები ანგარიშებისა და როლების სწორად ხელახლა შექმნას ფარავს.

definer-ები მეორე ნახევარია. ყოველი view, routine, trigger და event ასახელებს ანგარიშს, რომელმაც ის შექმნა, და ისეთის ჩატვირთვას, რომლის definer-იც შენ არ ხარ, 8.4-ზე SET_ANY_DEFINER სჭირდება; თუ ანგარიში ახალ სერვერზე საერთოდ არ არსებობს, ობიექტი გამოყენებისასაც მარცხდება. ყველაზე მარტივი გზა, როცა ჩვეულებრივი აპლიკაციის მომხმარებლით ტვირთავ, definer-ების მოშორებაა, რაც ყველაფრის definer-ად შენ გაქცევს:

bash
$ zstd -dc appdb.sql.zst \  | sed -E 's/DEFINER=`[^`]+`@`[^`]+`//g' \  | mysql -h new-host -P 30412 -u app -p appdb_new

RE:NODE-ზე MySQL სერვერი იწყება აპლიკაციის ბაზით, მისთვის განკუთვნილი აპლიკაციის მომხმარებლითა და root პაროლით, ყველა გენერირებული. ჩატვირთე იმ ბაზის სახელში, მიმართე აპლიკაცია იმ მომხმარებელზე, და root მხოლოდ მაშინ გამოიყენე, თუ dump-ს ისეთი პრივილეგიები სჭირდება, რაც აპლიკაციის მომხმარებელს არ აქვს.

ჩატვირთვა და შემოწმება#

ჩატვირთე ახალ სერვერში, შემდეგ დაამტკიცე, რომ იმუშავა, სანამ რომელიმე კლიენტი მასზე მიმართავს.

bash
$ zstd -dc appdb.sql.zst | mysql -h new-host -P 30412 -u root -p appdb_new

შეამოწმე რიცხვებით და არა თვალით. information_schema.tables.table_rows InnoDB-ისთვის შეფასებაა და სერვერებს შორის განსხვავდება მაშინაც კი, როცა მონაცემები იდენტურია; დათვალე ზუსტად:

sql
-- Generate exact counts for every table; run the output on both servers and diffSELECT CONCAT('SELECT ''', table_name, ''' AS t, COUNT(*) AS n FROM `',              table_name, '` UNION ALL') AS qFROM information_schema.tablesWHERE table_schema = DATABASE() AND table_type = 'BASE TABLE';

ბოლო ხაზიდან ამოიღე ბოლო UNION ALL, გაუშვი query ორივე მხარეს და შეადარე. შემდეგ შეადარე ის, რაც დათვლებს გამორჩება: უახლესი ხაზი შენს ყველაზე დატვირთულ ცხრილებში (SELECT MAX(id), MAX(updated_at) FROM orders), რამდენიმე ცნობილი ჩანაწერი აქცენტიანი ტექსტით, რომ სიმბოლოების ნაკრების დაზიანება დაიჭირო (იხილე utf8mb4 და collation-ები), routine-ებისა და event-ების არსებობა (SHOW PROCEDURE STATUS WHERE Db = DATABASE(), SHOW EVENTS), და რომ AUTO_INCREMENT მნიშვნელობები გადმოვიდა (SHOW CREATE TABLE მათ მოიცავს).

ბოლოს გაუშვი აპლიკაცია ახალ სერვერზე - აპლიკაციის staging ასლი შეცვლილი კავშირის პარამეტრებით - და გამოიყენე. შედი, შექმენი რამე, გაუშვი დაგეგმილი ამოცანები ხელით. ეს ის ნაბიჯია, რომელიც პოულობს რეზერვირებულ სიტყვებს, sql_mode-ის განსხვავებებსა და დაკარგულ grant-ებს, სანამ ძველი სერვერი ჯერ კიდევ მოქმედია.

პარამეტრები, რომლებიც არ გადადის#

dump მონაცემებს აკოპირებს და არა სერვერის ქცევას. ეს ის განსხვავებებია, რომლებიც აპლიკაციას უცნაურად აქცევინებს თავს გადატანის შემდეგ, რომელიც „იმუშავა“:

  • დროის სარტყლები. თუ აპლიკაცია დასახელებულ სარტყლებს იყენებს (CONVERT_TZ(ts, 'UTC', 'Europe/Berlin') ან SET time_zone = 'Europe/Berlin'), ახალ სერვერს დროის სარტყლების ცხრილები უნდა ჰქონდეს ჩატვირთული, თორემ CONVERT_TZ აბრუნებს NULL-ს, ხოლო SET time_zone მარცხდება შეცდომით Unknown or incorrect time zone. გამოცადე SELECT CONVERT_TZ('2026-01-01 00:00', 'UTC', 'Europe/Berlin');-ით. რიცხვითი წანაცვლებები, როგორიცაა '+00:00', ყოველთვის მუშაობს.
  • `lower_case_table_names`. Windows-ის ან macOS-ის სერვერიდან მოსულ ბაზას, სადაც ცხრილების სახელები რეგისტრის მიმართ არამგრძნობიარეა, შეიძლება ჰქონდეს query-ები, რომლებიც Orders-სა და orders-ს ურთიერთშემცვლელად მიმართავს. Linux-ზე ნაგულისხმევი რეგისტრის მიმართ მგრძნობიარეა, და ცვლადის შეცვლა სერვერის ინიციალიზაციის შემდეგ შეუძლებელია. გაასწორე query-ები და არა სერვერი.
  • `sql_mode`. შეადარე @@GLOBAL.sql_mode ორივეზე. თუ ძველი სერვერი უფრო თავისუფალ რეჟიმზე მუშაობდა, ან გაასწორე აპლიკაცია, ან დროებით გამოსავლად დააყენებინე მას ძველი რეჟიმი თავისი session-ისთვის დაკავშირებისას.
  • event scheduler. event-ები კოპირდება, მაგრამ მხოლოდ მაშინ ეშვება, თუ event_scheduler ახალ სერვერზე ON-ია (8.x-ში ნაგულისხმევი). შეამოწმე, რომ event-ები, რომელთა გაშვებასაც ელი, ეშვება - და რომ ის, რომლებიც ძველ სერვერზე გამორთე, ახალზეც გამორთულია.
  • მანძილი. თუ ბაზა აპლიკაციიდან უფრო შორს გადადის, ყოველი query დამატებით round trip-ს იხდის. აპლიკაცია, რომელიც გვერდზე ორმოც query-ს აკეთებს, 20 ms დამატებით დაყოვნებას თითქმის მთელ წამად აღიქვამს. RE:NODE-ის სერვერები გერმანიაშია; აპლიკაცია ბაზასთან ახლოს შეინახე.

გადართვა#

ჩვეულებრივი აპლიკაციისთვის საიმედო გადართვა მოკლე დაგეგმილი ფანჯარაა:

  1. შეამცირე timeout ყველაფერზე, რაც ქეშირდება. თუ აპლიკაცია ბაზას შენს კონტროლქვეშ მყოფი DNS სახელით პოულობს, ერთი დღით ადრე შეამცირე მისი TTL, რომ ცვლილება სწრაფად გავრცელდეს.
  2. შეაჩერე ჩაწერები ძველ სერვერზე. გადაიყვანე აპლიკაცია maintenance რეჟიმში და გააჩერე worker-ები და cron ამოცანები. თუ ძველ სერვერზე root გაქვს, SET GLOBAL super_read_only = ON უზრუნველყოფს, რომ სხვაც არაფერი ჩაწეროს - მათ შორის ის დავიწყებული კლიენტი, რომელიც სიაში არ შეიტანე.
  3. აიღე საბოლოო dump და ჩატვირთე ახალ სერვერში, სატესტო ასლის ჩანაცვლებით. სამიზნე ბაზის წინასწარ წაშლა და ხელახლა შექმნა თავიდან აგაცილებს სატესტო გაშვების ცხრილების შერევას.
  4. შეამოწმე იმავე დათვლებითა და შემოწმებებით, როგორც ადრე. ახლა ეს სწრაფია, რადგან სკრიპტად აქციე.
  5. შეცვალე ყოველი კლიენტის კავშირის პარამეტრები - host, port, მომხმარებელი, პაროლი, ბაზის სახელი - და გაუშვი აპლიკაცია, შემდეგ worker-ები და ამოცანები.
  6. ადევნე თვალი აპლიკაციის შეცდომებსა და ახალი სერვერის კავშირებს პირველი საათის განმავლობაში.

ძველი სერვერი, read-only რეჟიმში, მინიმუმ რამდენიმე დღე შეინახე. თუ რამე გამოჩნდება, შეგიძლია მასთან შეადარო; თუ პირველ საათში რამე სერიოზულად აირევა, უკან გადართვა კონფიგურაციის ცვლილებაა და არა აღდგენა. უფრო დიდი ბაზებისთვის, სადაც dump და ჩატვირთვა მოკლე ფანჯარაში არ ჩაეტევა, ალტერნატივაა საწყისი ასლის ჩატვირთვა, ახალი სერვერის ძველის replica-დ გადაქცევა CHANGE REPLICATION SOURCE TO-ით, სანამ არ დაეწევა, შემდეგ კი წამებში გადართვა. ამას სჭირდება binary logging და რეპლიკაციის პრივილეგიები წყაროზე, ქსელური წვდომა ორს შორის და ორივე სერვერის კონფიგურაციაზე კონტროლი - ღირს დიდი დატვირთული ბაზისთვის, უმეტესობისთვის ზედმეტია. migration-ები შეფერხების გარეშე აპლიკაციის მუშა მდგომარეობაში შენარჩუნების სქემის ცვლილებების მხარეს ფარავს, ხოლო კონკრეტულად WordPress საიტის გადატანა WordPress-ის ახალ ჰოსტზე გადატანაშია.

FAQ#

რამდენ ხანს გრძელდება MySQL ბაზის მიგრაცია?

კოპირებას დაახლოებით იმდენი დრო სჭირდება, რამდენიც dump-სა და ჩატვირთვას ერთად, და ჩატვირთვა ნელი ნახევარია, რადგან ყოველი index ხელახლა იგება. 1 GB ბაზა ჩვეულებრივ რამდენიმე წუთში გადადის; 20 GB-ს ერთნაკადიან რეჟიმში შეიძლება საათი ან მეტი დასჭირდეს. გაიმეორე ერთხელ რეალური მონაცემებით და გაზომე დრო - ეს რიცხვი, პლუს შემოწმება, შენი შეფერხებაა.

შემიძლია მიგრაცია ყოველგვარი შეფერხების გარეშე?

თითქმის, რეპლიკაციით: ჩატვირთე ასლი, გაიმეორე ცვლილებები ძველი სერვერიდან, სანამ ახალი აქტუალური გახდება, შემდეგ კლიენტები გადართე. ამას ორივე სერვერზე პრივილეგიები და კონფიგურაციაზე წვდომა სჭირდება. პატარა აპლიკაციების უმეტესობისთვის დაგეგმილი ათწუთიანი ფანჯარა უფრო მარტივი და უსაფრთხოა.

მჭირდება mysql სისტემური ბაზის კოპირება?

არა, და სხვა ვერსიიდან მისი ჩატვირთვა MySQL 8.4-ში არ უნდა მოხდეს. ხელახლა შექმენი საჭირო ანგარიშები SHOW CREATE USER-ისა და SHOW GRANTS-ის გამონატანით, და გადააყენე პაროლები ყველა ანგარიშისთვის, რომელიც mysql_native_password-ს იყენებდა.

რატომ ამბობს ჩემი აპლიკაცია მიგრაციის შემდეგ, რომ მომხმარებელი არ არსებობს?

ან ანგარიში ხელახლა არ შექმნილა, ან მისი host-ის ნიმუში აპლიკაციის ახალ მისამართს არ ემთხვევა, ან ის mysql_native_password-ს იყენებს, რომელიც 8.4-ზე ნაგულისხმევად გამორთულია. თუ შეცდომა მის ნაცვლად definer-ს ახსენებს, view ან routine ასახელებს ანგარიშს, რომელიც ახალ სერვერზე არ არსებობს - მოაშორე ან ხელახლა შექმენი definer.

შემიძლია phpMyAdmin-ით ბაზის გადატანა?

პატარა ბაზებისთვის - კი: გაიტანე SQL-ად ძველი სერვერიდან და შემოიტანე ახალზე. ის ვებ მოთხოვნის შიგნით მუშაობს, ასე რომ დიდი ბაზები ატვირთვისა და დროის ლიმიტებს ეჯახება. phpMyAdmin-ით იმპორტი და ექსპორტი ლიმიტებსა და პარამეტრებს ფარავს; რამდენიმე ასეულ მეგაბაიტზე მეტისთვის mysqldump და mysql კლიენტი გამოიყენე.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000