Skip to main content

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples in this guide work on VillageSQL. Install Now →
Inserting rows one at a time is the fastest way to make a large import slow. MySQL processes each single-row INSERT as a full transaction commit — parse, plan, execute, fsync. At scale, that per-row overhead dominates. Here’s how to avoid it.

Multi-Row INSERT

The simplest optimization: batch multiple rows into one statement.
Batch size matters. A few hundred to a few thousand rows per statement is typically optimal — larger batches increase memory pressure and extend transaction duration. Benchmark to find the sweet spot for your workload; 500–2000 rows per batch is a common starting point.

Transaction Batching

Wrapping many INSERTs in a single transaction avoids per-statement commit overhead:
Without explicit transactions, each INSERT auto-commits. Auto-commit means each row is individually flushed to disk. Wrapping 500 inserts in one transaction does one flush. Combine this with multi-row INSERT: batch rows into groups of 500–1000 per statement, wrap each batch in a transaction.

LOAD DATA INFILE

For large CSV imports, LOAD DATA INFILE is the fastest native MySQL option — significantly faster than batched INSERTs because it bypasses SQL parsing for each row.
The file must be readable by the MySQL server process. By default, the file path is restricted to the directory specified by secure_file_priv:
To load a file from the client machine rather than the server:
LOCAL INFILE requires local_infile = ON on the server and the client to have the --local-infile flag enabled.

INSERT … SELECT

For copying data between tables (ETL patterns, staging tables), INSERT...SELECT is faster than reading rows into the application and inserting them back:

Disabling Indexes During Bulk Load

For MyISAM tables, disabling non-unique index updates during a bulk insert speeds things up significantly:
For InnoDB, this doesn’t have the same effect. Instead, InnoDB automatically uses a “change buffer” for secondary index updates. For maximum InnoDB bulk-load performance:
Re-enable these immediately after the load. Disabling unique_checks on data that actually has duplicates produces a corrupt index.

Performance Comparison

Exact numbers depend heavily on row size, indexes, hardware, and configuration. Benchmark your specific case.

Frequently Asked Questions

How large should each INSERT batch be?

Start at 500–1000 rows and benchmark. Larger batches reduce per-commit overhead but increase memory usage and transaction duration (which holds locks longer). If a batch fails, the whole batch rolls back — smaller batches mean less rollback work. Most applications find the optimal batch size between 500 and 5000 rows.

Can I use LOAD DATA INFILE with ON DUPLICATE KEY UPDATE?

LOAD DATA supports REPLACE or IGNORE for duplicate handling, but not ON DUPLICATE KEY UPDATE. For upsert semantics during bulk load, load into a staging table, then use INSERT...SELECT...ON DUPLICATE KEY UPDATE from the staging table to the target.

Does bulk inserting bypass triggers?

No. Triggers fire for every row affected, including those loaded by LOAD DATA INFILE and INSERT...SELECT. If your trigger does significant work, it will slow bulk loads proportionally. For pure data migrations, you may want to temporarily disable triggers.

Troubleshooting

See also