MySQL

pt-online-schema-change: Zero-Downtime MySQL ALTER TABLE

Perform zero-downtime MySQL schema changes with pt-online-schema-change. Covers how it works, throttle settings, max-load protection, and when to use gh-ost instead.

JusDB Team
August 6, 2025
Updated June 20, 2026
5 min read

ALTER TABLE in MySQL locks the table for the duration of the change. For large tables this means minutes or hours of downtime. pt-online-schema-change (pt-osc) performs the change online with minimal locking.

How pt-osc Works

text
1. Create new table with the desired schema
2. Add triggers on original table (INSERT/UPDATE/DELETE) to sync changes
3. Copy rows in small batches from original → new table
4. Rename new table → original (atomic, milliseconds lock)
5. Drop original table (now the old one)

Installation

bash
apt-get install percona-toolkit
# or
yum install percona-toolkit

Basic Usage

bash
# Add a column to a large table with zero downtime
pt-online-schema-change \
  --alter 'ADD COLUMN last_login TIMESTAMP NULL' \
  --host=localhost \
  --user=root \
  --password=secret \
  --database=myapp \
  --table=users \
  --execute

Common ALTER Operations

bash
# Add index
pt-online-schema-change \
  --alter 'ADD INDEX idx_users_email (email)' \
  D=myapp,t=users --execute

# Change column type
pt-online-schema-change \
  --alter 'MODIFY COLUMN status ENUM("active","inactive","pending") NOT NULL DEFAULT "active"' \
  D=myapp,t=users --execute

# Drop column
pt-online-schema-change \
  --alter 'DROP COLUMN legacy_field' \
  D=myapp,t=users --execute

Throttling to Protect Production

bash
pt-online-schema-change \
  --alter 'ADD COLUMN score INT DEFAULT 0' \
  --chunk-size=1000 \
  --max-load='Threads_running=25' \
  --critical-load='Threads_running=50' \
  --sleep=0.5 \
  D=myapp,t=orders --execute

# --max-load: pause copying if MySQL load exceeds this
# --critical-load: abort if load reaches critical threshold
# --sleep: seconds to sleep between chunks

Dry Run First

bash
# Always dry-run first (omit --execute)
pt-online-schema-change \
  --alter 'ADD COLUMN score INT DEFAULT 0' \
  --dry-run \
  D=myapp,t=orders
Warning: pt-osc uses triggers. It does not work on tables that already have triggers. Consider gh-ost (GitHub's online schema tool) for tables with existing triggers — it uses binlog-based replication instead.

Key Takeaways

  • Always dry-run first before using --execute
  • Set --max-load and --critical-load to protect production during the copy
  • pt-osc does not work on tables with existing triggers — use gh-ost instead
  • The final rename step takes a brief lock (~milliseconds) — schedule during low traffic if possible

JusDB Can Help

Zero-downtime schema changes require careful planning and monitoring. JusDB can execute and validate your MySQL schema migrations safely.

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 Explained (2026): InnoDB, 8.4 LTS, Replication & Production Patterns

Everything you need to know about MySQL: storage engines, replication topologies, performance tuning, and cloud deployment. From basics to advanced optimization.

MySQL9 minMay 13, 2026
Read

MySQL binlog Retention, Rotation & Purge: Production Guide (2026)

Configure MySQL binlog retention safely: binlog_expire_logs_seconds, manual purging rules, AWS RDS retention, and the disk-exhaustion failure mode you should monitor for.

MySQL10 minMay 9, 2026
Read

MySQL "Communications Link Failure": Fix wait_timeout, HikariCP & All 8 Timeout Variables

MySQL wait_timeout, net_read_timeout, innodb_lock_wait_timeout and max_execution_time — production tuning rules and the HikariCP alignment trick that prevents 'communications link failure' errors.

MySQL6 minMay 9, 2026
Read