Deploy Changes to Multiple Databases Using Laravel Migrations

前言

前陣子遇到同一套系統需發佈在不同國家機房的需求,
其中資料庫 table schema 的更新似乎沒有比較聰明的作法,
因此只能將差異記錄成腳本,由 DBA 手動於各資料庫執行。

最近接觸的 Laravel Migrations 恰好能解決上述情境。

Migrations 簡介

Migrations 就像資料庫的版本控管,它讓開發團隊得以輕鬆地修改並分享 table schema。
Laravel 的 Schema facade 支援表格建立與操作,通用於各種資料庫,目前支援 MySQL/PostgreSQL/SQLite/SQL Server 等。(至於萬惡的 Oracle,有非官方套件可裝)
因此,開發者可使用程式碼逐行表達資料表的建立過程,包含各欄位的屬性與資料表間的關聯。

實作範例

準備工作

先以 Laravel Homestead 架設好三組機器作為多資料庫的環境。
資料庫的帳號、密碼與權限必須先設好,此為 DBA 職掌而非 Migrations 所能代勞的。

MySQL 指令如下:

CREATE USER 'homestead'@'%' IDENTIFIED BY 'secret';
CREATE DATABASE homestead;
GRANT ALL ON homestead.* TO 'homestead'@'%';

以程式碼描述 table schema

users 資料表為例,它的 migrations 節錄如下:

<?php
class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('username');
            $table->string('email')->unique();
            $table->string('password', 60);
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('users');
    }
}
?>

其中 up 函式描述了資料表的組成,包含:

  • id 欄位自動遞增
  • username 欄位類型為字串
  • email 欄位類型為字串,且不得重複
  • password 欄位類型為字串,且長度限制為 60
  • rememberToken 函式產生儲存 session 的特殊欄位
  • timestamps 函式自動產生 created_atupdated_at 欄位

down 函式則載明回復操作的步驟。

組態檔

各資料庫的連線組態,依慣例應寫入環境變數,並於 config/database.php 載入:

<?php
'connections' => [
        'mysql' => [
            'driver'    => 'mysql',
            'host'      => env('DB_HOST', 'localhost'),
            'database'  => env('DB_DATABASE', 'forge'),
            'username'  => env('DB_USERNAME', 'forge'),
            'password'  => env('DB_PASSWORD', ''),
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
            'strict'    => false,
        ],
        'mysql-tw' => [
            'driver'    => 'mysql',
            'host'      => env('DB_HOST_TW', 'localhost'),
            'database'  => env('DB_DATABASE_TW', 'forge'),
            'username'  => env('DB_USERNAME_TW', 'forge'),
            'password'  => env('DB_PASSWORD_TW', ''),
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
            'strict'    => false,
        ],
        'mysql-us' => [
            'driver'    => 'mysql',
            'host'      => env('DB_HOST_US', 'localhost'),
            'database'  => env('DB_DATABASE_US', 'forge'),
            'username'  => env('DB_USERNAME_US', 'forge'),
            'password'  => env('DB_PASSWORD_US', ''),
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
            'strict'    => false,
        ],
    ],
?>

實地操作

對於新建的資料表,可用 migrate 選項發佈 table schema

for DB_NAME in mysql mysql-tw mysql-us;
do
    ./artisan migrate --database ${DB_NAME}
done

若資料表已存在,可用 migrate:refresh 選項更新 table schema

for DB_NAME in mysql mysql-tw mysql-us;
do
    ./artisan migrate:refresh --database ${DB_NAME}
done

結語

Laravel Migrations 實作了 Active Record 的諸多要件。
對開發者,資料表異動可用程式碼描述並輕鬆分享;對維運者或 DBA,資料表異動受版本控管,可快速比較差異並於各版本間切換。

注意事項

Migrations 對資料表的操作,有些行為屬移除性質,會造成對應資料丟失,使用者應先於測試或開發環境驗證後,再施用於線上環境。

鍵盤錄影

讀者手邊若無操作環境,可參考鍵盤錄影。
全螢幕播放可得到最佳效果。

Leave a Reply

Your email address will not be published.