JusDB
Free DB Audit
MySQL

MySQL Shell Dump and Load Utilities vs mydumper

Compare MySQL Shell 26.10 dump and load with mydumper 1.0 on locking, chunking, managed targets, deferred indexes, resume, and GTIDs, with commands for each step.

JusDB Team
Published November 7, 2026
17 min read

You are moving a MySQL 8.0 or 8.4 database to a new server, a new major version, Amazon RDS, Aurora, or MySQL HeatWave, and a physical copy is not an option. The two parallel logical tools are MySQL Shell's dump and load utilities and the community mydumper/myloader pair, and they fail in different places. mydumper stops on an RDS source with We support LOCK_ALL and SAFE_NO_LOCK modes for RDS/Aurora. MySQL Shell stops a HeatWave-bound dump with Compatibility issues with MySQL HeatWave Service ... were found. This guide compares MySQL Shell 26.10 with mydumper 1.0.19 on locking, chunking, managed targets, deferred indexes, resume, and GTIDs, with commands for both.

TL;DR
  • Current releases: MySQL Shell 26.10.0 (20 October 2026) and mydumper v1.0.19-1 (5 November 2026). Shell is now one release line that supports MySQL Server 8.4, 9.7, and 26.7.
  • On RDS and Aurora sources, set mydumper's lock mode to LOCK_ALL or SAFE_NO_LOCK. Shell falls back to LOCK TABLES by itself.
  • Shell has built-in managed-target checks (ocimds, compatibility, targetVersion), native S3, OCI, and Azure output, and a resumable load. mydumper has none of these built in.
  • By default, mydumper skips triggers, routines, and events, and myloader loads with binary logging off. Shell includes those objects and logs by default.
  • Neither tool sets GTIDs on the target by default. Use updateGtidSet or myloader --set-gtid-purged before replicating to the target for cutover.

Scope and review date: November 2026. This post covers MySQL Shell 26.10 and mydumper/myloader 1.0.19-1 with MySQL 8.0, 8.4, 9.7, and 26.7 sources and self-managed, RDS, Aurora, and HeatWave targets. Options and defaults were checked on 9 November 2026 against the MySQL Shell 26.10 manual and release notes and the mydumper documentation and v1.0.19-1 source. The commands were not run in a lab, so rehearse them on a copy first.


Which MySQL Shell and mydumper versions should you use in November 2026?

The MySQL Shell 26.7.0 release notes (28 July 2026) moved Shell to a single release line, discontinued the 8.4 and 9.7 Shell series, and state that the latest Shell supports MySQL Server 8.4, 9.7, and 26.7. 26.7.0 also fixed unbalanced chunking of tables with composite primary keys, a Percona contribution. 26.10.0 followed on 20 October 2026.

mydumper's first stable long-term support release, v1.0.1-1, shipped on 17 May 2026, and the GitHub releases page lists v1.0.19-1 (5 November 2026) as the latest. Its README says it is a community project, not a Percona, MariaDB, or MySQL product.

bash
mysqlsh --version
mydumper --version
myloader --version

Interpretation: load a Shell dump with the same or a newer Shell. Each dump lists the features it uses, and an older loader stops with an error that names the Shell version it needs. For mydumper, run the same release on both sides.

How do MySQL Shell dump/load and mydumper/myloader compare?

Shell writes DDL as .sql and data as chunked, tab-separated files with JSON metadata, and loads them with LOAD DATA LOCAL INFILE. mydumper writes SQL INSERT files by default (--format also offers LOAD_DATA, CSV, and CLICKHOUSE) plus a plain-text metadata file. Neither loader is documented to read the other tool's dumps.

MySQL Shell 26.10 and mydumper 1.0.19 side by side

Switch tools to see the defaults you inherit before you set any option

MySQL Shell 26.10

AreaMySQL Shell
Default threads4 for dump and 4 for load
ChunkingOn; bytesPerChunk default 64 MB
CompressionOn; zstd level 1
Triggers, routines, eventsIncluded by default
Binary logging during loadOn (skipBinlog: false)
Secondary indexes during loadFull-text deferred; others built with the table
Object storageNative: OCI, S3-compatible, Azure Blob
Resume after failureProgress file; partly loaded tables deduplicated

mydumper 1.0.19

Areamydumper and myloader
Default threads4, minimum 2; 0 means one per CPU
ChunkingBy rows (--rows, adaptive) or MB (--chunk-filesize)
CompressionOff unless you pass --compress (gzip or zstd)
Triggers, routines, eventsSkipped unless you pass -G, -R, -E
Binary logging during loadOff (myloader sets SQL_LOG_BIN=0)
Secondary indexes during loadAll added after each table loads
Object storageNone built in; --stream to a CLI
Resume after failureresume file, written on a graceful stop only

Most common surprise: with default myloader settings, the target's replicas and binary-log-based recovery never see the loaded rows. Use --enable-binlog if they must; 1.0.0 marked it as discouraged in favor of a [myloader_session_variables] section in the defaults file.

How does each tool take a consistent snapshot?

Both tools briefly block writes with a global lock, open START TRANSACTION WITH CONSISTENT SNAPSHOT on every worker, then release the lock. They differ in which lock they take and what happens when they cannot take it.

With the default consistent: true, the Shell dump utilities issue FLUSH TABLES WITH READ LOCK if the account has RELOAD, otherwise LOCK TABLES. Once every thread has its snapshot, Shell takes LOCK INSTANCE FOR BACKUP, which allows DML but blocks DDL that would break the dump, and releases the read lock. Without BACKUP_ADMIN, Shell runs an extra consistency check; if it fails, an instance dump stops. Shell documents consistency only for InnoDB tables.

sql
-- Source account for util.dumpInstance(), per the Shell 26.10 manual
CREATE USER 'dumper'@'10.0.%' IDENTIFIED BY 'change-me';
GRANT SELECT, SHOW VIEW, EVENT, TRIGGER, RELOAD ON *.* TO 'dumper'@'10.0.%';
GRANT BACKUP_ADMIN, REPLICATION CLIENT ON *.* TO 'dumper'@'10.0.%';
-- Dumping users (the default) also needs SELECT on the mysql schema

Interpretation: REPLICATION CLIENT lets Shell record the binary log position; without it the dump has no coordinates. Since Shell 8.0.27, a read lock refused with access denied falls back to LOCK TABLES. That matters on RDS: the AWS knowledge center states that RDS does not support a global read lock.

util.dumpInstance() with the default consistent: true

Illustrative output, shaped like MySQL Shell's messages; schema and table counts are placeholders

admin@bastion-01:~
$ mysqlsh dumper@db-01:3306 --jsJS > util.dumpInstance("/backups/app-2026-11", {threads: 8})Acquiring global read lockGlobal read lock acquiredInitializing - done2 out of 6 schemas will be dumped and within them 48 tables, 3 views.Gathering information - doneAll transactions have been startedLocking instance for backupGlobal read lock has been releasedRunning data dump using 8 threads.Writing DDL - doneStarting data dumpTables dumped: 48 done

Watch the first lines: writes are blocked from "Global read lock acquired" until "Global read lock has been released". A long-running query on the source holds up the read lock, so check the process list before you start.

mydumper picks its method with --sync-thread-lock-mode. In the v1.0.19-1 source, the default AUTO gives MySQL and Percona Server 8.x LOCK INSTANCE FOR BACKUP plus FLUSH TABLES WITH READ LOCK, and other MySQL major versions, such as 9.7 and 26.7, the read lock alone. On RDS and Aurora, AUTO stops with the error quoted above. LOCK_ALL locks every exported table and needs LOCK TABLES. SAFE_NO_LOCK takes no lock, and the lock modes page says it stops if the binary log position moved while threads synced.

bash
# RDS or Aurora source: pick the lock mode explicitly
mydumper --defaults-extra-file=/etc/mydumper/source.cnf \
  --database=appdb --outputdir=/backups/appdb-2026-11 \
  --sync-thread-lock-mode=LOCK_ALL \
  --triggers --events --routines

Interpretation: on a busy primary the position keeps moving, so SAFE_NO_LOCK is likely to abort. To avoid locking an RDS primary, dump from a read replica with replication stopped, as AWS recommends for mysqldump.

How do threads, chunking, and compression work in each tool?

Shell splits each table into files of about bytesPerChunk (64 MB before compression) using table statistics, falls back to row counts for tables without a primary or unique key, and chunks partitions independently. Each thread has its own connection, and maxRate caps reads per thread.

javascript
// mysqlsh dumper@db-01:3306 --js
// threads and bytesPerChunk are starting points; set final values from a rehearsal
util.dumpInstance("/backups/app-2026-11", {
  threads: 8,
  bytesPerChunk: "128M",
  compression: "zstd;level=3",
  maxRate: "50M",
  checksum: true
})

Interpretation: checksum: true writes @.checksums.json, which util.loadDump(..., {checksum: true}) verifies after each table or chunk loads. zstd accepts levels 1 to 22 and gzip 0 to 9; both default to 1.

mydumper chunks by rows. --rows accepts MIN:START_AT:MAX, and its help text says it doubles a chunk that takes under 1 second and halves one over 2 seconds. --chunk-filesize splits by MB instead, and string primary keys split only with --split-string-pk.

bash
mydumper --defaults-extra-file=/etc/mydumper/source.cnf \
  --database=appdb --outputdir=/backups/appdb-2026-11 \
  --threads=8 --rows=100000 --compress=zstd \
  --triggers --events --routines --checksum-all

Interpretation: name the algorithm in --compress: the help text says a bare flag means gzip, but the code picks zstd when it is installed. myloader --checksum=fail stops on a mismatch; the default is warn. Neither project publishes a head-to-head benchmark, so time both tools on a production-sized copy.

Can you dump straight to S3, OCI, or Azure object storage?

Shell can. Pass osBucketName and osNamespace for OCI, s3BucketName (with s3Region, s3Profile, or s3EndpointOverride) for S3-compatible stores, or azureContainerName for Azure. The output path becomes an object prefix, and util.loadDump() takes the same options.

javascript
util.dumpInstance("app-2026-11", {s3BucketName: "example-dumps", s3Region: "us-east-1", threads: 8})

// mysqlsh admin@target:3306 --js
util.loadDump("app-2026-11", {s3BucketName: "example-dumps", s3Region: "us-east-1", threads: 8,
  progressFile: "/var/tmp/app-2026-11-progress.json"})

Interpretation: 26.10.0 fixed uploads to S3 buckets with Object Lock retention, which could fail earlier because checksum headers were not sent. To skip intermediate storage, the copy utilities stream from source to target directly.

mydumper has no object storage client. --stream writes each finished file to standard output, and myloader --stream reads them from standard input, so pipe through a CLI such as aws s3 cp -.

bash
mydumper --defaults-extra-file=/etc/mydumper/source.cnf \
  --database=appdb --outputdir=/backups/stage \
  --threads=8 --compress=zstd --triggers --events --routines --stream \
  | aws s3 cp - s3://example-dumps/appdb-2026-11.stream --expected-size 600000000000

aws s3 cp s3://example-dumps/appdb-2026-11.stream - \
  | myloader --defaults-extra-file=/etc/mydumper/target.cnf \
      --directory=/restore/stage --threads=8 --stream

Interpretation: the AWS CLI reference requires --expected-size in bytes for streams over 50 GB, so set it from your own size estimate. A failed single-object upload starts again from zero.

How do you prepare a dump for HeatWave, RDS, or Aurora?

For MySQL HeatWave, set ocimds: true. Shell then comments out DATA DIRECTORY, INDEX DIRECTORY, and ENCRYPTION clauses, and flags non-InnoDB tables, restricted grants, and missing primary keys, which HeatWave high availability requires. A dry run lists every issue without writing a dump.

javascript
util.dumpInstance("/backups/app-2026-11", {dryRun: true, ocimds: true, targetVersion: "8.4.7"})

util.dumpInstance("/backups/app-2026-11", {
  ocimds: true, targetVersion: "8.4.7",
  compatibility: ["force_innodb", "strip_restricted_grants", "strip_tablespaces",
                  "skip_invalid_accounts", "create_invisible_pks"]
})

Interpretation: set targetVersion to the target's exact version; without it, Shell assumes its own. create_invisible_pks makes the loader add a my_row_id invisible primary key and needs MySQL 8.0.24 or later on the target. strip_definers is not needed for HeatWave 8.2.0 and later.

RDS and Aurora have no flag of their own, but compatibility works without ocimds, as the manual's S3 example shows. Set local_infile to 1 in the RDS parameter group, because the loader uses LOAD DATA LOCAL INFILE. Shell skips users by default (loadUsers: false), so create application accounts yourself.

bash
# mydumper: drop definers at load time
myloader --defaults-extra-file=/etc/mydumper/target.cnf \
  --directory=/backups/appdb-2026-11 --threads=8 \
  --skip-definer

Interpretation: --replace-definer sets a definer that exists on the target instead. Otherwise the mydumper privileges page lists SET_USER_ID and SYSTEM_USER, and the Shell manual notes that MySQL 8.4 replaced SET_USER_ID with SET_ANY_DEFINER. mydumper has no managed-target checks, so run Shell's dry run against the source even when mydumper moves the data.

How do you load with deferred secondary indexes?

Inserting into a table that already has every secondary index updates each index row by row. Adding the indexes after the rows lets InnoDB build them in bulk, and the Shell manual says deferring "can reduce loading times". The defaults differ.

Parallel chunked dump, then a load with deferred indexes

Rows move in parallel chunks; secondary indexes are added after a table's last chunk lands

Parallel chunked dump and load with deferred indexes Step 1: worker threads read the source MySQL server from one consistent snapshot and write each table as compressed chunk files. Step 2: loader threads insert the chunks in parallel into target tables created with only the primary key. Step 3: after a table's last chunk loads, its secondary indexes are added. MySQL Shell does this with deferTableIndexes set to all; myloader does it by default. Source MySQL one snapshot Chunk files compressed Target tables primary key only Add indexes per table 1 2 3 N threads read chunks N threads load chunks Shell: deferTableIndexes: "all" myloader: --optimize-keys (default)

Plan for step 3: a load that shows all rows loaded can still be adding indexes to the largest tables. Leave time for it before replication catch-up.

Shell's deferTableIndexes takes off, fulltext (the default), or all, which creates only the primary key and auto-increment indexes during the load. Shell adds a table's indexes at the same time using the server's parallel index build, so innodb_ddl_threads on the load sessions applies.

javascript
// mysqlsh admin@target:3306 --js
util.loadDump("/backups/app-2026-11", {
  threads: 8,
  deferTableIndexes: "all",
  sessionInitSql: ["SET SESSION innodb_ddl_threads = 8"],
  analyzeTables: "on",
  checksum: true
})

Interpretation: analyzeTables: "on" runs ANALYZE TABLE after each table loads, so statistics are fresh at cutover. To build indexes later on purpose, load with loadIndexes: false, then rerun with loadIndexes: true and deferTableIndexes: "all".

myloader's --optimize-keys defaults to AFTER_IMPORT_PER_TABLE: it strips the keys from each CREATE TABLE and adds them with ALTER TABLE once that table's chunks are in. Foreign keys go in a later stage. AFTER_IMPORT_ALL_TABLES waits for every table, and SKIP turns it off.

bash
myloader --defaults-extra-file=/etc/mydumper/target.cnf \
  --directory=/backups/appdb-2026-11 --threads=8 \
  --optimize-keys=AFTER_IMPORT_PER_TABLE \
  --max-threads-for-index-creation=4 --checksum=fail

Interpretation: --max-threads-for-index-creation (default 4) limits concurrent index builds, and --optimize-keys-batchsize caps indexes per ALTER TABLE.

What happens if a load fails halfway?

Shell records completed steps in a progress file, load-progress.<server_uuid>.json in the dump directory by default. Rerun the same util.loadDump() command and it skips finished work; the load utility manual says it deduplicates partly loaded tables itself.

javascript
// Resume: same URL and options as the interrupted run
util.loadDump("/backups/app-2026-11", {threads: 8, deferTableIndexes: "all"})

// Start over only after dropping everything the partial load created
util.loadDump("/backups/app-2026-11", {threads: 8, resetProgress: true})

Interpretation: never edit dump files between failure and resume; the manual calls that undefined behavior. If the load stops with MySQL Error 1197 on max_binlog_cache_size, set maxBytesPerTransaction to that value or lower and resume.

myloader writes a resume file only on a graceful stop: a confirmed Ctrl+C or SIGTERM. Restart with --resume; without the flag, myloader stops with Resume file found but --resume has not been provided.

bash
myloader --defaults-extra-file=/etc/mydumper/target.cnf \
  --directory=/backups/appdb-2026-11 --threads=8 --resume

Interpretation: a process killed outright (out of memory, SIGKILL, host loss) leaves no resume file, and the default --drop-table=FAIL stops on the existing tables. Drop the target schema and reload.

How do you carry GTIDs across for replication?

For a short cutover you load the dump, then replicate from the source until the target catches up. The target needs the source's GTID set at the snapshot recorded as purged. Shell stores it as gtidExecuted in @.json, and showMetadata: true prints it with the binary log position.

javascript
// Self-managed target with gtid_mode=ON
util.loadDump("/backups/app-2026-11", {threads: 8, skipBinlog: true, updateGtidSet: "append"})
sql
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST = 'db-01.internal', SOURCE_PORT = 3306,
  SOURCE_USER = 'repl', SOURCE_PASSWORD = 'change-me',
  SOURCE_SSL = 1, SOURCE_AUTO_POSITION = 1;
START REPLICA;

Interpretation: append requires that the dumped set does not overlap the target's gtid_executed, and it is not for table dumps or Group Replication targets. On HeatWave, run SET @@GLOBAL.gtid_purged = "+<set>" after the load. On RDS, use mysql.rds_set_external_source_gtid_purged and the auto-position procedure from the RDS stored procedure reference, checking the versions each supports.

mydumper writes File, Position, and Executed_Gtid_Set into metadata. Because myloader already loads with binary logging off, --set-gtid-purged can run SET GLOBAL gtid_purged from that file after the import.

bash
grep -E 'File|Position|Executed_Gtid_Set' /backups/appdb-2026-11/metadata
myloader --defaults-extra-file=/etc/mydumper/target.cnf \
  --directory=/backups/appdb-2026-11 --threads=8 --set-gtid-purged

Interpretation: for a source without GTIDs, use the file and position; Shell's manual points to ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS, available from MySQL 8.0.23.

How do you migrate between MySQL versions with dump and load?

A logical dump lets you skip versions or change platforms because the target rebuilds every table. The risk moves to the DDL: reserved words, removed functions, and authentication plugins. Shell's upgrade checker finds those on the source, whichever tool copies the data. 26.10.0 added a removedLegacyHashingFunctions check for MD5(), SHA1(), and SHA() in generated columns, functional indexes, CHECK constraints, and defaults, which must change before moving to MySQL 9.6 or later.

javascript
util.checkForServerUpgrade("admin@db-01:3306", {targetVersion: "8.4.7", outputFormat: "TEXT"})

Interpretation: targetVersion accepts any release from 8.0.11 up to the Shell's own version. The loader refuses a dump whose source major version is not consecutive with the target's; set ignoreVersion: true only after the checker comes back clean.

Migration runbook: dump, load, replicate, cut over

Eight steps with MySQL Shell; step 7 is the outage window

  1. Check the source against the target version

    Fix every reported error on the source first.

    util.checkForServerUpgrade("admin@db-01:3306", {targetVersion: "8.4.7"})
  2. Dry-run the dump

    Lists compatibility issues and writes nothing.

    util.dumpInstance("/backups/app", {dryRun: true, ocimds: true})
  3. Prepare the target

    Turn on local_infile and GTIDs; create application and replication accounts.

    SET PERSIST local_infile = 1;
  4. Dump the source

    Writes pause only until every thread has its snapshot.

    util.dumpInstance("/backups/app", {threads: 8, checksum: true})
  5. Load with deferred indexes and the GTID set

    Rerun the same command to resume after a failure.

    util.loadDump("/backups/app", {deferTableIndexes: "all", skipBinlog: true, updateGtidSet: "append", checksum: true})
  6. Replicate until lag is zero

    On RDS, use the stored procedures instead.

    CHANGE REPLICATION SOURCE TO SOURCE_HOST = 'db-01', SOURCE_AUTO_POSITION = 1; START REPLICA;
  7. Stop writes and cut over

    Outage

    Make the source read-only, wait until the target has applied every source GTID, then repoint the application.

    SET GLOBAL super_read_only = ON;
  8. Verify and keep the source

    Compare row counts and checksums, then stop replication. Keep the old server until you no longer need a way back.

    STOP REPLICA; RESET REPLICA ALL;

Rollback point: until step 7 the source takes all writes. After the application writes to the target, going back means replicating those writes in reverse or losing them, so set up reverse replication before cutover if you need that option.

Frequently asked questions

Is mydumper faster than MySQL Shell dump and load?

There is no general answer, and neither project publishes a head-to-head benchmark. Both use parallel threads and chunked files, and both can defer secondary indexes. Speed depends on table sizes, primary key types, row width, storage, network, and target settings such as binary logging. Rehearse both tools on a production-sized copy and time the full dump, transfer, load, and verification.

Can MySQL Shell util.loadDump() load a mydumper dump?

No. util.loadDump() is documented for dumps created by MySQL Shell's instance, schema, and table dump utilities, which use JSON metadata and tab-separated data files. myloader reads mydumper's own SQL or LOAD DATA files and its metadata file. Use the same tool at both ends of a migration, with matched versions.

Does mydumper work with Amazon RDS and Aurora MySQL?

Yes, but not in its default lock mode. mydumper 1.0.19 stops on RDS and Aurora unless you set --sync-thread-lock-mode to LOCK_ALL or SAFE_NO_LOCK, because RDS does not support a global read lock. LOCK_ALL needs the LOCK TABLES privilege. SAFE_NO_LOCK aborts if the binary log position moves, so it suits a read replica with replication stopped.

Can I resume a MySQL Shell load after it fails?

Yes. util.loadDump() records completed steps in a progress state file, stored in the dump directory by default. Run the same command again and it skips finished work and handles partly loaded tables. Do not change the dump files in between. Use resetProgress only after dropping every object the partial load created on the target.

Key takeaways

  • Use MySQL Shell 26.10.0 or mydumper 1.0.19-1, and load with the same or a newer release than the one that dumped.
  • Pass --triggers --events --routines to mydumper and decide on binary logging for myloader; Shell includes those objects and logs by default.
  • On RDS and Aurora sources, set mydumper's lock mode to LOCK_ALL or SAFE_NO_LOCK, or dump from a stopped read replica.
  • Run Shell's checkForServerUpgrade and an ocimds or compatibility dry run before any cross-version or managed-target load, whichever tool moves the data.
  • Apply the dump's GTID set with updateGtidSet or --set-gtid-purged, then replicate to close the gap before cutover.

Plan a MySQL logical migration with JusDB

JusDB's MySQL migration team can review your source for version and managed-target compatibility and help you choose between MySQL Shell and mydumper for your tables and network. We can also write and rehearse a dump, load, and replication runbook with a tested rollback point.

Bring your source and target versions, data size and largest tables, and the outage window you can accept. Your team approves every production change.

Share this article

Database engineering notes

Articles like this one, in your inbox. No spam, unsubscribe anytime.

JusDB Team

Official JusDB content team

Keep reading

MySQL 26.7 and Calendar Versioning: Which MySQL Should You Run Now?

Learn how MySQL 26.7 and 26.10 calendar versions work, how LTS and Innovation tracks differ, which upgrade paths are allowed, and which MySQL version to run in 2026.

MySQL18 minOct 30, 2026
Read

MySQL Clone Plugin: Provisioning Replicas Without a Backup Restore

Provision a MySQL 8.4 replica with the clone plugin: prerequisites, privileges, DDL and bandwidth settings, progress monitoring, and GTID replication after the clone.

MySQL18 minOct 29, 2026
Read

MySQL INSTANT DDL: What Is Instant and What Still Copies the Table

Learn which ALTER TABLE changes are instant on MySQL 8.4 and 9.7, how to make MySQL refuse a silent table copy, and how to avoid metadata lock pileups.

MySQL18 minOct 29, 2026
Read