DEV3 Technologies home

DEV3 Developer Forum

How do I run migrations safely in production without downtime?

Laravel helpSolved1 view
dev3 ·

I need to add a NOT NULL column and an index to a table with 20 million rows on MySQL during business hours. php artisan migrate asks for confirmation in production. How do I do this safely?

#laravel#database#migrations

0

1 reply

dev3 · Accepted answer

Production migrations need planning, not just a command.

Running them

php artisan migrate --force      # --force skips the prompt in production
php artisan migrate --pretend    # print the SQL without running it

Take a backup first and run migrations in your deploy pipeline, before switching traffic to new code when the change is backwards compatible.

The expand and contract pattern (zero-downtime)

  1. Expand: add the new column as nullable (or with a default), deploy code that writes both the old and new columns.
  2. Backfill existing rows in batches with a command or queued job, not inside the migration:
User::whereNull('region')->chunkById(1000, fn ($rows) => $rows->each->update(['region' => 'IN']));
  1. Contract: after all rows are filled and code reads the new column only, add the NOT NULL constraint or drop the old column in a later release.

Locking

  • MySQL 8 supports many ALTER TABLE operations online (ALGORITHM=INPLACE, LOCK=NONE), but adding some column types or changing a type can rebuild the table. In Laravel you can run raw statements for control: DB::statement('ALTER TABLE orders ADD INDEX idx_user (user_id), ALGORITHM=INPLACE, LOCK=NONE');.
  • For very large tables use gh-ost or pt-online-schema-change.
  • Index creation on huge tables on Postgres: CREATE INDEX CONCURRENTLY (set public $withinTransaction = false; on the migration).

Good habits

  • Keep migrations small and one concern each. Never edit a migration that already ran in production; add a new one.
  • Always write down() for reversible changes, or accept that production rollbacks are done by a new forward migration.
  • Do not use Eloquent models in migrations, since later model changes may break old migrations. Use DB::table().
  • Test the migration on a copy of production data to measure duration.
  • Schema::hasColumn guards help when a hotfix was applied by hand.
0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.