RE:NODE

Databáze11 min čtení

Migrace MySQL na nový hosting: dump, import, přepnutí

Přesun databáze MySQL na nový server: inventura, kontrola verzí, dump a import, uživatelé a definery, ověření dat a přepnutí s minimálním výpadkem.

1 čtenář

Přesun databáze MySQL na nový hosting jsou čtyři úkoly: udělat inventuru toho, co přesouváš, zkopírovat data logickým dumpem, znovu vytvořit to, co dump nepřenese (uživatele, oprávnění, někdy definery a časová pásma), a pak aplikaci přepnout v krátkém okně, kdy na starý server nic nezapisuje. U databází do několika gigabajtů se celý přesun vejde do plánovaného výpadku deset až třicet minut a příkazy jsou obyčejné mysqldump a mysql. Riziko není v kopírování - je v tom, čeho sis nevšiml, dokud aplikace nezačala mířit na nový server. Tento návod je seřazený tak, abys toho všiml dřív.

Cílem je všude MySQL 8.4 LTS. Zdrojem může být MySQL 5.7, 8.0, 8.4 nebo server z jiné rodiny; rozdíly jsou popsané tam, kde na nich záleží.

Nejdřív inventura#

Spusť tyto dotazy na starém serveru dřív, než cokoli naplánuješ. Odpovídají na otázky, které rozhodují o průběhu přesunu.

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;

Zapiš si: celkovou velikost, všechny tabulky mimo InnoDB (nejdřív je převeď pomocí ALTER TABLE t ENGINE=InnoDB, nebo se smiř se zamčeným dumpem), každého definera, který není uživatel aplikace, zda existují eventy a jsou zapnuté, a verzi a sql_mode zdroje. Sepiš také každého klienta, který se připojuje - nejen hlavní aplikaci, ale i cron úlohy, workery, reportovací nástroje a zapomenutý admin panel. Dotaz SELECT user, host, COUNT(*) FROM information_schema.processlist GROUP BY user, host spouštěný po celý den zachytí většinu z nich; každému je při přepnutí potřeba změnit nastavení připojení.

Kontrola verzí a kompatibility#

ZdrojDo MySQL 8.4Na co dát pozor
MySQL 8.4Bez potížíDefinery, uživatelé
MySQL 8.0Bez potížíÚčty s mysql_native_password, odstraněné volby
MySQL 5.7S logickým dumpem obvykle v pořádkuNová rezervovaná slova, nulová data, výchozí utf8mb3
Jiné servery kompatibilní s MySQLJe třeba ověřitSyntaxe, typy a kolace specifické pro server

Logický dump se do novější verze načte mnohem spolehlivěji než fyzická kopie a přeskakuje skoky mezi verzemi: dump z 5.7 jde načíst přímo do 8.4, kdežto upgrade na místě z 5.7 musí projít přes 8.0. Obvyklé potíže z 5.7:

  • Rezervovaná slova. MySQL 8 rezervovalo slova jako RANK, GROUPS, ROWS, LEAD, LAG, SYSTEM, CUME_DIST a MEMBER. Sloupec s názvem rank se načte bez problémů (dump identifikátory uvozuje), ale dotazy aplikace bez uvozovek se rozbijí. Prohledej kód.
  • Nulová data. Hodnoty 0000-00-00 ve sloupcích DATE a DATETIME se při výchozím sql_mode verze 8.x odmítají. Nejdřív je na zdroji oprav na NULL, nebo načítej s sql_mode relace, který je povolí, a oprav je potom.
  • Dotazy s `GROUP BY`. ONLY_FULL_GROUP_BY je od 5.7 zapnuté ve výchozím stavu, ale mnoho starých aplikací ho vypnulo. Pokud sql_mode zdroje tuto volbu nemá, mohou dotazy aplikace na novém serveru selhávat, dokud se neopraví nebo dokud se režim relace nenastaví stejně.

Přesun ze serveru z jiné rodiny, například ze staršího forku, přidává názvy kolací, které cíl nezná (chyby Unknown collation), typy, které MySQL nemá, a syntaxi pro funkce jako sekvence nebo systémově verzované tabulky. Dump si nejdřív vyzkoušej na zkušebním serveru MySQL 8.4, než naplánuješ ten skutečný přesun. Změny oproti 8.0 vypisuje MySQL 8.4 LTS: co se změnilo a pro konkrétní server je hlásí util.checkForServerUpgrade() v MySQL Shellu.

Dump dat#

U většiny databází stačí jeden příkaz na stroji, který dosáhne na starý server:

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

Všimni si, co tam není: --databases. Bez něj dump neobsahuje příkaz CREATE DATABASE ani USE, takže ho můžeš načíst do databáze s jiným názvem. Na hostovaném serveru, kde ti databázi vytvořili s vlastním názvem, na tom záleží. S --databases appdb by dump vytvořil appdb a přepnul se do ní bez ohledu na cokoli.

--set-gtid-purged=OFF a --no-tablespaces zabraňují dvěma chybám, které nejčastěji zlomí načtení uživatelem bez superuživatelských práv. --hex-blob zajistí, že binární sloupce přežijí jakékoli textové zacházení po cestě. Každý přepínač vysvětluje záloha a obnova přes mysqldump.

U databází tak velkých, že se jednovláknový dump a načtení nevejde do okna - zhruba nad 20 GB, podle indexů - pracují paralelně util.dumpSchemas() a util.loadDump() z MySQL Shellu a umějí za běhu odstraňovat definery a opravovat běžné potíže s kompatibilitou. loadDump potřebuje na cíli zapnuté local_infile; ověř to dřív, než se na něj spolehneš.

Uživatelé, oprávnění a definery#

Dump jedné databáze nese tabulky, data, pohledy, rutiny, triggery a eventy. Účty nenese. Znovu je vytvoř na novém serveru místo dumpování systémového schématu mysql, které je svázané s verzí zdroje a není to nic, co by se mělo načítat do serveru 8.4.

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

SHOW CREATE USER vypíše účet s hashem hesla, takže účet jde znovu vytvořit se stejným heslem, aniž bys ho znal - až na jednu výjimku. Účty s mysql_native_password nesou hash tohoto pluginu a v MySQL 8.4 je plugin ve výchozím stavu vypnutý, takže znovu vytvořený účet se nepřihlásí. Nastav místo toho nové heslo s výchozím caching_sha2_password, což je také dobrá chvíle obměnit přihlašovací údaje, které mohly roky ležet ve starých konfiguračních souborech. Řádné znovuvytvoření účtů a rolí popisuje uživatelé a oprávnění v MySQL.

Definery jsou druhá polovina. Každý pohled, rutina, trigger a event jmenuje účet, který ho vytvořil, a načtení takového, jehož definerem je někdo jiný než ty, vyžaduje v 8.4 oprávnění SET_ANY_DEFINER; pokud účet na novém serveru vůbec neexistuje, objekt selže i při použití. Nejjednodušší cesta při načítání jako běžný uživatel aplikace je definery odstranit, čímž se ty staneš definerem všeho:

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

Na RE:NODE se server MySQL vytvoří s aplikační databází, aplikačním uživatelem pro ni a heslem roota, vše vygenerované. Načítej do názvu této databáze, nasměruj aplikaci na toho uživatele a roota použij jen tehdy, když dump vyžaduje oprávnění, která aplikační uživatel nemá.

Načtení a ověření#

Načti do nového serveru a dokaž, že to fungovalo, dřív než na něj ukáže jakýkoli klient.

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

Ověřuj čísly, ne od oka. information_schema.tables.table_rows je u InnoDB odhad a liší se mezi servery, i když jsou data shodná; počítej přesně:

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';

Z posledního řádku odstraň závěrečné UNION ALL, spusť dotaz na obou stranách a porovnej. Pak porovnej to, co počty minou: nejnovější řádek v nejvytíženějších tabulkách (SELECT MAX(id), MAX(updated_at) FROM orders), pár známých záznamů s diakritikou, aby se odhalilo poškození znakové sady (viz utf8mb4 a kolace), přítomnost rutin a eventů (SHOW PROCEDURE STATUS WHERE Db = DATABASE(), SHOW EVENTS) a zda se přenesly hodnoty AUTO_INCREMENT (SHOW CREATE TABLE je obsahuje).

Nakonec spusť aplikaci proti novému serveru - testovací kopii aplikace se změněným nastavením připojení - a používej ji. Přihlas se, něco vytvoř, spusť ručně plánované úlohy. Tento krok najde rezervovaná slova, rozdíly v sql_mode a chybějící oprávnění, dokud je živým serverem pořád ten starý.

Nastavení, která se nepřenášejí#

Dump kopíruje data, ne chování serveru. Tohle jsou rozdíly, kvůli kterým se aplikace po přesunu, který „fungoval“, chová divně:

  • Časová pásma. Pokud aplikace používá pojmenovaná pásma (CONVERT_TZ(ts, 'UTC', 'Europe/Berlin') nebo SET time_zone = 'Europe/Berlin'), nový server musí mít načtené tabulky časových pásem, jinak CONVERT_TZ vrací NULL a SET time_zone selže s chybou Unknown or incorrect time zone. Otestuj to pomocí SELECT CONVERT_TZ('2026-01-01 00:00', 'UTC', 'Europe/Berlin');. Číselné offsety jako '+00:00' fungují vždy.
  • `lower_case_table_names`. Databáze ze serveru s Windows nebo macOS, kde se v názvech tabulek nerozlišují velká a malá písmena, může obsahovat dotazy, které se zaměnitelně odkazují na Orders a orders. V Linuxu je výchozí rozlišování velikosti písmen a proměnnou nelze po inicializaci serveru změnit. Oprav dotazy, ne server.
  • `sql_mode`. Porovnej @@GLOBAL.sql_mode na obou. Pokud starý server běžel s volnějším režimem, buď oprav aplikaci, nebo ji nech nastavit starý režim pro relaci při připojení jako provizorium.
  • Plánovač eventů. Eventy se zkopírují, ale běží jen tehdy, když je na novém serveru event_scheduler nastavený na ON (v 8.x výchozí). Ověř, že eventy, které čekáš, běží - a že ty, které jsi na starém serveru vypnul, jsou vypnuté i na novém.
  • Vzdálenost. Pokud se databáze přesune dál od aplikace, každý dotaz platí za dodatečný round trip. Aplikace, která na stránku udělá čtyřicet dotazů, pocítí 20 ms navíc jako většinu vteřiny. Servery RE:NODE jsou v Německu; aplikaci drž blízko databáze.

Přepnutí#

U běžné aplikace je spolehlivým přepnutím krátké plánované okno:

  1. Sniž timeout u všeho, co se cachuje. Pokud aplikace najde databázi přes DNS jméno, které ovládáš, sniž jeho TTL den předem, aby se změna rychle rozšířila.
  2. Zastav zápisy na starý server. Dej aplikaci do režimu údržby a zastav workery a cron úlohy. Pokud máš na starém serveru roota, SET GLOBAL super_read_only = ON zajistí, že nezapisuje nic jiného - ani zapomenutý klient, kterého jsi neuvedl.
  3. Vytvoř finální dump a načti ho do nového serveru místo zkušební kopie. Předchozí zahození a znovuvytvoření cílové databáze zabrání tomu, aby se přimíchaly tabulky ze zkoušky.
  4. Ověř stejnými počty a kontrolami jako dřív. Teď je to rychlé, protože jsi to naskriptoval.
  5. Změň nastavení připojení každého klienta - host, port, uživatel, heslo, název databáze - a spusť aplikaci, pak workery a úlohy.
  6. Sleduj chyby aplikace a připojení na novém serveru po první hodinu.

Starý server nech minimálně několik dní v režimu jen pro čtení. Když se něco objeví, můžeš s ním porovnávat; když se v první hodině něco pokazí vážně, návrat je změna konfigurace, ne obnova. U větších databází, kde by se dump a načtení nevešly do krátkého okna, je alternativou načíst počáteční kopii, udělat z nového serveru repliku starého pomocí CHANGE REPLICATION SOURCE TO, dokud nedožene stav, a pak přepnout během vteřin. To vyžaduje binární logování a oprávnění k replikaci na zdroji, síťový přístup mezi oběma servery a kontrolu nad konfigurací obou - pro velkou vytíženou databázi se to vyplatí, pro většinu je to zbytečné. Stranu změn schématu při udržení aplikace v provozu popisuje migrace bez výpadku a přesun webu na WordPressu zvlášť migrace WordPressu na nový hosting.

FAQ#

Jak dlouho trvá migrace databáze MySQL?

Kopírování trvá zhruba tak dlouho, jako dump plus načtení, a načtení je ta pomalá polovina, protože se znovu sestavují všechny indexy. Databáze o velikosti 1 GB se obvykle přesune za pár minut; 20 GB může jednovláknově trvat hodinu i víc. Jednou si to vyzkoušej se skutečnými daty a změř čas - to číslo plus ověření je tvůj výpadek.

Mohu migrovat úplně bez výpadku?

Skoro, pomocí replikace: načti kopii, replikuj změny ze starého serveru, dokud nový není aktuální, a pak přepni klienty. Vyžaduje to oprávnění a přístup ke konfiguraci obou serverů. Pro většinu malých aplikací je plánované desetiminutové okno jednodušší a bezpečnější.

Musím kopírovat systémovou databázi mysql?

Ne, a neměl bys načítat tu z jiné verze do MySQL 8.4. Potřebné účty znovu vytvoř z výstupu SHOW CREATE USER a SHOW GRANTS a obnov hesla u každého účtu, který používal mysql_native_password.

Proč aplikace po migraci hlásí, že uživatel neexistuje?

Buď se účet znovu nevytvořil, nebo jeho vzor hostitele neodpovídá nové adrese aplikace, nebo používá mysql_native_password, který je v 8.4 ve výchozím stavu vypnutý. Pokud chyba zmiňuje definera, nějaký pohled nebo rutina jmenuje účet, který na novém serveru neexistuje - definera odstraň nebo vytvoř znovu.

Mohu k přesunu databáze použít phpMyAdmin?

U malých databází ano: exportuj jako SQL ze starého serveru a naimportuj na novém. Běží uvnitř webového požadavku, takže velké databáze narazí na limity nahrávání a času. Limity a nastavení popisuje import a export v phpMyAdminu; nad několik stovek megabajtů použij mysqldump a klienta mysql.


Komentáře

Zcela anonymně: žádný účet, žádný e-mail, žádné cookie. Ukládáme jen jméno, které napíšeš, text a čas - nic dalšího. Počet odkazů je omezený a značkování se nevykresluje.

0/2000