MySQL Online Schema Change on Production with gh-ost: Testing on AWS Aurora Database
09/11/2026
10
In the previous article, we tested gh-ost on an on-premises MySQL server. In this article, let’s look at the results of running gh-ost on AWS Aurora MySQL, and see whether a MySQL online schema change with gh-ost is just as safe on a managed cloud database.

Setting Up the Test Environment
| No. | Running Service | OS | Instance Type | VPC Zone |
|---|---|---|---|---|
| 1 | MySQL 8.0.42 (binlog and performance schema enabled) | MySQL Aurora 3.10.3 | db.t4g.medium | Zone A |
| 2 | Sysbench 1.1.0 | Amazon Linux 2023 | t3.medium | Zone A |
| 3 | gh-ost 1.1.10 | Amazon Linux 2023 | t3.medium | Zone A |
Test Scenario
As in the scenario we ran on the on-premises server, we will change the data type of column k in table sbtest1 from INT to BIGINT while the application is still running queries (simulated with sysbench using 20 parallel threads), across 2 + 1 cases:
#1: Connect to the database and execute the query:
ALTER TABLE sbtest1 MODIFY COLUMN k BIGINT NOT NULL;
#2: Use gh-ost to ALTER TABLE, with chunk size = 5000 and nice-ratio = 0.1
#3: Optimize gh-ost to avoid increasing database CPU, with chunk size = 5000 and nice-ratio = 20
Case 1: Connect to the Database and Execute the Query
Step 1: Run sysbench to simulate application queries
sysbench --db-driver=mysql --mysql-user=admin --mysql-password='YOUR_PASSWORD' --mysql-db=sysbench --tables=1 --table-size=10000000 --auto-inc=off --threads=20 --time=0 --rate=2 --rand-type=pareto --report-interval=1 --mysql-host="YOUR_AURORA_CLUSTER_ENDPOINT" oltp_read_write prepare 2>&1 | tee sysbench.log
sysbench --db-driver=mysql --mysql-user=admin --mysql-password='YOUR_PASSWORD' --mysql-db=sysbench --tables=1 --table-size=10000000 --auto-inc=off --threads=20 --time=0 --rate=2 --rand-type=pareto --report-interval=1 --mysql-host="YOUR_AURORA_CLUSTER_ENDPOINT" oltp_read_write run 2>&1 | tee sysbench.log
WARNING: Both event and time limits are disabled, running an endless test
sysbench 1.1.0-3ceba0b (using bundled LuaJIT 2.1.0-beta3)
Running the test with following options:
Number of threads: 20
Target transaction rate: 2/sec
Report intermediate results every 1 second(s)
Initializing random number generator from current time
Initializing worker threads...
Threads started!
[ 1s ] thds: 20 tps: 0.00 qps: 5.98 (r/w/o: 4.99/0.00/1.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 1s ] queue length: 0, concurrency: 1
[ 2s ] thds: 20 tps: 1.00 qps: 14.01 (r/w/o: 9.01/4.00/1.00) lat (ms,95%): 45.79 err/s: 0.00 reconn/s: 0.00
[ 2s ] queue length: 0, concurrency: 0
[ 3s ] thds: 20 tps: 4.00 qps: 80.01 (r/w/o: 56.00/16.00/8.00) lat (ms,95%): 37.56 err/s: 0.00 reconn/s: 0.00
[ 3s ] queue length: 0, concurrency: 0
...
Step 2: Run ALTER TABLE with native SQL
Database changed
MySQL [sysbench]> ALTER TABLE sbtest1 MODIFY COLUMN k BIGINT NOT NULL;
Query OK, 10000000 rows affected (3 min 33.372 sec)
Records: 10000000 Duplicates: 0 Warnings: 0
Result: It took ~3 minutes 33 seconds for the query above to finish.
Observations
The number of queries executed by sysbench still dropped to 0, just like last time, after the ALTER TABLE query was executed.
[ 66s ] thds: 20 tps: 0.00 qps: 16.00 (r/w/o: 15.00/0.00/1.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 66s ] queue length: 0, concurrency: 14
[ 67s ] thds: 20 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 67s ] queue length: 0, concurrency: 14
[ 68s ] thds: 20 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 68s ] queue length: 0, concurrency: 14
[ 69s ] thds: 20 tps: 0.00 qps: 89.00 (r/w/o: 83.00/0.00/6.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 69s ] queue length: 1, concurrency: 20
[ 70s ] thds: 20 tps: 0.00 qps: 1.00 (r/w/o: 1.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 70s ] queue length: 2, concurrency: 20
...
[ 143s ] thds: 20 tps: 24.99 qps: 748.84 (r/w/o: 556.88/121.97/69.99) lat (ms,95%): 39206.03 err/s: 20.00 reconn/s: 0.00
[ 143s ] queue length: 39, concurrency: 20
[ 144s ] thds: 20 tps: 61.01 qps: 971.19 (r/w/o: 647.13/222.04/102.02) lat (ms,95%): 26861.48 err/s: 0.00 reconn/s: 0.00
[ 144s ] queue length: 0, concurrency: 0
[ 145s ] thds: 20 tps: 2.00 qps: 40.00 (r/w/o: 28.00/8.00/4.00) lat (ms,95%): 12.98 err/s: 0.00 reconn/s: 0.00
[ 145s ] queue length: 0, concurrency: 0
[ 146s ] thds: 20 tps: 2.00 qps: 40.00 (r/w/o: 28.00/8.00/4.00) lat (ms,95%): 12.75 err/s: 0.00 reconn/s: 0.00
[ 146s ] queue length: 0, concurrency: 0


As in the on-premises test, from the moment ALTER TABLE was executed, the transactions created by sysbench could not be executed because they had to wait for the lock to be released.
- Database CPU Utilization increased to ~90%
- The number of blocked transactions increased
Case 2: Use gh-ost to ALTER TABLE, with Chunk Size = 5000 and Nice-Ratio = 0.1
Step 1: Run sysbench to simulate application queries
sysbench --db-driver=mysql --mysql-user=admin --mysql-password='YOUR_PASSWORD' --mysql-db=sysbench --tables=1 --table-size=10000000 --auto-inc=off --threads=20 --time=0 --rate=2 --rand-type=pareto --report-interval=1 --mysql-host="YOUR_AURORA_CLUSTER_ENDPOINT" oltp_read_write prepare 2>&1 | tee sysbench.log
sysbench --db-driver=mysql --mysql-user=admin --mysql-password='YOUR_PASSWORD' --mysql-db=sysbench --tables=1 --table-size=10000000 --auto-inc=off --threads=20 --time=0 --rate=2 --rand-type=pareto --report-interval=1 --mysql-host="YOUR_AURORA_CLUSTER_ENDPOINT" oltp_read_write run 2>&1 | tee sysbench.log
WARNING: Both event and time limits are disabled, running an endless test
sysbench 1.1.0-3ceba0b (using bundled LuaJIT 2.1.0-beta3)
Running the test with following options:
Number of threads: 20
Target transaction rate: 2/sec
Report intermediate results every 1 second(s)
Initializing random number generator from current time
Initializing worker threads...
Threads started!
[ 1s ] thds: 20 tps: 0.00 qps: 5.98 (r/w/o: 4.99/0.00/1.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 1s ] queue length: 0, concurrency: 1
[ 2s ] thds: 20 tps: 1.00 qps: 14.01 (r/w/o: 9.01/4.00/1.00) lat (ms,95%): 45.79 err/s: 0.00 reconn/s: 0.00
[ 2s ] queue length: 0, concurrency: 0
[ 3s ] thds: 20 tps: 4.00 qps: 80.01 (r/w/o: 56.00/16.00/8.00) lat (ms,95%): 37.56 err/s: 0.00 reconn/s: 0.00
[ 3s ] queue length: 0, concurrency: 0
[ 4s ] thds: 20 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 4s ] queue length: 0, concurrency: 0
[ 5s ] thds: 20 tps: 1.00 qps: 21.00 (r/w/o: 14.00/4.00/3.00) lat (ms,95%): 33.72 err/s: 0.00 reconn/s: 0.00
[ 5s ] queue length: 0, concurrency: 1
[ 6s ] thds: 20 tps: 3.00 qps: 59.00 (r/w/o: 42.00/12.00/5.00) lat (ms,95%): 40.37 err/s: 0.00 reconn/s: 0.00
[ 6s ] queue length: 0, concurrency: 0
...
Step 2: Run ALTER TABLE with gh-ost, chunk size = 5000 and nice-ratio = 0.1
./gh-ost -allow-on-master -assume-rbr -exact-rowcount \
-critical-load Threads_running=400 -critical-load-hibernate-seconds 60 \
-database sysbench -max-load Threads_running=100 -nice-ratio 0.1 \
-chunk-size 5000 -ask-pass -table sbtest1 -user admin \
-host YOUR_AURORA_CLUSTER_ENDPOINT \
-postpone-cut-over-flag-file /home/ec2-user/gh-ost-sentinel \
-throttle-additional-flag-file /home/ec2-user/gh-ost-throttle_ghost.sbtest1 \
-alter 'MODIFY COLUMN k BIGINT NOT NULL' \
-verbose -execute -skip-metadata-lock-check 2>&1 | tee gh-ost.log
...
Copy: 0/9873575 0.0%; Applied: 0; Backlog: 1/1000; Time: 0s(total), 0s(copy); streamer: mysql-bin-changelog.000002:2609576; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: N/A
2026-08-19 08:11:53 INFO Copy: 0/9873575 0.0%; Applied: 0; Backlog: 1/1000; Time: 0s(total), 0s(copy); streamer: mysql-bin-changelog.000002:2609576; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: N/A []
Copy: 0/9873575 0.0%; Applied: 0; Backlog: 25/1000; Time: 1s(total), 1s(copy); streamer: mysql-bin-changelog.000002:2622867; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: N/A
2026-08-19 08:11:54 INFO Copy: 0/9873575 0.0%; Applied: 0; Backlog: 25/1000; Time: 1s(total), 1s(copy); streamer: mysql-bin-changelog.000002:2622867; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: N/A []
Copy: 19998/9873580 0.2%; Applied: 20; Backlog: 1/1000; Time: 2s(total), 2s(copy); streamer: mysql-bin-changelog.000002:6527307; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 16m27s
2026-08-19 08:11:55 INFO Copy: 19998/9873580 0.2%; Applied: 20; Backlog: 1/1000; Time: 2s(total), 2s(copy); streamer: mysql-bin-changelog.000002:6527307; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 16m27s []
Copy: 44998/9873582 0.5%; Applied: 28; Backlog: 1/1000; Time: 3s(total), 3s(copy); streamer: mysql-bin-changelog.000002:11409647; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 6m33s
2026-08-19 08:11:56 INFO Copy: 44998/9873582 0.5%; Applied: 28; Backlog: 1/1000; Time: 3s(total), 3s(copy); streamer: mysql-bin-changelog.000002:11409647; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 6m33s []
Copy: 64998/9873586 0.7%; Applied: 52; Backlog: 2/1000; Time: 4s(total), 4s(copy); streamer: mysql-bin-changelog.000002:15327928; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 8m10s
2026-08-19 08:11:57 INFO Copy: 64998/9873586 0.7%; Applied: 52; Backlog: 2/1000; Time: 4s(total), 4s(copy); streamer: mysql-bin-changelog.000002:15327928; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 8m10s []
Copy: 89997/9873587 0.9%; Applied: 60; Backlog: 1/1000; Time: 5s(total), 5s(copy); streamer: mysql-bin-changelog.000002:20210878; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 6m31s
2026-08-19 08:11:58 INFO Copy: 89997/9873587 0.9%; Applied: 60; Backlog: 1/1000; Time: 5s(total), 5s(copy); streamer: mysql-bin-changelog.000002:20210878; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 6m31s []
Copy: 109995/9873589 1.1%; Applied: 72; Backlog: 2/1000; Time: 6s(total), 6s(copy); streamer: mysql-bin-changelog.000002:24121921; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 8m8s
2026-08-19 08:11:59 INFO Copy: 109995/9873589 1.1%; Applied: 72; Backlog: 2/1000; Time: 6s(total), 6s(copy); streamer: mysql-bin-changelog.000002:24121921; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 8m8s []
Copy: 129995/9873589 1.3%; Applied: 72; Backlog: 0/1000; Time: 7s(total), 7s(copy); streamer: mysql-bin-changelog.000002:28014402; Lag: 0.20s, HeartbeatLag: 0.34s, State: migrating; ETA: 8m7s
2026-08-19 08:12:00 INFO Copy: 129995/9873589 1.3%; Applied: 72; Backlog: 0/1000; Time: 7s(total), 7s(copy); streamer: mysql-bin-changelog.000002:28014402; Lag: 0.20s, HeartbeatLag: 0.34s, State: migrating; ETA: 8m7s []
Copy: 134995/9873590 1.4%; Applied: 76; Backlog: 2/1000; Time: 8s(total), 8s(copy); streamer: mysql-bin-changelog.000002:29006018; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: 32m27s
Observations
- gh-ost still creates the temporary table
_sbtest1_ghowith the modified schema, while replicating data from the binlog into the temporary table. - The migration status is continuously updated on the console, along with the estimated completion time.
Copy: 9119862/10000155 91.2%; Applied: 4500; Backlog: 3/1000; Time: 9m51s(total), 9m51s(copy); streamer: mysql-bin-changelog.000015:32280777; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 44s
2026-08-19 08:21:44 INFO Copy: 9119862/10000155 91.2%; Applied: 4500; Backlog: 3/1000; Time: 9m51s(total), 9m51s(copy); streamer: mysql-bin-changelog.000015:32280777; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 44s []
Copy: 9144862/10000155 91.4%; Applied: 4512; Backlog: 5/1000; Time: 9m53s(total), 9m53s(copy); streamer: mysql-bin-changelog.000015:37174190; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 57s
2026-08-19 08:21:46 INFO Copy: 9144862/10000155 91.4%; Applied: 4512; Backlog: 5/1000; Time: 9m53s(total), 9m53s(copy); streamer: mysql-bin-changelog.000015:37174190; Lag: 0.03s, HeartbeatLag: 0.04s, State: migrating; ETA: 57s []
- Sysbench still creates transactions and executes them normally.
- Database CPU utilization is ~70%.

- Transactions are blocked during the cut-over because the table metadata is locked during the rename.

Case 3: Use gh-ost to ALTER TABLE, with Chunk Size = 5000 and Nice-Ratio = 20
With nice-ratio = 0.1 in test case #2, database CPU utilization was quite high at ~70%. The migration took ~10 minutes for a table with 10,000,000 records. Let’s adjust nice-ratio to increase the delay between each copy of a 5,000-record chunk and reduce the load on the database.
Step 1: Run sysbench to simulate application queries
sysbench --db-driver=mysql --mysql-user=admin --mysql-password='YOUR_PASSWORD' --mysql-db=sysbench --tables=1 --table-size=10000000 --auto-inc=off --threads=20 --time=0 --rate=2 --rand-type=pareto --report-interval=1 --mysql-host="YOUR_AURORA_CLUSTER_ENDPOINT" oltp_read_write prepare 2>&1 | tee sysbench.log
sysbench --db-driver=mysql --mysql-user=admin --mysql-password='YOUR_PASSWORD' --mysql-db=sysbench --tables=1 --table-size=10000000 --auto-inc=off --threads=20 --time=0 --rate=2 --rand-type=pareto --report-interval=1 --mysql-host="YOUR_AURORA_CLUSTER_ENDPOINT" oltp_read_write run 2>&1 | tee sysbench.log
WARNING: Both event and time limits are disabled, running an endless test
sysbench 1.1.0-3ceba0b (using bundled LuaJIT 2.1.0-beta3)
Running the test with following options:
Number of threads: 20
Target transaction rate: 2/sec
Report intermediate results every 1 second(s)
Initializing random number generator from current time
Initializing worker threads...
Threads started!
[ 1s ] thds: 20 tps: 0.00 qps: 5.98 (r/w/o: 4.99/0.00/1.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 1s ] queue length: 0, concurrency: 1
[ 2s ] thds: 20 tps: 1.00 qps: 14.01 (r/w/o: 9.01/4.00/1.00) lat (ms,95%): 45.79 err/s: 0.00 reconn/s: 0.00
[ 2s ] queue length: 0, concurrency: 0
[ 3s ] thds: 20 tps: 4.00 qps: 80.01 (r/w/o: 56.00/16.00/8.00) lat (ms,95%): 37.56 err/s: 0.00 reconn/s: 0.00
[ 3s ] queue length: 0, concurrency: 0
[ 4s ] thds: 20 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 4s ] queue length: 0, concurrency: 0
[ 5s ] thds: 20 tps: 1.00 qps: 21.00 (r/w/o: 14.00/4.00/3.00) lat (ms,95%): 33.72 err/s: 0.00 reconn/s: 0.00
[ 5s ] queue length: 0, concurrency: 1
[ 6s ] thds: 20 tps: 3.00 qps: 59.00 (r/w/o: 42.00/12.00/5.00) lat (ms,95%): 40.37 err/s: 0.00 reconn/s: 0.00
[ 6s ] queue length: 0, concurrency: 0
...
Step 2: Run ALTER TABLE with gh-ost, chunk size = 5000 and nice-ratio = 20
./gh-ost -allow-on-master -assume-rbr -exact-rowcount \
-critical-load Threads_running=400 -critical-load-hibernate-seconds 60 \
-database sysbench -max-load Threads_running=100 -nice-ratio 20 \
-chunk-size 5000 -ask-pass -table sbtest1 -user admin \
-host YOUR_AURORA_CLUSTER_ENDPOINT \
-postpone-cut-over-flag-file /home/ec2-user/gh-ost-sentinel \
-throttle-additional-flag-file /home/ec2-user/gh-ost-throttle_ghost.sbtest1 \
-alter 'MODIFY COLUMN k BIGINT NOT NULL' \
-verbose -execute -skip-metadata-lock-check 2>&1 | tee gh-ost.log
...
Copy: 0/9868941 0.0%; Applied: 0; Backlog: 1/1000; Time: 0s(total), 0s(copy); streamer: mysql-bin-changelog.000047:114696801; Lag: 0.05s, HeartbeatLag: 0.05s, State: migrating; ETA: N/A
2026-08-19 09:32:48 INFO Copy: 0/9868941 0.0%; Applied: 0; Backlog: 1/1000; Time: 0s(total), 0s(copy); streamer: mysql-bin-changelog.000047:114696801; Lag: 0.05s, HeartbeatLag: 0.05s, State: migrating; ETA: N/A []
Copy: 0/9868941 0.0%; Applied: 0; Backlog: 6/1000; Time: 1s(total), 1s(copy); streamer: mysql-bin-changelog.000047:114703065; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: N/A
2026-08-19 09:32:49 INFO Copy: 0/9868941 0.0%; Applied: 0; Backlog: 6/1000; Time: 1s(total), 1s(copy); streamer: mysql-bin-changelog.000047:114703065; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: N/A []
Copy: 5000/9868942 0.1%; Applied: 4; Backlog: 26/1000; Time: 2s(total), 2s(copy); streamer: mysql-bin-changelog.000047:115689557; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: N/A
2026-08-19 09:32:50 INFO Copy: 5000/9868942 0.1%; Applied: 4; Backlog: 26/1000; Time: 2s(total), 2s(copy); streamer: mysql-bin-changelog.000047:115689557; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: N/A []
Copy: 5000/9868942 0.1%; Applied: 4; Backlog: 36/1000; Time: 3s(total), 3s(copy); streamer: mysql-bin-changelog.000047:115694066; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: N/A
2026-08-19 09:32:51 INFO Copy: 5000/9868942 0.1%; Applied: 4; Backlog: 36/1000; Time: 3s(total), 3s(copy); streamer: mysql-bin-changelog.000047:115694066; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: N/A []
Copy: 5000/9868944 0.1%; Applied: 24; Backlog: 1/1000; Time: 4s(total), 4s(copy); streamer: mysql-bin-changelog.000047:115705521; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: N/A
2026-08-19 09:32:52 INFO Copy: 5000/9868944 0.1%; Applied: 24; Backlog: 1/1000; Time: 4s(total), 4s(copy); streamer: mysql-bin-changelog.000047:115705521; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: N/A []
Copy: 10000/9868944 0.1%; Applied: 24; Backlog: 19/1000; Time: 5s(total), 5s(copy); streamer: mysql-bin-changelog.000047:116688009; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 32m51s
2026-08-19 09:32:53 INFO Copy: 10000/9868944 0.1%; Applied: 24; Backlog: 19/1000; Time: 5s(total), 5s(copy); streamer: mysql-bin-changelog.000047:116688009; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 32m51s []
Copy: 10000/9868944 0.1%; Applied: 24; Backlog: 33/1000; Time: 6s(total), 6s(copy); streamer: mysql-bin-changelog.000047:116694274; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: 1h38m38s
2026-08-19 09:32:54 INFO Copy: 10000/9868944 0.1%; Applied: 24; Backlog: 33/1000; Time: 6s(total), 6s(copy); streamer: mysql-bin-changelog.000047:116694274; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: 1h38m38s []
Copy: 15000/9868946 0.2%; Applied: 40; Backlog: 12/1000; Time: 7s(total), 7s(copy); streamer: mysql-bin-changelog.000047:117683108; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 32m50s
2026-08-19 09:32:55 INFO Copy: 15000/9868946 0.2%; Applied: 40; Backlog: 12/1000; Time: 7s(total), 7s(copy); streamer: mysql-bin-changelog.000047:117683108; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 32m50s []
Copy: 15000/9868946 0.2%; Applied: 40; Backlog: 34/1000; Time: 8s(total), 8s(copy); streamer: mysql-bin-changelog.000047:117692884; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: 1h27m37s
2026-08-19 09:32:56 INFO Copy: 15000/9868946 0.2%; Applied: 40; Backlog: 34/1000; Time: 8s(total), 8s(copy); streamer: mysql-bin-changelog.000047:117692884; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: 1h27m37s []
Copy: 20000/9868950 0.2%; Applied: 68; Backlog: 7/1000; Time: 9s(total), 9s(copy); streamer: mysql-bin-changelog.000047:118683818; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 32m49s
2026-08-19 09:32:57 INFO Copy: 20000/9868950 0.2%; Applied: 68; Backlog: 7/1000; Time: 9s(total), 9s(copy); streamer: mysql-bin-changelog.000047:118683818; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 32m49s []
2026-08-19 09:32:57 INFO Exact number of rows via COUNT: 10000000
Observations
- Database CPU Utilization remains stable at <30%.

- The total run time was 1 hour 46 minutes before the cut-over could be performed.
Copy: 9988484/10001517 99.9%; Applied: 51732; Backlog: 1/1000; Time: 1h46m42s(total), 1h46m42s(copy); streamer: mysql-bin-changelog.000062:113275187; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 8s
2026-08-19 11:19:30 INFO Copy: 9988484/10001517 99.9%; Applied: 51732; Backlog: 1/1000; Time: 1h46m42s(total), 1h46m42s(copy); streamer: mysql-bin-changelog.000062:113275187; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 8s []
Copy: 9993483/10001517 99.9%; Applied: 51732; Backlog: 15/1000; Time: 1h46m43s(total), 1h46m43s(copy); streamer: mysql-bin-changelog.000062:114255726; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: 1s
2026-08-19 11:19:31 INFO Copy: 9993483/10001517 99.9%; Applied: 51732; Backlog: 15/1000; Time: 1h46m43s(total), 1h46m43s(copy); streamer: mysql-bin-changelog.000062:114255726; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: 1s []
Copy: 9993483/10001517 99.9%; Applied: 51732; Backlog: 29/1000; Time: 1h46m44s(total), 1h46m44s(copy); streamer: mysql-bin-changelog.000062:114261997; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 5s
2026-08-19 11:19:32 INFO Copy: 9993483/10001517 99.9%; Applied: 51732; Backlog: 29/1000; Time: 1h46m44s(total), 1h46m44s(copy); streamer: mysql-bin-changelog.000062:114261997; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 5s []
Copy: 9993483/10001517 99.9%; Applied: 51732; Backlog: 47/1000; Time: 1h46m45s(total), 1h46m45s(copy); streamer: mysql-bin-changelog.000062:114270020; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 5s
2026-08-19 11:19:33 INFO Copy: 9993483/10001517 99.9%; Applied: 51732; Backlog: 47/1000; Time: 1h46m45s(total), 1h46m45s(copy); streamer: mysql-bin-changelog.000062:114270020; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: 5s []
Copy: 9998483/10001517 100.0%; Applied: 51748; Backlog: 9/1000; Time: 1h46m46s(total), 1h46m46s(copy); streamer: mysql-bin-changelog.000062:115257757; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: due
2026-08-19 11:19:34 INFO Copy: 9998483/10001517 100.0%; Applied: 51748; Backlog: 9/1000; Time: 1h46m46s(total), 1h46m46s(copy); streamer: mysql-bin-changelog.000062:115257757; Lag: 0.04s, HeartbeatLag: 0.05s, State: migrating; ETA: due []
Copy: 9998483/10001517 100.0%; Applied: 51748; Backlog: 19/1000; Time: 1h46m47s(total), 1h46m47s(copy); streamer: mysql-bin-changelog.000062:115262266; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: 1s
2026-08-19 11:19:35 INFO Copy: 9998483/10001517 100.0%; Applied: 51748; Backlog: 19/1000; Time: 1h46m47s(total), 1h46m47s(copy); streamer: mysql-bin-changelog.000062:115262266; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: 1s []
2026-08-19 11:19:36 INFO Row copy complete
Copy: 9998483/9998483 100.0%; Applied: 51760; Backlog: 0/1000; Time: 1h46m48s(total), 1h46m47s(copy); streamer: mysql-bin-changelog.000062:115275273; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: due
2026-08-19 11:19:36 INFO Copy: 9998483/9998483 100.0%; Applied: 51760; Backlog: 0/1000; Time: 1h46m48s(total), 1h46m47s(copy); streamer: mysql-bin-changelog.000062:115275273; Lag: 0.04s, HeartbeatLag: 0.04s, State: migrating; ETA: due []
Copy: 9998483/9998483 100.0%; Applied: 51760; Backlog: 1/1000; Time: 1h46m48s(total), 1h46m47s(copy); streamer: mysql-bin-changelog.000062:115276044; Lag: 0.04s, HeartbeatLag: 0.05s, State: postponing cut-over; ETA: due
- Blocked transactions appeared, caused by the application side, not by gh-ost.

Read more:
Conclusion: Is gh-ost Safe for MySQL Online Schema Change on Aurora?
- gh-ost works well with Aurora MySQL for MySQL online schema change.
- gh-ost can only be used to migrate the schema of tables that have a PRIMARY KEY or UNIQUE KEY.
- Binlog must be enabled for the cluster, which requires rebooting the DB instance.
- The
-skip-metadata-lock-checkoption must be added for gh-ost to be able to run. - Reduce chunk-size, combined with increasing nice-ratio, to reduce database CPU utilization when running gh-ost, at the cost of a longer run time.
- Run gh-ost directly on an EC2 instance in the same zone as the database writer instance, combined with the
screencommand, since gh-ost can take a long time to run. - Remove the
-executeoption to do a dry run before the real execution.
If your team runs critical workloads on Aurora and needs support planning database changes, migrations or upgrades, explore SupremeTech’s Cloud Infrastructure & DevOps services or contact our team.
Frequently Asked Questions About MySQL Online Schema Change
What is a MySQL online schema change?
A MySQL online schema change is a way to alter a table’s structure while the application keeps reading and writing to it. Tools such as gh-ost create a new table with the target schema, copy data in small chunks, keep it in sync with ongoing changes, and swap the tables in a short final step called cut-over.
Why does ALTER TABLE block transactions when changing a column type?
According to the MySQL 8.0 online DDL documentation, changing a column’s data type rebuilds the table and does not permit concurrent DML. On a large table, application writes have to wait until the rebuild finishes, which is why sysbench transactions dropped to zero in our first test case.
Does gh-ost work with Amazon Aurora MySQL?
Yes. In our test, gh-ost 1.1.10 completed the change on Aurora MySQL 3.10.3 while application queries continued. It ran against the writer with -allow-on-master and -assume-rbr, and the gh-ost documentation for Amazon RDS explains that Aurora readers share storage with the writer, so gh-ost treats them as the primary.
What does nice-ratio do in gh-ost?
nice-ratio adds sleep time after each copied chunk, relative to how long the copy took. The gh-ost interactive commands documentation explains that a ratio of 1 makes gh-ost sleep as long as it copied, roughly doubling run time. Higher values reduce database load but make the migration take longer.
What are the requirements for using gh-ost?
The table must have a PRIMARY KEY or UNIQUE KEY that gh-ost can use to iterate rows, and row-based binary logging must be available. The gh-ost requirements and limitations also note that foreign keys and triggers are not supported. On Aurora, changing the binlog setting in the cluster parameter group requires a reboot of the writer instance.
How can I test gh-ost before running it on production?
Run the same command without the -execute option. gh-ost then performs a dry run that checks the configuration and the ALTER statement without changing data. Use -postpone-cut-over-flag-file in the real run so you can choose when the final table swap happens.












