Linux commando: mysqldump
Elke website die iets opslaat, slaat dat op in een database, en een database is het ene onderdeel van een server dat je niet kunt kopieren door een map te verslepen. De bestanden staan in een formaat dat alleen de database-engine begrijpt, ze veranderen terwijl je ze leest, en de helft van wat ertoe doet staat in geheugen dat nog niet naar schijf is geschreven. Als je een kopie van een MySQL- of MariaDB-database nodig hebt, kopieer je dus geen bestanden. Je vraagt de server om een reeks instructies uit te schrijven waarmee de database opnieuw is op te bouwen. Het programma dat die vraag stelt is mysqldump.
1. De basis
De taak van mysqldump is smal: het verbindt als gewone client met een draaiende MySQL- of MariaDB-server, leest de structuur en de rijen uit die je hebt gevraagd, en schrijft platte SQL-tekst naar standaarduitvoer. Die tekst is een recept. Voer het terug aan een databaseserver en je krijgt je gegevens terug.
Dit heet een logische backup. Die slaat op wat de gegevens betekenen (een CREATE TABLE-statement en een lijst INSERT-statements) in plaats van hoe de gegevens zijn opgeslagen (de ruwe pagina's in de ibdata- en .ibd-bestanden op schijf). Dat verschil doet er meer toe dan het klinkt.
Logische backup (mysqldump) | Fysieke backup (bestandskopie, snapshot) | |
|---|---|---|
| Uitvoer | Leesbare SQL-tekst | Binaire databestanden |
| Omvang | Groter, maar comprimeert uitstekend | Ongeveer de omvang op schijf |
| Snelheid | Trager te maken, veel trager terug te zetten | Snel in beide richtingen |
| Overdraagbaarheid | Zet terug op een andere versie, een andere engine, een andere machine | Vraagt meestal dezelfde serverversie en hetzelfde platform |
| Bewerkbaar | Ja, het is een tekstbestand | Nee |
| Gedeeltelijk terugzetten | Ja, haal er een tabel uit met een teksteditor | Lastig |
Twee eigenschappen maken mysqldump het standaardgereedschap voor dagelijks werk. Ten eerste is het alleen-lezen op je gegevens. Het draait SELECT-statements en leest tabeldefinities; het wijzigt nooit de database die het uitleest. Ten tweede is het overal. Het zit in het clientpakket van elke MySQL- en MariaDB-installatie, op elk hostingplatform, in elke Docker-image. Er valt niets te installeren en niets te licenseren.
Het kopieert je database niet. Het schrijft de instructies op om je database opnieuw te bouwen.
Het juiste mentale model:
mysqldumpis geen backupsysteem. Het is een programma dat een database omzet in tekst op standaarduitvoer. Al het andere dat je van een backup wilt - een schema, een bestemming, compressie, bewaartermijnen, een testterugzetting - is jouw werk, gedaan met gewone shellgereedschappen eromheen.
1.1 Welk programma heb je eigenlijk?
Controleer dit voordat je een script schrijft, want het antwoord verschilt per systeem:
$ mysqldump --version
mysqldump Ver 8.0.46-0ubuntu0.24.04.3 for Linux on x86_64 ((Ubuntu))
Dat is de MySQL-client van Oracle. Op een MariaDB-systeem krijg je iets anders, en paragraaf 7.2 legt uit waarom de naam lang niet altijd mysqldump is.
2. Waar komt de naam vandaan?
De naam is een simpele samenstelling, zonder verborgen grap:
mysqldump = MySQL + dump
De interessante helft is dump. In de informatica is een dump een volledige, onbewerkte uitdraai van een interne toestand: een core dump, een memory dump, een hex dump. Het woord draagt de belofte dat er niets is samengevat en niets is weggelaten. Dat is precies de belofte die dit programma over je tabellen doet.
Het schept ook de juiste verwachting over het formaat. Een dump is geen slim archief. Het is de inhoud, op de meest voor de hand liggende manier uitgeschreven, en daarom is de uitvoer leesbare SQL die je in een teksteditor kunt openen en om drie uur 's nachts met de hand kunt repareren.
MariaDB heeft het programma inmiddels hernoemd naar mariadb-dump, met dezelfde opbouw en een andere leveranciersnaam ervoor. Zie paragraaf 7.2.
3. Een korte geschiedenis
mysqldump is ouder dan de meeste databases waarop het wordt gebruikt, en het begon helemaal niet bij MySQL AB. Het bronbestand draagt nog steeds het commentaarblok van de oorspronkelijke auteur, en dat is de moeite van het lezen waard omdat het het karakter van het gereedschap verklaart:
/* mysqldump.c - Dump a tables contents and format to an ASCII file
**
** The author's original notes follow :-
**
** AUTHOR: Igor Romanenko (Dit e-mailadres wordt beveiligd tegen spambots. JavaScript dient ingeschakeld te zijn om het te bekijken. )
** DATE: December 3, 1994
** WARRANTY: None, expressed, impressed, implied
** or other
** STATUS: Public domain
*/
Een publiek-domeingereedschap uit december 1994, geschreven door een enkeling, met een uitdrukkelijke belofte van geen enkele garantie. MySQL AB nam het over, en de functies die je dagelijks gebruikt zijn stuk voor stuk door iemand anders bijgedragen in het decennium daarna. De koptekst legt vast wie wat toevoegde:
| Datum | Mijlpaal |
|---|---|
| 3 december 1994 | Igor Romanenko schrijft het origineel en geeft het vrij in het publieke domein |
| midden jaren negentig | Aangepast en geoptimaliseerd voor MySQL door Michael Widenius, Sinisa Milivojevic en Jani Tolonen |
| 10 september 1998 | Jim Faucette voegt -w / --where toe, zodat je een deel van een tabel kunt dumpen |
| 2001 | Gary Huntress voegt XML-uitvoer toe, door Jani Tolonen in het gereedschap ingepast |
| 6 juni 2002 | Peter Zaitsev voegt --single-transaction toe, de optie die consistente dumps zonder locks mogelijk maakt |
| 10 juni 2003 | Alexander Barkov voegt afhandeling van SET NAMES toe, en daarom overleven tekensets een dump |
| 2010 | MariaDB splitst zich af van MySQL en neemt het gereedschap mee |
| 2015 | MySQL 5.7 levert mysqlpump, een parallelle herschrijving die het moest vervangen |
| 2019 | MariaDB 10.4 introduceert de naam mariadb-dump als symlink die naar mysqldump wijst |
| 2020 | MariaDB 10.5 draait het om: mariadb-dump wordt het echte programma en mysqldump de symlink |
| 2023 | MySQL 8.0.34 verklaart mysqlpump verouderd en verwijst gebruikers terug naar mysqldump |
De laatste twee rijen zijn de interessante. De vervanger is verouderd verklaard en het dertig jaar oude origineel is nog steeds het aanbevolen gereedschap. Dat is geen nostalgie: dat is wat er gebeurt als een formaat eenvoudig genoeg is dat al het andere in het ecosysteem het heeft leren lezen.
3.1 Het versienummer dat het versienummer niet is
De uitvoer van MariaDB verwart mensen de eerste keer:
$ mariadb-dump --version
mariadb-dump from 11.4.12-MariaDB, client 10.19 for debian-linux-gnu (x86_64)
De server is 11.4.12, maar de "client" zegt 10.19. Die 10.19 is een constante in de broncode die DUMP_VERSION heet, en die beschrijft het dumpformaat, niet het programma. Hij verandert alleen als de opbouw van de gegenereerde SQL verandert. Een oud nummer zien is normaal en betekent niet dat je met verouderd gereedschap werkt.
4. Eenvoudige toepassingen
4.1 De eenvoudigst mogelijke dump
Noem een database. De SQL gaat naar je terminal:
$ mysqldump -u root -p sitedb
De vlag -u (kort voor user) noemt het account, en -p (kort voor password) laat het programma om het wachtwoord vragen. Doe dit een keer, kijk hoe de SQL voorbijrolt, en het gereedschap is niet mysterieus meer. Stuur het daarna door naar een bestand, zoals je het altijd zult gebruiken:
$ mysqldump -u root -p sitedb > sitedb.sql
Let op waar de omleiding staat. mysqldump schrijft naar standaarduitvoer, dus de shell maakt het bestand, niet het programma. Dat ene feit verklaart de meeste pipelines verderop in dit artikel, en de meeste valkuilen in paragraaf 7.3.
4.2 Lezen wat eruit komt
Hier is een echte dump van een kleine tabel, met het commentaarblok bovenaan weggelaten om ruimte te sparen:
$ mysqldump -u root -p --skip-dump-date sitedb j6_content
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
DROP TABLE IF EXISTS `j6_content`;
CREATE TABLE `j6_content` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`alias` varchar(255) NOT NULL,
`state` tinyint(4) NOT NULL DEFAULT 1,
`created` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
LOCK TABLES `j6_content` WRITE;
/*!40000 ALTER TABLE `j6_content` DISABLE KEYS */;
INSERT INTO `j6_content` VALUES
(1,'Welcome','welcome',1,'2026-08-01 10:00:00'),
(2,'About us','about-us',1,'2026-08-02 11:30:00'),
(3,'Draft post','draft-post',0,'2026-08-03 09:15:00');
/*!40000 ALTER TABLE `j6_content` ENABLE KEYS */;
UNLOCK TABLES;
Vijf dingen zijn het aanwijzen waard, want ze beantwoorden vragen die mensen voortdurend over dumps stellen.
- De
/*!40101 ... */-opmerkingen zijn uitvoerbaar. Dit is de versiecommentaar-syntaxis van MySQL. Een gewone SQL-parser ziet commentaar en slaat het over; een MySQL-server van versie 4.01.01 of nieuwer haalt de markering weg en voert het statement erbinnen uit. Zo blijft een dumpbestand geschikt voor zowel oude als nieuwe servers. DROP TABLE IF EXISTSkomt eerst. Terugzetten is met opzet destructief: het vervangt de tabel in plaats van erin samen te voegen. Zie paragraaf 9.- De rijen zijn gebundeld. Een
INSERTdraagt meerdereVALUES-lijsten. Dat is--extended-insert, standaard aan, en het maakt terugzetten veel sneller dan een statement per rij. TIME_ZONE='+00:00'staat bovenaan. JeTIMESTAMP-kolommen worden in UTC gedumpt, zodat ze een verhuizing naar een server in een andere tijdzone overleven. Dit is--tz-utc, ook standaard aan.FOREIGN_KEY_CHECKS=0staat er ook bovenaan. Tabellen worden weggeschreven in de volgorde waarin de server ze noemt, en dat is niet de volgorde van afhankelijkheden. Een kindrij kan dus makkelijk worden ingevoegd voordat de ouder waarnaar hij wijst bestaat. Door de controles voor de duur van het terugzetten uit te zetten, werkt dat toch. Aan het eind krijgen ze hun oude waarde terug, en de constraints zelf staan gewoon nog in deCREATE TABLE-statements - alleen de controle stond even stil. Paragraaf 6.3 laat de verwante truc zien diemysqldumpvoor views gebruikt.
4.3 Terugzetten: er is geen mysqlrestore
Mensen zoeken naar het bijbehorende terugzetcommando en vinden het niet. Het bestaat niet, en dat hoeft ook niet. De dump is een script van SQL-statements, dus je zet hem terug door hem aan de gewone client te voeren:
$ mysql -u root -p sitedb < sitedb.sql
De database zelf moet al bestaan, want een gewone dump van een database bevat geen CREATE DATABASE. Maak hem dus eerst aan:
$ mysql -u root -p -e "CREATE DATABASE sitedb CHARACTER SET utf8mb4"
$ mysql -u root -p sitedb < sitedb.sql
Merk op dat de doelnaam op de opdrachtregel van het terugzetten staat. Daardoor is het triviaal om een dump in een anders genoemde database terug te zetten, en precies zo kloon je een live site naar een staging-kopie.
4.4 Een tabel, meerdere tabellen, meerdere databases
De argumenten na de databasenaam zijn tabelnamen:
$ mysqldump -u root -p sitedb j6_content # one table
$ mysqldump -u root -p sitedb j6_content j6_users # two tables
$ mysqldump -u root -p --databases sitedb shopdb # two whole databases
$ mysqldump -u root -p --all-databases # everything on the server
De vlag --databases (-B) verandert de betekenis van elk argument: het worden allemaal databasenamen, en de uitvoer krijgt er per database een CREATE DATABASE en een USE-statement bij. Dat is een echt verschil in gedrag, niet alleen in schrijfwijze:
| Commando | Bevat CREATE DATABASE? | Zet terug in |
|---|---|---|
mysqldump sitedb |
Nee | Welke database je bij het terugzetten ook noemt |
mysqldump --databases sitedb |
Ja | Altijd sitedb, die opnieuw wordt aangemaakt |
Gebruik dus de gewone vorm als je hem misschien ergens anders wilt terugzetten, en --databases als je een getrouwe, op zichzelf staande herbouw wilt.
4.5 Verbinden met de juiste server
De verbindingsvlaggen zijn dezelfde als voor de mysql-client:
$ mysqldump -h db.example.com -P 3306 -u backup -p sitedb > sitedb.sql
-h is de host, -P (hoofdletter P) is de poort, en -p (kleine letter) is het wachtwoord. De twee p-vlaggen door elkaar halen is een inwijdingsritueel. Nog een subtiliteit: op Linux betekent de host localhost "gebruik de lokale Unix-socket", terwijl 127.0.0.1 "gebruik TCP naar deze machine" betekent. Dat is niet hetzelfde, en als een dump strandt op een socketfout, is overschakelen naar 127.0.0.1 het eerste dat je probeert.
5. Gemiddelde toepassingen
5.1 De vlag die het meest uitmaakt: --single-transaction
Standaard neemt mysqldump een leeslock op elke tabel terwijl hij die leest (--lock-tables staat aan). Op een live website is dat een probleem: zolang de dump duurt, staan schrijfacties in de rij achter de backup. Bezoekers zien een site die niet meer reageert.
--single-transaction lost dit op. Het opent een transactie op isolatieniveau REPEATABLE READ en dumpt elke tabel daarbinnen. De multiversie-opslag van InnoDB geeft de dump dan een bevroren beeld van de hele database zoals die bij de start was, terwijl de site gewoon doorgaat met schrijven:
$ mysqldump --single-transaction -u root -p sitedb > sitedb.sql
Er gelden twee voorwaarden, en die zijn allebei echt:
- Alleen InnoDB. Het consistente beeld komt uit InnoDB's eigen versiebeheer. Een MyISAM-tabel in dezelfde dump wordt zonder enige bescherming gelezen en kan half gewijzigd worden vastgelegd. Moderne MySQL en MariaDB gebruiken standaard InnoDB, dus meestal zit dit goed - maar controleer een oude, overgenomen database voordat je erop vertrouwt.
- DDL breekt het. Als een andere verbinding
ALTER TABLE,DROP TABLE,RENAME TABLEofTRUNCATE TABLEuitvoert terwijl de dump loopt, is de momentopname niet van die wijziging afgeschermd en kan de dump inconsistent uitvallen. In de praktijk betekent dat: draai geen extensie-update of sitemigratie tijdens je nachtelijke backup.
Voor een live site op InnoDB is
--single-transactiongeen optimalisatie die je later toevoegt. Het is het verschil tussen een backup en een storing.
5.2 Comprimeren onderweg naar buiten
SQL-tekst is buitengewoon herhalend en comprimeert dus hard. Omdat de uitvoer een stroom is, hoef je het grote bestand helemaal niet weg te schrijven:
$ mysqldump --single-transaction -u root -p sitedb | gzip > sitedb.sql.gz
Op de kleine testdatabase voor dit artikel werd 3002 byte SQL 984 byte gecomprimeerd. Op een echte site is de verhouding meestal beter dan 5 op 1. Terugzetten draait de pipe om:
$ gunzip < sitedb.sql.gz | mysql -u root -p sitedb
Gebruik zstd in plaats van gzip als je die hebt: het comprimeert beter en een paar keer sneller. Wat je ook kiest, lees paragraaf 7.3 voordat je zo'n pipe in een cronjob zet, want doorsluizen verandert wat er gebeurt als de dump mislukt.
5.3 Structuur zonder gegevens, en gegevens zonder structuur
Twee vlaggen splitsen een dump in tweeën:
$ mysqldump -u root -p --no-data sitedb > schema.sql # -d: definitions only
$ mysqldump -u root -p --no-create-info sitedb > data.sql # -t: rows only
--no-data (-d) is op zichzelf al echt nuttig. Een dump met alleen het schema is klein, veilig te delen en makkelijk in een diff-gereedschap te lezen, en dat maakt het de snelste manier om te beantwoorden: "wat heeft die update in de database veranderd?" Maak er een voor een upgrade en een erna, en draai dan diff.
5.4 Een deel van een tabel dumpen: --where
De clausule wordt rechtstreeks doorgegeven aan de SELECT:
$ mysqldump -u root -p --where="state=1" sitedb j6_content
INSERT INTO `j6_content` VALUES
(1,'Welcome','welcome',1,'2026-08-01 10:00:00'),
(2,'About us','about-us',1,'2026-08-02 11:30:00');
De niet-gepubliceerde rij is weg. Zo neem je een steekproef uit een enorme tabel voor een ontwikkelkopie:
$ mysqldump -u root -p --where="created > '2026-01-01'" sitedb j6_content
5.5 Tabellen weglaten: --ignore-table
Deze heeft elke keer zowel de databasenaam als de tabelnaam nodig:
$ mysqldump -u root -p --ignore-table=sitedb.j6_session sitedb > sitedb.sql
Herhaal de vlag per tabel die je wilt overslaan. Alleen --ignore-table=j6_session schrijven is de klassieke fout: het wordt stilzwijgend genegeerd en de tabel gaat toch mee.
5.6 Het patroon dat je moet kennen: sla de gegevens over, houd de tabel
Sessietabellen, cachetabellen en logtabellen zijn vaak de grootste tabellen in een database en het minst de moeite van het backuppen waard. Maar je kunt ze niet zomaar uit de dump laten, want de teruggezette site loopt dan vast op een ontbrekende tabel. Wat je wilt is de structuur zonder de rijen, en dat kost twee slagen:
# pass 1: everything except the session table
$ mysqldump --single-transaction -u root -p \
--ignore-table=sitedb.j6_session sitedb > sitedb.sql
# pass 2: append the session table's definition, but none of its rows
$ mysqldump --single-transaction -u root -p \
--no-data sitedb j6_session >> sitedb.sql
Let op de >> in het tweede commando: die voegt toe in plaats van te overschrijven. Het resultaat bevat CREATE TABLE `j6_session` zonder INSERT-statements erachter. De site komt terug, logt iedereen uit en draait door - precies wat je wilt. Voor een Joomla-site zijn de vergelijkbare tabellen #__session, #__action_logs en eventuele grote cachetabellen die een extensie heeft toegevoegd.
6. Gevorderde toepassingen
6.1 De standaarden die je nooit hebt ingesteld: --opt
Nieuwe gebruikers kopieren vaak een lang commando vol vlaggen zonder te weten dat de meeste al aanstaan. De verzameloptie --opt staat standaard aan en betekent acht dingen tegelijk:
Zit in --opt | Wat het doet |
|---|---|
--add-drop-table |
Schrijft DROP TABLE IF EXISTS voor elke CREATE TABLE |
--add-locks |
Zet LOCK TABLES om de INSERT-statements heen zodat terugzetten sneller gaat |
--create-options |
Behoudt MySQL-specifieke clausules zoals ENGINE=InnoDB en AUTO_INCREMENT |
--quick |
Stroomt rijen direct naar buiten in plaats van een hele tabel in geheugen te bufferen |
--extended-insert |
Bundelt veel rijen in een INSERT |
--lock-tables |
Vergrendelt elke tabel op de server tijdens het lezen |
--set-charset |
Zet SET NAMES utf8mb4 bovenaan de dump |
--disable-keys |
Stelt het herbouwen van indexen uit tot na het laden van de rijen |
mysqldump --opt sitedb is dus hetzelfde als mysqldump sitedb. Losse onderdelen zet je uit met het voorvoegsel --skip-, en allemaal tegelijk met --skip-opt.
Een paar is makkelijk te verwarren, en de namen liggen echt zo dicht bij elkaar:
--lock-tablesvergrendelt tabellen op de server, tijdens de dump. Dit is degene die je met--single-transactionuitzet.--add-locksschrijftLOCK TABLES-statements in het uitvoerbestand, om het latere terugzetten te versnellen. Het heeft geen enkel effect op de live server, en--single-transactionlaat het gewoon aanstaan.
6.2 Een dump leesbaar maken: --compact
Als je een dump wilt lezen of vergelijken in plaats van terugzetten, zit de standaardtekst in de weg. --compact haalt die weg:
$ mysqldump -u root -p --compact --no-data sitedb j6_content
CREATE TABLE `j6_content` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`alias` varchar(255) NOT NULL,
`state` tinyint(4) NOT NULL DEFAULT 1,
`created` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
Het zet commentaar, de drop-statements, de locks, de indexafhandeling en de tekensetregels uit. Dat maakt het uitstekend om te lezen en ongeschikt als backup. Gebruik het niet voor de dump waar je echt op leunt.
6.3 De onderdelen die geen tabellen zijn
Een database is meer dan tabellen, en mysqldump behandelt de extra's niet allemaal hetzelfde. Dit pakt mensen lelijk, want de dump slaagt en ziet er compleet uit:
| Object | Vlag | Standaard inbegrepen? |
|---|---|---|
| Views | geen, maar het account heeft SHOW VIEW nodig |
Ja |
| Triggers | --triggers |
Ja |
| Stored procedures en functies | --routines (-R) |
Nee |
| Geplande events | --events (-E) |
Nee |
| Tablespace-definities | --no-tablespaces om te onderdrukken |
Ja |
De scheefheid is niet vanzelfsprekend en er komt geen waarschuwing. Een database met stored procedures, gedumpt zonder --routines, levert een bestand op dat netjes terugkomt en ze mist:
$ mysqldump -u root -p --no-data sitedb | grep -c PROCEDURE
0
$ mysqldump -u root -p --no-data --routines sitedb | grep -c PROCEDURE
1
Als de kans bestaat dat de database routines of events heeft, dump dan met allebei de vlaggen. Ze kosten niets als er niets te dumpen valt.
Views gaan ongevraagd mee, maar ze worden op een manier weggeschreven die de eerste keer op een bug lijkt. Elke view verschijnt twee keer. Een keer op zijn alfabetische plek tussen de tabellen, als plaatshouder van nepkolommen:
--
-- Temporary table structure for view `v_published`
--
DROP TABLE IF EXISTS `v_published`;
/*!50001 DROP VIEW IF EXISTS `v_published`*/;
/*!50001 CREATE VIEW `v_published` AS SELECT
1 AS `id`,
1 AS `title` */;
En dan nog een keer, helemaal aan het eind van het bestand, als het echte werk:
--
-- Final view structure for view `v_published`
--
/*!50001 DROP VIEW IF EXISTS `v_published`*/;
/*!50001 CREATE ALGORITHM=UNDEFINED */
/*!50013 DEFINER=`root`@`localhost` SQL SECURITY DEFINER */
/*!50001 VIEW `v_published` AS select `j6_content`.`id` AS `id`,
`j6_content`.`title` AS `title` from `j6_content` */;
Dit is opnieuw de volgorde van terugzetten, hetzelfde probleem dat de foreign-keyschakelaar oplost. Een view selecteert uit tabellen die op dat punt in het bestand misschien nog niet bestaan, en een andere view selecteert misschien uit deze view. De plaatshouder geeft alles wat naar de view verwijst iets geldigs om aan te haken terwijl de tabellen nog worden aangemaakt, en de echte definitie vervangt hem zodra ze er allemaal zijn. Beide helften zijn nodig, dus ruim die SELECT 1 AS ...-blokken niet op uit een dump die je wilt terugzetten.
6.4 Binaire gegevens: --hex-blob
Standaard worden BLOB- en BINARY-kolommen als ontsnapte tekstliteralen weggeschreven. Meestal overleeft dat de heenreis en terug, maar binaire bytes binnen een aanhalingsteken zijn kwetsbaar: een ongelukkige bytereeks, een tekensetconversie tijdens het terugzetten, of een editor die regeleindes "repareert" kan ze stilzwijgend beschadigen. --hex-blob schrijft ze in plaats daarvan als hexadecimale 0x-literalen, die niet verkeerd te lezen zijn:
$ mysqldump --single-transaction --hex-blob -u root -p sitedb > sitedb.sql
Het bestand wordt groter. Als de database afbeeldingen, pdf's of geserialiseerde binaire gegevens opslaat, zet de vlag er dan toch bij.
6.5 Grote waarden en max_allowed_packet
Een rij moet in een enkel pakket tussen server en client oversteken, en beide kanten begrenzen hoe groot dat pakket mag zijn. Als een waarde groter is dan die grens, stopt de dump - met een foutmelding die mensen op een volstrekt verkeerd spoor zet:
$ mysqldump --max-allowed-packet=1M -u root -p sitedb big > big.sql
mysqldump: Error 2013: Lost connection to server during query
when dumping table `big` at row: 0
$ echo $?
3
Er is niets verloren gegaan en het netwerk is in orde. De rij paste simpelweg niet. De client meldt een verbroken verbinding omdat dat werkelijk is wat hij ziet als de server een te groot pakket weigert, en beheerders raken daar elk jaar middagen aan kwijt. De aanwijzing is at row: 0 op een tabel waarvan je weet dat er grote waarden in zitten.
De standaard van de client is ruim, maar niet onbeperkt: 16 MB op MySQL 8.0, 24 MB op MariaDB. Verhoog hem als een tabel afbeeldingen, pdf's of lange TEXT bevat:
$ mysqldump --single-transaction --hex-blob --max-allowed-packet=512M \
-u root -p sitedb > sitedb.sql
Denk daarna aan de andere kant. Een dump die alleen met een verhoogde grens gemaakt kon worden, heeft dezelfde ruimte nodig om teruggezet te worden, dus de max_allowed_packet van de doelserver moet ook groot genoeg zijn. Anders schrijft de backup zich prima weg en weigert hij te laden, en dat ontdek je op de dag dat je hem nodig hebt.
6.6 Uitvoer met tabs: --tab
De optie -T / --tab schrijft per tabel twee bestanden in een map: een .sql-bestand met de definitie en een .txt-bestand met de ruwe rijen.
$ mysqldump -u root -p --tab=/var/lib/mysql-files/out sitedb j6_content
$ ls /var/lib/mysql-files/out/
j6_content.sql j6_content.txt
$ cat /var/lib/mysql-files/out/j6_content.txt
1 Welcome welcome 1 2026-08-01 10:00:00
2 About us about-us 1 2026-08-02 11:30:00
3 Draft post draft-post 0 2026-08-03 09:15:00
Dat terugladen met LOAD DATA INFILE gaat veel sneller dan INSERT-statements afspelen, en de .txt-bestanden gaan zo andere gereedschappen in. Maar er zijn twee harde beperkingen, en daarom gebruiken de meeste mensen het nooit: mysqldump moet op dezelfde machine draaien als de server, want de server schrijft de databestanden zelf, en de server heeft schrijfrechten nodig op de doelmap (meestal die uit secure_file_priv). Het is een lokaal bulkoverdrachtgereedschap, geen backupmethode.
6.7 Replicatiecoördinaten
Om een replica uit een dump op te bouwen, moet de dump de exacte positie in het binaire log vastleggen waarop hij is gemaakt. Modern MySQL noemt dat --source-data:
$ mysqldump --single-transaction --source-data=2 \
-u root -p --all-databases > full.sql
Waarde 1 schrijft een uitvoerbaar CHANGE MASTER-statement; 2 schrijft het als commentaar, en dat is wat je wilt als je alleen een backup maakt en niet wilt dat terugzetten de replicatie herconfigureert. Samen met --single-transaction wordt de globale leeslock alleen kort aan het begin vastgehouden in plaats van de hele dump lang.
De oudere namen werken nog, maar geven nu een melding dat ze verouderd zijn. De vertaling is:
| Verouderd | Gebruik in plaats daarvan |
|---|---|
--master-data |
--source-data |
--dump-slave |
--dump-replica |
--delete-master-logs |
--delete-source-logs |
--apply-slave-statements |
--apply-replica-statements |
Denk op een server met GTID's aan ook aan --set-gtid-purged. De standaard is AUTO, en die zet een SET @@GLOBAL.GTID_PURGED-statement in de dump. Dat is juist als je een replica bouwt en fout als je een kopie van de site in een stagingserver terugzet, waar het zal weigeren te laden of de GTID-toestand van het doel bederft. Gebruik voor een gewone backup --set-gtid-purged=OFF.
6.8 Het wachtwoord van de opdrachtregel houden
-pSecret123 schrijven zet het wachtwoord in je shellgeschiedenis en in de proceslijst, waar elke gebruiker op de machine het met ps kan lezen. De client van MySQL waarschuwt je er elke keer voor:
$ mysqldump -u root -pSecret123 sitedb > sitedb.sql
mysqldump: [Warning] Using a password on the command line interface can be insecure.
De client van MariaDB drukt die waarschuwing niet af, waardoor de gewoonte makkelijker vol te houden is en geen haar minder gevaarlijk. Zet de inloggegevens voor scripts in een bestand dat alleen de eigenaar kan lezen:
$ cat ~/.my.cnf
[client]
user = backup
password = Secret123
$ chmod 600 ~/.my.cnf
$ mysqldump --single-transaction sitedb > sitedb.sql # no credentials needed
Nog beter voor een cronjob: houd het in een eigen bestand en wijs er uitdrukkelijk naar, zodat je niet afhankelijk bent van de gebruiker onder wie de taak toevallig draait:
$ mysqldump --defaults-extra-file=/etc/backup/db.cnf \
--single-transaction sitedb > sitedb.sql
Die vlag moet vooraan op de opdrachtregel staan. MySQL biedt ook mysql_config_editor, dat inloggegevens versluierd in ~/.mylogin.cnf bewaart voor gebruik met --login-path. Versluierd is niet versleuteld - het houdt iemand tegen die over je schouder meekijkt, niet een aanvaller die het bestand heeft.
6.9 Een backupgebruiker met alleen de rechten die nodig zijn
Een backuptaak heeft geen root nodig. Geef hem precies de rechten die een dump gebruikt:
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT, PROCESS
ON *.* TO 'backup'@'localhost';
FLUSH PRIVILEGES;
Elk recht hoort bij iets wat het gereedschap doet: SELECT om rijen te lezen, SHOW VIEW om views te reproduceren, TRIGGER voor triggers, LOCK TABLES voor het standaardvergrendelen, EVENT voor --events, en PROCESS omdat MySQL 8.0 dat nodig heeft om tablespace-informatie te lezen. Dump je nooit events, laat EVENT dan weg. Weigert je hoster PROCESS, zet dan --no-tablespaces bij het commando en heb je het niet meer nodig.
6.10 Het draaien tegen een Docker-container
De meeste lokale ontwikkeling gebeurt tegenwoordig in containers, en dat voegt een rimpel toe die het begrijpen waard is. De databaseserver draait binnen de container, terwijl je shell, je omleiding en het dumpbestand allemaal buiten de container leven. Elk probleem in deze paragraaf komt voort uit die ene grens.
Dumpen is de makkelijke richting. Noem de service precies zoals hij in je docker-compose.yml staat en leid om zoals altijd:
$ docker compose exec -T db mysqldump --single-transaction \
-u root -p sitedb > sitedb.sql
Bij terugzetten verliezen mensen een avond:
$ docker compose exec db mysql -u root -p sitedb < sitedb.sql
the input device is not a TTY
Er wordt niets teruggezet. De oplossing is de vlag -T, en de reden is het begrijpen waard in plaats van uit je hoofd leren. Standaard wijst docker compose exec een pseudoterminal toe, alsof je het commando interactief had getypt. Een terminal is geen pipe: die draagt geen bestand op standaardinvoer. -T betekent "geen terminal", en dat laat de omleiding erdoor.
Gewoon docker exec heeft hetzelfde probleem, andersom gespeld. Het wijst geen terminal toe, maar het koppelt ook geen standaardinvoer tenzij je erom vraagt met -i:
$ docker exec CONTAINER mysql -u root -p sitedb < dump.sql # does nothing
$ docker exec -i CONTAINER mysql -u root -p sitedb < dump.sql # correct
De eerste vorm is verreweg de gevaarlijkste van de twee, want die drukt helemaal geen fout af. Het commando keert terug, de database is onaangeroerd, en nergens hoor je dat het terugzetten niet is gebeurd.
Je vindt genoeg advies dat je -T ook bij de dump moet zetten, omdat het bestand anders met Windows-regeleindes terugkomt. Op Compose v2 klopt dat niet meer: die wijst alleen een terminal toe als de uitvoer echt naar een terminal gaat, dus een omleiding of een pipe is al veilig. Voor de interactieve client houd ik een klein wrapperscript aan dat op [ -t 0 ] test en alleen -T toevoegt als standaardinvoer geen terminal is. Hetzelfde commando geeft me dan een echte SQL-prompt als ik die wil, en slikt een dumpbestand als ik er een in voer.
Er is nog een valkuil die eigen is aan containers, en dat is het naamprobleem uit paragraaf 7.2 met aanzienlijk scherpere tanden. De client binnen de container is degene die de image meelevert, niet die op je laptop. Wijs een compose-bestand naar mariadb:11.4 en de vertrouwde naam is verdwenen:
$ docker compose exec -T db mysqldump -u root -p sitedb > sitedb.sql
$ echo $?
127
Kijk nu naar de backup die dat commando zojuist heeft opgeleverd:
$ ls -l sitedb.sql
-rw-r--r-- 1 peter peter 137 Aug 23 11:04 sitedb.sql
$ cat sitedb.sql
OCI runtime exec failed: exec failed: unable to start container process:
exec: "mysqldump": executable file not found in $PATH: unknown
Docker heeft zijn eigen foutmelding in je dumpbestand geschreven. Het bestand bestaat, het is niet leeg, en de datum is van vandaag, dus een backupcontrole die alleen op een omvang groter dan nul test, accepteert het zonder klagen. Gebruik mariadb-dump voor MariaDB 11-images, en lees paragraaf 7.3 nog eens: dit is het lege-backupprobleem met een andere hoed op.
Tot slot de inloggegevens. Een compose-stack houdt ze meestal al in het .env-bestand, dus er is geen reden om een wachtwoord op de opdrachtregel te typen en aan je shellgeschiedenis te geven:
$ docker compose exec -T db mysqldump --single-transaction \
-u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" > sitedb.sql
Wees eerlijk over wat je daarmee koopt. Het houdt het wachtwoord uit je geschiedenis en uit het script, maar het wachtwoord is nog steeds zichtbaar in de proceslijst binnen de container. Op een lokale ontwikkelstack is dat een redelijke ruil. Gebruik op een server een optiebestand zoals in paragraaf 6.8.
Naar boven7. Iets wat de meeste gebruikers niet weten
7.1 De sandboxregel bovenaan MariaDB-dumps
Dump iets met een recente MariaDB en de allereerste regel lijkt een vergissing:
$ mariadb-dump -u root -p sitedb | head -2
/*M!999999\- enable the sandbox mode */
-- MariaDB dump 10.19-11.4.12-MariaDB, for debian-linux-gnu (x86_64)
Het is met opzet, en het dicht een echt beveiligingsgat. De opdrachtregelclients mysql en mariadb kennen hun eigen commando's naast SQL, waaronder \! en system, die een shellcommando op jouw machine uitvoeren. Een dumpbestand uit onbetrouwbare bron kon dus van alles uitvoeren zodra je het de client in sluisde - en mensen sluizen dumpbestanden voortdurend als root een client in.
Het commando \- zet de client in sandboxmodus, waar die ontsnappingen voor de rest van de sessie uit staan en niet meer aan te zetten zijn. Door het in een versiecommentaar voor versie 999999 te verpakken, zal geen enkele server het ooit uitvoeren, dus voor de database zelf is het onzichtbaar.
Het voorvoegsel veranderde tussen versies, en dat verschil is handig om te kennen als je dumps vergelijkt: MariaDB 10.6 schrijft /*!999999 ...*/, terwijl 11.4 /*M!999999 ...*/ schrijft met een M, wat het als MariaDB-only markeert zodat andere gereedschappen het netjes negeren. Hoe dan ook: laat de regel staan.
7.2 Op MariaDB bestaat mysqldump misschien helemaal niet
MariaDB hernoemde zijn hele clientpakket, en deed dat in drie zorgvuldige stappen. Eerst voegde het de nieuwe naam als alias toe, toen maakte het de nieuwe naam de echte, en daarna liet het de oude naam helemaal vallen. Je kunt het versie voor versie zien gebeuren:
# MariaDB 10.4 - the old name is the program, the new name points at it
/usr/bin/mariadb-dump -> mysqldump
/usr/bin/mysqldump (4111264 bytes)
# MariaDB 10.5 and 10.6 - the arrow has turned around
/usr/bin/mariadb-dump (4150176 bytes)
/usr/bin/mysqldump -> mariadb-dump
Voor die versies maakt het niet uit welke naam je typt. Op MariaDB 11.4 maakt het heel veel uit, want de symlink is weg:
$ ls -l /usr/bin/mysqldump
ls: cannot access '/usr/bin/mysqldump': No such file or directory
$ mysqldump --version
bash: mysqldump: command not found
Zo sterft een backupscript dat jarenlang draaide van de ene op de andere nacht tijdens een routine-upgrade. Erger nog: als het script naar gzip sluist, blijft het bestanden opleveren, dus er lijkt niets kapot. Schrijf scripts die pakken welke naam er is:
DUMP=$(command -v mariadb-dump || command -v mysqldump) || {
echo "no dump program found" >&2; exit 1; }
7.3 De exitcode, de pipe en de lege backup
Dit is het duurste onderwerp uit het artikel, dus het krijgt zijn eigen demonstratie. mysqldump meldt een mislukking netjes:
$ mysqldump -h 127.0.0.1 -P 13306 -u root -pfake somedb > out.sql
mysqldump: Got error: 2003: Can't connect to MySQL server on '127.0.0.1:13306'
$ echo $?
2
Exitcode 2, precies zoals het hoort. Maar kijk wat de shell heeft achtergelaten:
$ ls -l out.sql
-rw-r--r-- 1 peter peter 0 Aug 23 10:22 out.sql
Er is een bestand. Het is leeg, maar het bestaat, en het draagt de datum van vandaag. Een monitoringcontrole die kijkt of er vandaag een backupbestand is aangemaakt, zegt ja.
Voeg nu de pipe toe die vrijwel elk echt backupscript heeft:
$ mysqldump -h 127.0.0.1 -P 13306 -u root -pfake db 2>/dev/null | gzip > db.sql.gz
$ echo $?
0
Exitcode 0. De shell meldt de exitstatus van het laatste commando in een pipeline, en gzip is geslaagd - het heeft niets gecomprimeerd, en dat foutloos. Je cronjob is tevreden, je log zegt geslaagd, en db.sql.gz is een geldig gzip-bestand met een lege database erin. Niemand komt erachter tot het terugzetten.
De oplossing is een regel, en elk backupscript zou ermee moeten beginnen:
$ set -o pipefail
$ mysqldump -h 127.0.0.1 -P 13306 -u root -pfake db 2>/dev/null | gzip > db.sql.gz
$ echo $?
2
Met pipefail geeft de pipeline de mislukking terug. Controleer daarna de exitcode en de omvang, en overschrijf nooit de goede backup van gisteren met de slechte van vandaag:
#!/bin/bash
set -euo pipefail
OUT=/backups/sitedb-$(date +%F).sql.gz
TMP=$OUT.part
mysqldump --defaults-extra-file=/etc/backup/db.cnf \
--single-transaction --routines --events --hex-blob \
sitedb | gzip > "$TMP"
# a dump smaller than 1 KB is not a real dump
[ "$(stat -c%s "$TMP")" -gt 1024 ] || { echo "dump too small" >&2; exit 1; }
mv "$TMP" "$OUT"
Naar een .part-bestand schrijven en alleen bij succes hernoemen, betekent dat de uiteindelijke bestandsnaam nooit bestaat tenzij de dump echt is gelukt. De mv is atomair binnen een bestandssysteem, dus er is geen moment waarop een half geschreven bestand er af uitziet.
Als je toch de status controleert, is het de moeite waard om te loggen welke mislukking je kreeg in plaats van alleen "backup mislukt". De codes zijn met weinig en ze wijzen direct naar de oorzaak:
| Exitcode | Betekenis | Waar je moet kijken |
|---|---|---|
0 |
Geslaagd | Controleer alsnog de bestandsomvang |
2 |
Kon niet verbinden, of de server weigerde de login | Host, poort, socket, inloggegevens, of een losse DEFINER (paragraaf 7.4) |
3 |
Halverwege een tabel gestrand | Meestal max_allowed_packet (paragraaf 6.5) of een recht dat het account mist |
6 |
De genoemde database of tabel bestaat niet | Een typefout, of een tabel die is verwijderd sinds het script is geschreven |
Code 3 is de gemene. Die betekent dat de dump goed begon en halverwege stopte, dus het bestand is niet leeg - het is afgekapt. Een omvangscontrole die alleen op "groter dan nul" test, accepteert het zonder morren.
Daarom is op de omgevingen die ik beheer niet het succes van de taak waar ik op alarmeer, maar de omvang van de nieuwste dump afgezet tegen die van vorige week. Een database die elke dag groeit en dan een backup van de helft van zijn gebruikelijke omvang oplevert, vertelt je iets, en dat is het ene signaal dat een groen vinkje in een cronlog je nooit kan geven.
7.4 De DEFINER die je backups maanden later breekt
Elke view, trigger, stored routine en event onthoudt het account dat hem heeft aangemaakt, en mysqldump schrijft dat account in de dump:
CREATE DEFINER=`u3`@`%` PROCEDURE `p_count`()
/*!50013 DEFINER=`u3`@`%` SQL SECURITY DEFINER */
Dat is een verwijzing naar een account op de bronserver, en die reist met het bestand mee. Zet de dump terug op een plek waar dat account niet bestaat en het terugzetten zegt helemaal niets:
$ mysql -u root -p rfull < full.sql
$ echo $?
0
Exitcode 0, geen waarschuwingen, elke tabel aanwezig en in orde. De mislukking wacht tot iets het object echt gebruikt:
$ mysql -u root -p rfull -e 'SELECT * FROM v'
ERROR 1449 (HY000): The user specified as a definer ('u3'@'%') does not exist
Dit is de "de migratie ging perfect maar de site is stuk"-bug, en het is het sterkste argument dat er is om een terugzetting te testen door de applicatie te gebruiken in plaats van rijen te tellen.
Er is een tweede helft die minder bekend is en aanzienlijk erger. Een losse definer beschadigt niet alleen de teruggezette kopie. Hij breekt elke toekomstige dump van de brondatabase:
$ mysqldump -u root -p full1 > backup.sql
mysqldump: Got error: 1449: "The user specified as a definer ('u3'@'%')
does not exist" when using LOCK TABLES
$ echo $?
2
Lees dat als een verhaal uit de praktijk. Iemand ruimt een MySQL-account op dat niemand lijkt te gebruiken. De website blijft werken, want vandaag bevraagt niets die view. Die nacht mislukt de backuptaak - en als de taak zonder pipefail naar gzip sluist, meldt hij succes terwijl hij een leeg bestand wegschrijft, precies zoals paragraaf 7.3 beschrijft. De backups zijn gestopt en elk signaal zegt dat het goed gaat.
Zet dus voordat je een database-account verwijdert eerst de definers op een rij:
SELECT DISTINCT DEFINER FROM information_schema.VIEWS
UNION SELECT DISTINCT DEFINER FROM information_schema.ROUTINES
UNION SELECT DISTINCT DEFINER FROM information_schema.TRIGGERS
UNION SELECT DISTINCT DEFINER FROM information_schema.EVENTS;
+-----------------------+
| DEFINER |
+-----------------------+
| mariadb.sys@localhost |
| u3@% |
| root@localhost |
+-----------------------+
Negeer de onderhoudsaccounts van de server zelf en leg de rest naast SELECT user, host FROM mysql.user. Alles uit de eerste lijst dat in de tweede ontbreekt, is een storing die op een rustige nacht wacht.
Die query is het draaien waard op elke site die je niet zelf hebt gebouwd, want overgenomen databases verzamelen definers die wijzen naar ontwikkelaars en bureaus die jaren geleden zijn vertrokken, en niets brengt ze aan de oppervlakte tot de nacht waarin een backup ophoudt te werken.
Het repareren is een van de praktische beloningen van een backup die uit tekst bestaat. Je kunt de objecten in de dump omleggen voordat je hem terugzet:
$ sed -i 's/DEFINER=`u3`@`%`/DEFINER=`root`@`localhost`/g' full.sql
Wil je in plaats daarvan de live bron repareren, maak dan het ontbrekende account opnieuw aan, of verwijder en hermaak elk getroffen object met een definer die wel bestaat. Het account opnieuw aanmaken gaat meestal sneller, en je kunt het zonder enige rechten laten.
7.5 De fout over kolomstatistieken die niemand verwacht
Gebruik een MySQL 8.0-client tegen een MySQL 5.7-server en de dump mislukt meteen:
Unknown table 'COLUMN_STATISTICS' in information_schema (1109)
Er is niets mis met je database. MySQL 8.0 voegde histogramstatistieken toe en zette --column-statistics standaard aan in de client, waardoor de nieuwere client een tabel bevraagt die de oudere server niet heeft. Zet hem uit:
$ mysqldump --skip-column-statistics -u root -p sitedb > sitedb.sql
Je komt dit voortdurend tegen bij dumpen vanaf een oudere shared host met een moderne lokale client.
7.6 De vervanger die zelf werd vervangen
MySQL 5.7 leverde mysqlpump, een herschrijving met parallel dumpen die mysqldump moest opvolgen. Dat is nooit gebeurd. Draai hem vandaag en hij zegt het zelf:
$ mysqlpump --version
WARNING: mysqlpump is deprecated and will be removed in a future version. Use mysqldump instead.
mysqlpump Ver 8.0.46 for Linux on x86_64
Verouderd verklaard in MySQL 8.0.34 en verwijderd in 8.4. De officiele opvolger voor grote databases is nu de MySQL Shell dump utilities (util.dumpInstance() en util.loadDump()), die de handleiding van mysqldump zelf aanbeveelt voor parallel dumpen met compressie en voortgangsweergave. Heb je ooit geaarzeld om een werkwijze op mysqldump te bouwen omdat het vervangen zou worden: het heeft zijn vervanger overleefd.
7.7 Weten waar mysqldump ophoudt
Het is het juiste gereedschap voor een verrassend breed bereik en het verkeerde gereedschap voorbij een zekere omvang. De kosten zitten in het terugzetten, niet in het dumpen: miljoenen INSERT-statements afspelen en elke index herbouwen kost uren waar een fysieke kopie minuten kost.
| Wanneer | Grijp naar |
|---|---|
| Alles tot een paar GB: websites, webshops, CMS-databases | mysqldump |
| Grote databases waar de terugzettijd telt | xtrabackup (Percona) of mariabackup, fysiek en warm |
| Grote databases, nog steeds logisch, maar parallel | MySQL Shell util.dumpInstance() |
| Herstel tot op de seconde nauwkeurig | Een dump als basis, plus binaire logs afgespeeld met mysqlbinlog |
| Continue beschikbaarheid | Replicatie - wat geen backup is: een DROP TABLE repliceert net zo hard mee |
| Een hele server in seconden terugdraaien | Snapshots van bestandssysteem of volume (LVM, ZFS, cloudschijfsnapshots) |
| PostgreSQL in plaats van MySQL | pg_dump, hetzelfde idee bij een andere leverancier |
Het duo dat je moet onthouden is de op twee na laatste rij. Een nachtelijke mysqldump geeft je een herstelpunt om 03:00 uur; de binaire logs laten je van daaraf vooruitrollen tot het moment vlak voordat iemand het verkeerde weggooide. Geen van beide doet het werk alleen.
8. Best practices
- Gebruik altijd
--single-transactionop een live InnoDB-site. Zonder die vlag vergrendel je de database tegen je eigen bezoekers zolang de backup duurt. - Zet
--routines --eventserbij, tenzij je hebt gecontroleerd dat de database geen van beide heeft. Ze gaan niet standaard mee en hun afwezigheid is stil. - Zet
--hex-bloberbij als een kolom binaire gegevens bevat, en verhoog--max-allowed-packetbij zowel het dumpen als het terugzetten als rijen groot zijn. - Zet de definers op een rij voordat je een database-account verwijdert. Een view of routine die naar een ontbrekend account blijft wijzen, breekt vanaf dat moment elke dump van die database.
- Begin elk backupscript met
set -euo pipefail, en controleer daarna zowel de exitcode als de bestandsomvang. Een backupscript dat niet hardop kan mislukken is erger dan geen script. - Schrijf naar een tijdelijke naam en hernoem bij succes, zodat een mislukte draaibeurt nooit een goede backup vervangt.
- Houd inloggegevens van de opdrachtregel - gebruik
--defaults-extra-filemet een bestand opchmod 600, en geef het als eerste argument mee. - Gebruik een apart backupaccount met
SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT, PROCESS, geenroot. - Comprimeer in de pipe met
gzipofzstd. SQL-tekst comprimeert een paar keer over en je hoeft het grote bestand helemaal niet weg te schrijven. - Detecteer de programmanaam in scripts, zodat een MariaDB-upgrade die de
mysqldump-symlink weghaalt de taak niet stilzwijgend breekt. - Haal de dump van de server af. Een backup naast de database beschermt je nergens tegen behalve je eigen fouten, en het is een bestand dat een aanvaller dolgraag vindt.
- Zet hem ergens terug, volgens een schema. Een ongeteste dump is een gok. Terugzetten in een wegwerpdatabase kost tien minuten en is het enige dat bewijst dat de backup werkt.
- Onthoud wat er niet in zit: gebruikers en rechten wonen in de systeemdatabase
mysql, niet in de database van je site, en ze zitten niet in een dump van een enkele database. - Lees de documentatie met
man mysqldump, ofmysqldump --helpvoor de vlaggen plus een volledige lijst van elke standaardwaarde.
9. Veelgemaakte fouten
9.1 Veelvoorkomende mythes
| Mythe | Werkelijkheid |
|---|---|
| "Er verscheen een dumpbestand, dus de backup is gelukt." | De shell maakt het bestand voordat mysqldump draait. Een mislukte dump laat een bestand van 0 byte achter met de datum van vandaag, en een mislukte dump door een pipe laat een volkomen geldige, volkomen lege .gz achter. |
| "Een dump terugzetten voegt hem samen met de huidige database." | De standaarddump bevat DROP TABLE IF EXISTS voor elke tabel. Terugzetten vervangt die tabellen. Tabellen die wel in het doel maar niet in de dump staan, blijven onaangeroerd, en dat levert een verwarrende half-om-halfdatabase op. |
"--single-transaction maakt elke dump consistent." |
Alleen voor InnoDB-tabellen, en alleen als niemand tijdens de dump ALTER, DROP, RENAME of TRUNCATE uitvoert. MyISAM-tabellen krijgen helemaal geen bescherming. |
"--all-databases betekent dat ik de hele server kan herbouwen." |
Je komt heel dichtbij, maar niet de configuratiebestanden, de SSL-certificaten of de binaire logs. En de systeemdatabase mysql terugzetten over verschillende serverversies heen geeft zijn eigen problemen. |
| "De dump bevat alles wat in de database zit." | Stored procedures, functies en events blijven buiten de dump tenzij je erom vraagt. |
| "Een dump die vannacht werkte, werkt vanavond ook." | Niet als iemand er tussendoor een database-account heeft verwijderd. Views, triggers, routines en events bewaren het account dat ze aanmaakte, en een enkele losse DEFINER laat mysqldump voor de hele database mislukken. |
| "Een replica is een backup." | Een replica kopieert je fouten getrouw en binnen seconden. Hij beschermt tegen hardwarestoringen, niet tegen DELETE FROM. |
"mysqldump is achterhaald; ik zou iets moderns moeten gebruiken." |
De officiele vervanger, mysqlpump, is in MySQL 8.0.34 verouderd verklaard en in 8.4 verwijderd, terwijl mysqldump het aanbevolen algemene gereedschap blijft. |
9.2 Valkuilen om te vermijden
-pen-Pdoor elkaar halen. Kleine letter is het wachtwoord, hoofdletter is de poort. En er staat geen spatie na-p:-p secretwordt gelezen als "vraag om het wachtwoord en dump dan de database diesecretheet".- De databasenaam vergeten bij
--ignore-table. Het moet--ignore-table=db.tabelzijn. De korte vorm wordt zonder klagen geaccepteerd en doet niets. - Een sessie- of cachetabel helemaal weglaten. Gebruik
--ignore-tableplus een tweede slag met--no-data, anders loopt de teruggezette site vast op een ontbrekende tabel. --compactgebruiken voor een echte backup. Het haalt deDROP TABLE-statements en de tekensetregels weg. Het is om dumps te lezen, niet om ze te bewaren.--set-gtid-purgedop de standaard laten bij terugzetten naar staging. Op een server met GTID's draagt de dump eenSET @@GLOBAL.GTID_PURGEDmee die het doel breekt. Gebruik--set-gtid-purged=OFFvoor gewone backups.- Verwachten dat
--tabover het netwerk werkt. De server schrijft die bestanden, dus het werkt alleen als de dump op de databasemachine draait en de server in de map mag schrijven. - Aannemen dat de tekenset zichzelf wel redt. Meestal doet die dat, want
SET NAMESen--default-character-set=utf8mb4zijn standaard. Kom je een oude database tegen die alslatin1is opgegeven maar in werkelijkheid UTF-8-bytes bevat, dan redt die zich niet, en de beschadiging wordt pas na het terugzetten zichtbaar. - De dump draaien terwijl een update loopt. Schemawijzigingen tijdens een
--single-transaction-dump leveren stilzwijgend een inconsistent bestand op. Laat je backupvenster niet samenvallen met je onderhoudsvenster. - De dump in de webroot bewaren. Een onbeschermde
backup.sqlonder de documentroot geeft elke wachtwoordhash en elk klantgegeven weg aan iedereen die de bestandsnaam raadt. -Tof-ivergeten bij terugzetten in een container.docker compose execzegt "the input device is not a TTY"; gewoondocker execzegt helemaal niets en zet niets terug.- Alleen controleren of het dumpbestand niet leeg is. Exitcode 3 betekent dat de dump halverwege een tabel is gestopt, dus het bestand is afgekapt in plaats van leeg. Controleer de exitcode net zo goed als de omvang.
- De blokken "temporary table structure for view" weggooien. Ze zien eruit als rommel die een bug heeft achtergelaten. Het zijn plaatshouders waarmee een view kan terugkomen voordat de tabellen bestaan waaruit hij leest, en de dump heeft ze nodig.
- Het terugzetten nooit testen. Elk ander punt op deze lijst ontdek je tijdens een terugzetting. Kies zelf of dat op een rustige dinsdag gebeurt of tijdens een storing.
10. Samenvatting
mysqldump ziet eruit als een gereedschap van een regel en gedraagt zich ook zo, maar bijna alles wat er misgaat met databasebackups gaat mis in de ruimte eromheen: de shell, de pipe, het schema en de terugzetting die niemand heeft geprobeerd.
- Het maakt een logische backup: platte SQL-tekst die je database overal herbouwt, op elke versie, leesbaar en bewerkbaar. Die overdraagbaarheid is waarom het na dertig jaar nog het standaardgereedschap is.
- Het schrijft naar standaarduitvoer. De shell maakt het bestand, en daarom werken omleiding, pipes en compressie zo vanzelfsprekend - en daarom laat een mislukte dump toch een bestand achter.
- Er is geen terugzetcommando. Je voert de dump terug met
mysql < bestand.sql, in een database die al moet bestaan. --single-transactionis de vlag die op een live site telt: een InnoDB-momentopname in plaats van een tabellock, zodat de backup de site niet meesleurt.- Standaarden doen het meeste werk via
--opt, maar--routinesen--eventshoren daar niet bij, en hun afwezigheid is stil. - Snoei de dump bij met
--where,--ignore-table,--no-dataen het tweeslagpatroon dat de structuur van een sessietabel houdt zonder de rijen. - Objecten onthouden wie ze heeft gemaakt. Een
DEFINERdie naar een niet meer bestaand account wijst, laat het terugzetten stil slagen en faalt zodra het object wordt gebruikt - en laat de brondatabase helemaal niet meer dumpen. - De exitcode is eerlijk maar de pipeline niet. Zonder
set -o pipefailmeldt een volledig mislukte backup succes. Controleer de status, controleer de omvang, en hernoem pas op zijn plek bij succes. - Op MariaDB heet het programma
mariadb-dump, demysqldump-symlink verdween in 11.4, en dumps beginnen met een sandboxregel die je tegen een vijandig dumpbestand beschermt. - Weet waar het ophoudt:
xtrabackupofmariabackupvoor grote databases, MySQL Shell voor parallelle logische dumps,mysqlbinlogvoor herstel tot op de seconde, snapshots om een hele server terug te draaien. - Bij twijfel: typ
man mysqldump.
Dit is het overzicht dat je wilt bewaren:
mysqldump -u U -p DB > db.sql dump one database to a file
mysql -u U -p DB < db.sql restore it (database must exist)
mysqldump --single-transaction ... consistent dump without locking (InnoDB)
mysqldump ... | gzip > db.sql.gz compress in the pipe
gunzip < db.sql.gz | mysql -u U -p DB restore a compressed dump
mysqldump -u U -p DB tbl1 tbl2 only these tables
mysqldump -u U -p --databases DB1 DB2 several databases, with CREATE DATABASE
mysqldump -u U -p --all-databases the whole server
mysqldump --no-data DB > schema.sql structure only (-d)
mysqldump --no-create-info DB > data.sql rows only (-t)
mysqldump --where="state=1" DB tbl only matching rows (-w)
mysqldump --ignore-table=DB.tbl DB skip a table (needs DB.tbl)
mysqldump --routines --events DB include procedures and events
mysqldump --hex-blob DB safe binary columns
mysqldump --compact --no-data DB readable schema, for diffing only
mysqldump --skip-column-statistics ... 8.0 client against a 5.7 server
mysqldump --set-gtid-purged=OFF ... backup on a GTID server
mysqldump --defaults-extra-file=f.cnf credentials from a file (must be first)
mysqldump --max-allowed-packet=512M ... tables holding large TEXT or BLOB values
docker compose exec -T db mysqldump ... dump from a container (-T for restores)
docker exec -i CONTAINER mysql db < f restore into a plain docker container
set -o pipefail or a failed dump reports success
exit 0 ok | 2 connect or definer | 3 truncated | 6 no such table
Een databasebackup is de goedkoopste verzekering die een website heeft, en hij wordt bijna altijd een keer ingericht en daarna nooit meer bekeken. Het ongemakkelijke is dat een werkende backuproutine en een kapotte backuproutine er van buiten precies hetzelfde uitzien, tot op de dag dat je er een nodig hebt. Wil je de database achter je site laten backuppen, testen en terugzetbaar houden door iemand die de terugzetting al eens heeft gedaan, dan is dat precies het soort stille werk waar ik graag bij help.
Naar boven

Peter is Joomla specialist en Linux admin voor snelle, veilige en schaalbare websites.












