Last updated: July 2026 — rewritten for MySQL 8.x (originally written for 5.6)
The four BLOB types and their real limits
| Type | Max size |
|---|---|
| TINYBLOB | 255 bytes |
| BLOB | 64 KB |
| MEDIUMBLOB | 16 MB |
| LONGBLOB | 4 GB |
CREATE TABLE documents (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
mime_type VARCHAR(100) NOT NULL,
content LONGBLOB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
The error everyone hits: packet too large
Declaring LONGBLOB is not enough — inserts are capped by max_allowed_packet (default 64 MB in MySQL 8). Larger uploads fail with “Got a packet bigger than ‘max_allowed_packet’ bytes” or a silently dropped connection.
# /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
max_allowed_packet = 256M
Restart MySQL, and remember the client side has the same setting — in PHP/PDO the server value governs, but tools like mysqldump need --max-allowed-packet=256M too. Verify with:
SHOW VARIABLES LIKE 'max_allowed_packet';
Inserting from PHP the right way
Use prepared statements with the LOB parameter type rather than concatenating megabytes into a query string:
$pdo = new PDO($dsn, $user, $pass);
$stmt = $pdo->prepare(
'INSERT INTO documents (filename, mime_type, content) VALUES (?, ?, ?)'
);
$fp = fopen('/path/report.pdf', 'rb');
$stmt->bindValue(1, 'report.pdf');
$stmt->bindValue(2, 'application/pdf');
$stmt->bindParam(3, $fp, PDO::PARAM_LOB);
$stmt->execute();
Streaming via PDO::PARAM_LOB avoids holding the whole file in PHP memory.
Should the file be in the database at all?
Honest answer: usually not. BLOBs bloat the buffer pool, slow backups, and make replication chatty. My rule: under ~1 MB and needing transactional integrity with the row (signatures, thumbnails, generated PDFs) → BLOB is fine. Bigger or high-traffic files → store on disk or S3-style object storage and keep only the path + checksum in MySQL:
content_path VARCHAR(500), sha256 CHAR(64)
You keep referential integrity where it matters and MySQL stays fast. If you’re on Laravel, that pattern is exactly what Storage::put() + a path column gives you — see Laravel file uploads.
